Code Maze https://code-maze.com A practical programmer's resource. Sat, 04 Jan 2020 10:35:37 +0000 en-US 1.2 https://code-maze.com https://code-maze.com 1 3 6 11 14 5 15 18 19 22 23 24 168 495 505 12 4 3 504 66 171 506 86 605 169 1 618 170 13 10 22 205 426 524 148 564 565 67 68 363 364 281 180 384 226 123 132 494 379 570 477 527 23 166 61 417 388 394 395 29 28 172 340 593 9 535 314 313 275 291 529 221 179 298 80 79 227 443 586 85 176 160 159 623 158 335 89 73 74 541 585 599 161 128 156 353 209 46 267 126 260 424 520 480 486 7 56 483 385 326 55 318 259 235 110 391 516 575 475 109 114 111 115 416 324 608 500 344 508 98 496 497 503 254 253 146 137 136 549 239 339 328 325 534 431 537 381 548 519 113 112 116 131 553 232 380 33 582 150 120 101 542 134 446 551 320 525 609 598 213 217 556 545 502 507 71 360 225 347 216 382 122 532 59 536 469 32 373 482 433 94 26 278 83 378 149 155 70 309 573 264 82 317 139 138 104 105 601 602 476 84 153 471 145 473 568 343 550 406 554 566 619 203 204 613 430 252 372 547 52 53 349 351 191 192 133 208 530 200 201 202 69 234 362 567 215 439 338 36 485 177 490 491 329 308 301 165 408 277 399 538 552 481 428 262 77 279 528 183 383 377 367 368 366 121 54 206 521 526 523 533 30 163 390 272 371 603 90 91 142 141 14 190 400 517 423 425 611 610 210 414 418 409 410 412 255 256 257 37 38 43 233 224 581 247 352 559 237 246 577 359 447 358 285 193 88 276 280 438 584 187 186 230 231 76 75 248 65 140 515 236 544 95 440 376 195 350 42 44 222 261 621 562 389 403 413 393 392 402 397 401 396 288 93 557 118 117 119 144 546 445 62 315 583 312 299 361 531 539 92 27 24 189 106 501 354 607 41 342 341 274 420 229 228 561 563 437 220 292 316 595 596 597 293 295 238 442 355 588 398 300 290 327 198 147 154 307 441 375 57 555 271 270 269 436 282 287 499 35 135 321 197 330 331 332 196 284 286 283 374 58 522 369 569 96 97 143 78 199 305 333 310 311 240 306 422 296 484 297 124 125 211 294 435 15 219 212 405 223 560 245 571 51 50 8 47 432 587 427 540 289 415 188 63 357 157 574 151 319 365 589 590 518 72 558 578 323 620 164 152 421 185 167 493 622 434 591 592 184 543 370 251 34 498 99 100 470 336 337 474 512 303 302 242 243 348 345 346 244 600 258 322 604 162 514 174 576 127 419 250 129 612 249 411 87 207 273 31 266 268 579 178 173 175 572 407 429 194 265 218 472 214 444 356 39 40 60 181 182 25 130 49 64 45 606 263 81 304 334 241 580 18nav_menu https://wordpress.org/?v=5.3.2 continuous development and delivery https://code-maze.com/ci-4/ Mon, 01 Feb 2016 21:26:17 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/ci-4.png 7 0 0 0 continuous integration cycle https://code-maze.com/ci-cycle-2/ Mon, 01 Feb 2016 21:26:26 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/ci-cycle-2.png 8 0 0 0 create new project https://code-maze.com/teamcity-basic-concepts/create-new-project/ Tue, 02 Feb 2016 10:36:20 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/create-new-project.png 25 13 0 0 build configuration panel https://code-maze.com/teamcity-basic-concepts/build-configuration-panel/ Tue, 02 Feb 2016 10:48:02 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/build-configuration-panel.png 26 13 0 0 build configuration panel https://code-maze.com/teamcity-basic-concepts/build-configuration-panel-2/ Tue, 02 Feb 2016 10:50:21 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/build-configuration-panel-1.png 28 13 0 0 build steps https://code-maze.com/teamcity-basic-concepts/build-steps/ Tue, 02 Feb 2016 11:22:46 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/build-steps.png 30 13 0 0 nunit build step https://code-maze.com/teamcity-basic-concepts/nunit-build-step/ Tue, 02 Feb 2016 16:50:40 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/nunit-build-step.png 36 13 0 0 lightbulb trigger https://code-maze.com/teamcity-basic-concepts/lightbulb-trigger/ Tue, 02 Feb 2016 17:10:09 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/lightbulb-trigger.png 39 13 0 0 notification settings https://code-maze.com/teamcity-basic-concepts/notification-settings/ Tue, 02 Feb 2016 17:20:19 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/notification-settings.png 40 13 0 0 create new project https://code-maze.com/teamcity-basic-concepts/create-new-project-2/ Wed, 03 Feb 2016 12:38:57 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/create-new-project-1.png 46 13 0 0 header10 https://code-maze.com/header10/ Wed, 03 Feb 2016 19:23:19 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/header10.jpg 53 0 0 0 cropped-header10.jpg https://code-maze.com/cropped-header10-jpg/ Wed, 03 Feb 2016 19:23:28 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/cropped-header10.jpg 54 0 0 0 favicon https://code-maze.com/favicon/ Wed, 03 Feb 2016 20:37:56 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/favicon.ico 57 0 0 0 TeamCity https://code-maze.com/advanced_ads/1275/ Wed, 31 Jan 2018 07:21:41 +0000 https://code-maze.com/?post_type=advanced_ads&p=1275 ]]> 1275 0 0 0 TeamCity horizontal https://code-maze.com/advanced_ads/teamcity-horizontal/ Thu, 08 Feb 2018 21:42:36 +0000 https://code-maze.com/?post_type=advanced_ads&p=1434 ]]> 1434 0 0 0 Jetbrains Rider Side Ad https://code-maze.com/?post_type=advanced_ads&p=4886 Thu, 11 Oct 2018 13:01:58 +0000 https://code-maze.com/?post_type=advanced_ads&p=4886 ]]> 4886 0 0 0 Jetbrains Rider Top Ad https://code-maze.com/?post_type=advanced_ads&p=4887 Thu, 11 Oct 2018 14:06:44 +0000 https://code-maze.com/?post_type=advanced_ads&p=4887 ]]> 4887 0 0 0 Ads.txt https://code-maze.com/?post_type=adstxt&p=48481 Wed, 03 Jul 2019 10:02:24 +0000 https://code-maze.com/?post_type=adstxt&p=48481 48481 0 0 0 Ezoic Sidebar Middle https://code-maze.com/advanced_ads/ezoic-sidebar-middle/ Tue, 12 Nov 2019 21:35:21 +0000 https://code-maze.com/?post_type=advanced_ads&p=49515
]]>
49515 0 0 0
Ezoic Sidebar Bottom https://code-maze.com/advanced_ads/ezoic-sidebar-bottom/ Tue, 12 Nov 2019 21:37:29 +0000 https://code-maze.com/?post_type=advanced_ads&p=49516
]]>
49516 0 0 0
cropped-cropped-header10.jpg https://code-maze.com/cropped-cropped-header10-jpg/ Wed, 03 Feb 2016 20:38:58 +0000 http://www.code-maze.com/blog/wp-content/uploads/2016/02/cropped-cropped-header10.jpg 58 0 0 0 TravisCI logo gray https://code-maze.com/top-8-continuous-integration-tools/travisci-logo-gray/ Sat, 06 Feb 2016 14:45:33 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/TravisCI-logo-gray.png 95 91 0 0 jenkins logo text https://code-maze.com/top-8-continuous-integration-tools/jenkins-logo-text/ Sat, 06 Feb 2016 14:47:32 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/jenkins-logo-text.png 96 91 0 0 go cd logo https://code-maze.com/top-8-continuous-integration-tools/go-cd-logo/ Sat, 06 Feb 2016 14:51:15 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/go-cd-logo.png 97 91 0 0 TeamCity logo https://code-maze.com/top-8-continuous-integration-tools/teamcity-logo/ Sat, 06 Feb 2016 14:55:08 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/TeamCity-logo.png 98 91 0 0 bamboo logo https://code-maze.com/top-8-continuous-integration-tools/bamboo_logo/ Sat, 06 Feb 2016 14:56:36 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/bamboo_logo.png 99 91 0 0 favicon https://code-maze.com/favicon-2/ Sat, 06 Feb 2016 19:35:46 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/favicon-1.ico 110 0 0 0 profile image vladimir https://code-maze.com/about/profile_image/ Sat, 06 Feb 2016 19:47:26 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/profile_image.png 112 2 0 0 gitlab_logo https://code-maze.com/top-8-continuous-integration-tools/wm_no_bg/ Mon, 08 Feb 2016 21:00:31 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/wm_no_bg.png 129 91 0 0 circleci logo https://code-maze.com/top-8-continuous-integration-tools/circleci/ Mon, 15 Feb 2016 07:24:19 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/circleci.png 170 91 0 0 codeship logo https://code-maze.com/top-8-continuous-integration-tools/codeship-logo/ Tue, 16 Feb 2016 21:45:58 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/codeship-logo.png 173 91 0 0 What is Continuous Integration https://code-maze.com/what-is-continuous-integration/header13/ Sat, 20 Feb 2016 08:48:45 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/header13.png 201 161 0 0 8 Top Continuous Integration Tools https://code-maze.com/top-8-continuous-integration-tools/8-top-continuous-integration-tools-3/ Sat, 20 Feb 2016 11:49:03 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/8-Top-Continuous-Integration-Tools-1.png 214 91 0 0 New web application https://code-maze.com/new-project/ Mon, 22 Feb 2016 20:47:03 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/New-Project.png 227 0 0 0 New Project MVC https://code-maze.com/new-project-mvc/ Mon, 22 Feb 2016 20:48:33 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/New-Project-MVC.png 228 0 0 0 ASP.NET Application https://code-maze.com/asp-net-application/ Mon, 22 Feb 2016 20:54:22 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/ASP.NET-Application.png 229 0 0 0 git create repo https://code-maze.com/git-clone/ Mon, 22 Feb 2016 21:00:45 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/git-clone.png 230 0 0 0 Nuget feed position https://code-maze.com/nuget-feed-position/ Fri, 26 Feb 2016 20:46:26 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/Nuget-feed-position.png 240 0 0 0 enable nuget packages https://code-maze.com/enable-nuget-packages/ Fri, 26 Feb 2016 20:51:47 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/enable-nuget-packages.png 241 0 0 0 guest login enable https://code-maze.com/guest-login-enable/ Fri, 26 Feb 2016 20:55:39 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/guest-login-enable.png 242 0 0 0 nuget octopack https://code-maze.com/nuget-octopack/ Fri, 26 Feb 2016 21:35:04 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/nuget-octopack.png 244 0 0 0 content copy always https://code-maze.com/content-copy-always/ Fri, 26 Feb 2016 22:02:35 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/content-copy-always.png 248 0 0 0 octopus packaging https://code-maze.com/octopus-packaging/ Fri, 26 Feb 2016 22:19:55 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/octopus-packaging.png 249 0 0 0 octopack options https://code-maze.com/octopack-options/ Fri, 26 Feb 2016 22:22:45 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/octopack-options.png 250 0 0 0 New Deployment Target - Octopus Deploy https://code-maze.com/new-deployment-target-octopus-deploy/ Sat, 27 Feb 2016 19:21:21 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/New-Deployment-Target-Octopus-Deploy.png 262 0 0 0 Environments - Octopus Deploy https://code-maze.com/environments-octopus-deploy/ Sat, 27 Feb 2016 19:23:40 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/Environments-Octopus-Deploy.png 263 0 0 0 Octopus Deploy create release - TeamCity https://code-maze.com/octopus-deploy-create-release-teamcity/ Sat, 27 Feb 2016 19:43:13 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/Octopus-Deploy-create-release-TeamCity.png 264 0 0 0 Octopus Deploy create release - TeamCity https://code-maze.com/octopus-deploy-create-release-teamcity-2/ Sat, 27 Feb 2016 20:09:04 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/Octopus-Deploy-create-release-TeamCity-1.png 266 0 0 0 Enabled features - Octopus Deploy https://code-maze.com/enabled-features-octopus-deploy/ Sat, 27 Feb 2016 20:09:32 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/Enabled-features-Octopus-Deploy.png 267 0 0 0 content copy always https://code-maze.com/content-copy-always-2/ Sun, 28 Feb 2016 09:28:24 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/content-copy-always-1.png 274 0 0 0 how to level up your net deployment process https://code-maze.com/how-to-level-up-your-net-deployment-process/ Sun, 28 Feb 2016 23:04:40 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/how-to-level-up-your-net-deployment-process.png 281 0 0 0 how Octopus Deploy Fits In https://code-maze.com/howoctopusdeployfitsin/ Mon, 29 Feb 2016 18:32:11 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/howOctopusDeployFitsIn.png 283 0 0 0 What is Continuous Integration https://code-maze.com/what-is-continuous-integration/header14/ Tue, 01 Mar 2016 18:02:04 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/header14.png 286 161 0 0 Continuous Integration with TeamCity https://code-maze.com/teamcity-basic-concepts/header4/ Tue, 01 Mar 2016 18:19:55 +0000 http://www.code-maze.com/wp-content/uploads/2016/02/header4.png 288 13 0 0 RestSharp releases https://code-maze.com/different-ways-consume-restful-api-csharp/restsharp-releases/ Sun, 04 Jun 2017 11:09:22 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/RestSharp-releases.png 319 296 0 0 GitHub-Mark-32px https://code-maze.com/github-mark-32px/ Tue, 06 Jun 2017 16:49:00 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/GitHub-Mark-32px.png 339 0 0 0 GitHub-Mark-64px https://code-maze.com/github-mark-64px/ Tue, 06 Jun 2017 16:49:01 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/GitHub-Mark-64px.png 340 0 0 0 GitHub-Mark-120px-plus https://code-maze.com/github-mark-120px-plus/ Tue, 06 Jun 2017 16:49:04 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/GitHub-Mark-120px-plus.png 341 0 0 0 401wizard https://code-maze.com/http-series-part-1/tumblr_ntqxnerhqr1udik9co2_1280/ Sun, 18 Jun 2017 18:55:51 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/tumblr_ntqxneRHQr1udik9co2_1280.jpg 456 350 0 0 404batman https://code-maze.com/http-series-part-1/tumblr_ntqxdocovo1udik9co2_1280/ Sun, 18 Jun 2017 18:57:34 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/tumblr_ntqxdoCovo1udik9co2_1280.jpg 458 350 0 0 resource https://code-maze.com/http-series-part-1/resource/ Sun, 18 Jun 2017 19:24:39 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/resource.jpg 460 350 0 0 Network stack https://code-maze.com/http-series-part-1/network-stack/ Mon, 19 Jun 2017 05:25:21 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/Network-stack.png 480 350 0 0 CacheFlow https://code-maze.com/http-series-part-2/cacheflow/ Sun, 25 Jun 2017 12:10:30 +0000 https://code-maze.com/wp-content/uploads/2017/06/CacheFlow.png 507 490 0 0 servers https://code-maze.com/http-series-part-2/servers/ Sun, 25 Jun 2017 15:05:37 +0000 https://code-maze.com/wp-content/uploads/2017/06/servers.jpg 513 490 0 0 servers2 https://code-maze.com/http-series-part-2/managed-server-hosting-your-server-in-the-cloud-523968604/ Sun, 25 Jun 2017 15:09:38 +0000 https://code-maze.com/wp-content/uploads/2017/06/managed-server-hosting-your-server-in-the-cloud-523968604.jpg 514 490 0 0 proxy server https://code-maze.com/http-series-part-2/proxy-server/ Sun, 25 Jun 2017 16:03:55 +0000 https://code-maze.com/wp-content/uploads/2017/06/proxy-server.png 516 490 0 0 reverse proxy server https://code-maze.com/http-series-part-2/proxy-server-2/ Sun, 25 Jun 2017 16:14:35 +0000 https://code-maze.com/wp-content/uploads/2017/06/proxy-server-1.png 520 490 0 0 Web-crawler https://code-maze.com/http-series-part-2/web-crawler/ Sun, 25 Jun 2017 16:22:05 +0000 https://code-maze.com/wp-content/uploads/2017/06/Web-crawler.png 521 490 0 0 multipass https://code-maze.com/http-series-part-3/multipass/ Sun, 09 Jul 2017 11:23:12 +0000 https://code-maze.com/wp-content/uploads/2017/07/multipass.jpg 582 542 0 0 https shaming https://code-maze.com/http-series-part-5/https-shaming-1/ Sat, 29 Jul 2017 17:46:29 +0000 https://code-maze.com/wp-content/uploads/2017/07/https-shaming-1.png 670 661 0 0 https shaming future https://code-maze.com/http-series-part-5/https-shaming-2/ Sat, 29 Jul 2017 17:47:47 +0000 https://code-maze.com/wp-content/uploads/2017/07/https-shaming-2.png 671 661 0 0 HTTP security considerations https://code-maze.com/http-series-part-5/title5/ Sat, 29 Jul 2017 21:09:41 +0000 https://code-maze.com/wp-content/uploads/2017/07/Title5.png 673 661 0 0 http vs https results https://code-maze.com/http-series-part-5/httpvshttpsresults/ Sat, 29 Jul 2017 21:20:43 +0000 https://code-maze.com/wp-content/uploads/2017/07/httpvshttpsresults.png 674 661 0 0 http vs https results https://code-maze.com/http-series-part-5/httpvshttpsresults-2/ Sat, 29 Jul 2017 21:22:00 +0000 https://code-maze.com/wp-content/uploads/2017/07/httpvshttpsresults-1.png 675 661 0 0 ssl test https://code-maze.com/http-series-part-5/www-code-maze-com-powered-by-qualys-ssl-labs/ Sun, 30 Jul 2017 12:13:52 +0000 https://code-maze.com/wp-content/uploads/2017/07/www.code-maze.com-Powered-by-Qualys-SSL-Labs.png 679 661 0 0 Code Maze Certificate https://code-maze.com/http-series-part-5/code-maze-certificate-modified/ Sun, 30 Jul 2017 12:59:33 +0000 https://code-maze.com/wp-content/uploads/2017/07/Code-Maze-Certificate-modified.png 681 661 0 0 Twitter EV certificate https://code-maze.com/http-series-part-5/twitter-ev-certificate/ Sun, 30 Jul 2017 13:15:23 +0000 https://code-maze.com/wp-content/uploads/2017/07/Twitter-EV-certificate.png 682 661 0 0 certificate chain https://code-maze.com/http-series-part-5/certificate-chain/ Sun, 30 Jul 2017 20:29:20 +0000 https://code-maze.com/wp-content/uploads/2017/07/certificate-chain.png 685 661 0 0 certificate chain https://code-maze.com/http-series-part-5/certificate-chain-2/ Sun, 30 Jul 2017 20:30:24 +0000 https://code-maze.com/wp-content/uploads/2017/07/certificate-chain-1.png 686 661 0 0 root certificate https://code-maze.com/http-series-part-5/root-certificate/ Sun, 30 Jul 2017 20:46:51 +0000 https://code-maze.com/wp-content/uploads/2017/07/root-certificate.png 687 661 0 0 MITM https://code-maze.com/http-series-part-5/mitm/ Mon, 31 Jul 2017 16:27:16 +0000 https://code-maze.com/wp-content/uploads/2017/07/MITM.png 693 661 0 0 TLS handshake https://code-maze.com/http-series-part-5/tls-handshake/ Mon, 31 Jul 2017 16:57:11 +0000 https://code-maze.com/wp-content/uploads/2017/07/TLS-handshake.png 695 661 0 0 logo1 https://code-maze.com/logo1/ Sun, 06 Aug 2017 17:07:33 +0000 https://code-maze.com/wp-content/uploads/2017/08/logo1.png 710 0 0 0 codefresh logo https://code-maze.com/top-8-continuous-integration-tools/stickey-logo/ Sun, 20 Aug 2017 10:08:18 +0000 https://code-maze.com/wp-content/uploads/2016/02/stickey-logo.png 714 91 0 0 Top8Featured https://code-maze.com/top-8-continuous-integration-tools/top8featured/ Thu, 23 Nov 2017 20:25:55 +0000 https://code-maze.com/wp-content/uploads/2016/02/Top8Featured.png 794 91 0 0 CIfeatured https://code-maze.com/http-series-part-1/cifeatured/ Thu, 23 Nov 2017 20:51:42 +0000 https://code-maze.com/wp-content/uploads/2017/06/CIfeatured.png 806 350 0 0 WhatisciFeatured https://code-maze.com/what-is-continuous-integration/whatiscifeatured/ Thu, 23 Nov 2017 20:54:51 +0000 https://code-maze.com/wp-content/uploads/2016/02/WhatisciFeatured.png 808 161 0 0 CodeMaze https://code-maze.com/logo/ Fri, 24 Nov 2017 18:09:49 +0000 https://code-maze.com/wp-content/uploads/2017/11/Logo-1.png 854 0 0 0 01-mysql_models_menu https://code-maze.com/net-core-web-development-part1/01-mysql_models_menu/ Wed, 03 Jan 2018 19:34:05 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/01-mysql_models_menu.png 926 943 0 0 02-mysql_models_view https://code-maze.com/net-core-web-development-part1/02-mysql_models_view/ Wed, 03 Jan 2018 19:45:04 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/02-mysql_models_view.png 928 943 0 0 new-sql-tab https://code-maze.com/net-core-web-development-part1/07-2/ Wed, 03 Jan 2018 19:50:02 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/07.1-new-sql-tab.png 935 943 0 0 08-generate-sql-script https://code-maze.com/net-core-web-development-part1/08-generate-sql-script/ Wed, 03 Jan 2018 19:50:17 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/08-generate-sql-script.png 936 943 0 0 09-database-view https://code-maze.com/net-core-web-development-part1/09-database-view/ Wed, 03 Jan 2018 19:50:25 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/09-database-view.png 937 943 0 0 03-mysql_schema_view https://code-maze.com/net-core-web-development-part1/03-mysql_schema_view/ Thu, 04 Jan 2018 12:52:05 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/03-mysql_schema_view..png 971 943 0 0 04_table-creation https://code-maze.com/net-core-web-development-part1/04_table-creation/ Thu, 04 Jan 2018 12:52:24 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/04_table-creation..png 972 943 0 0 05-first_table_created https://code-maze.com/net-core-web-development-part1/05-first_table_created/ Thu, 04 Jan 2018 12:52:45 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/05-first_table_created..png 973 943 0 0 06-second_table_created https://code-maze.com/net-core-web-development-part1/06-second_table_created/ Thu, 04 Jan 2018 12:53:05 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/06-second_table_created..png 974 943 0 0 07-foreign-key-settings https://code-maze.com/net-core-web-development-part1/07-foreign-key-settings/ Thu, 04 Jan 2018 12:53:18 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/07-foreign-key-settings..png 975 943 0 0 10-complete-database-view https://code-maze.com/net-core-web-development-part1/10-complete-database-view/ Thu, 04 Jan 2018 12:53:49 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/10-complete-database-view..png 976 943 0 0 11-owner-table-selected https://code-maze.com/net-core-web-development-part1/11-owner-table-selected/ Thu, 04 Jan 2018 12:54:01 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/11-owner-table-selected..png 977 943 0 0 favicon https://code-maze.com/favicon-3/ Sun, 07 Jan 2018 12:33:54 +0000 https://code-maze.com/wp-content/uploads/2018/01/favicon.ico 1021 0 0 0 header-web-dev https://code-maze.com/header-web-dev/ Mon, 08 Jan 2018 18:59:11 +0000 https://code-maze.com/wp-content/uploads/2018/01/header-web-dev.jpg 1045 0 0 0 bla https://code-maze.com/bla/ Mon, 08 Jan 2018 19:02:05 +0000 https://code-maze.com/wp-content/uploads/2018/01/bla.png 1046 0 0 0 marinko https://code-maze.com/about/marinko-2/ Mon, 08 Jan 2018 19:34:33 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/marinko.jpg 1050 2 0 0 12-creating-netcore-project https://code-maze.com/net-core-web-development-part2/12-creating-netcore-project/ Tue, 09 Jan 2018 08:59:13 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/12-creating-netcore-project.png 1057 1053 0 0 13-choosing-webapi-project https://code-maze.com/net-core-web-development-part2/13-choosing-webapi-project-2/ Tue, 09 Jan 2018 09:08:54 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/13-choosing-webapi-project-1.png 1060 1053 0 0 14-iis-integration-options https://code-maze.com/net-core-web-development-part2/14-iis-integration-options/ Tue, 09 Jan 2018 12:15:48 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/14-iis-integration-options.png 1078 1053 0 0 15-create-new-project-contracts https://code-maze.com/15-create-new-project-contracts/ Tue, 16 Jan 2018 08:32:44 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/15-create-new-project-contracts.png 1165 0 0 0 16-adding-references-of-new-projects https://code-maze.com/net-core-web-development-part3/16-adding-references-of-new-projects/ Tue, 16 Jan 2018 08:33:00 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/16-adding-references-of-new-projects.png 1166 1164 0 0 17-installing-Nlog https://code-maze.com/net-core-web-development-part3/17-installing-nlog/ Tue, 16 Jan 2018 08:33:10 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/17-installing-Nlog.png 1167 1164 0 0 18-logged-file https://code-maze.com/net-core-web-development-part3/18-logged-file/ Tue, 16 Jan 2018 08:33:24 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/18-logged-file.png 1168 1164 0 0 top-rest-best-practices-title https://code-maze.com/top-rest-api-best-practices/pexels-photo-436784/ Mon, 22 Jan 2018 17:44:16 +0000 https://code-maze.com/wp-content/uploads/2018/01/pexels-photo-436784.jpeg 1199 1127 0 0 web-development-header https://code-maze.com/net-core-web-development-part1/web-development-header/ Tue, 23 Jan 2018 17:51:32 +0000 https://code-maze.com/wp-content/uploads/2018/01/web-development-header.png 1209 943 0 0 Test https://code-maze.com/net-core-web-development-part1/test/ Tue, 23 Jan 2018 18:16:23 +0000 https://code-maze.com/wp-content/uploads/2018/01/Test.png 1212 943 0 0 web-dev-header-title https://code-maze.com/net-core-web-development-part1/notebook-1280538_1920/ Tue, 23 Jan 2018 18:30:21 +0000 https://code-maze.com/wp-content/uploads/2018/01/notebook-1280538_1920.jpg 1213 943 0 0 Logo-1100x620 https://code-maze.com/net-core-series/logo-1100x620/ Sat, 27 Jan 2018 10:15:42 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/Logo-1100x620.png 1227 1008 0 0 featured-image https://code-maze.com/featured-image/ Sun, 28 Jan 2018 11:55:54 +0000 https://code-maze.com/wp-content/uploads/2018/01/featured-image.png 1232 0 0 0 19-adding-efcore-reference-toassembly https://code-maze.com/net-core-web-development-part4/19-adding-efcore-reference-toassembly/ Tue, 30 Jan 2018 10:01:01 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/19-adding-efcore-reference-toassembly.png 1257 1250 0 0 17 https://code-maze.com/17/ Tue, 30 Jan 2018 12:25:00 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/17.1-installing-Nlog-package_manager-console.png 1263 0 0 0 http-reference-featured-image https://code-maze.com/http-referemce-featured-image/ Wed, 31 Jan 2018 12:22:47 +0000 https://code-maze.com/wp-content/uploads/Vlada/HTTP_SERIES/http-referemce-featured-image.png 1300 0 0 0 http1-featured-image https://code-maze.com/http1-featured-image/ Wed, 31 Jan 2018 12:27:38 +0000 https://code-maze.com/wp-content/uploads/Vlada/HTTP_SERIES/http1-featured-image.png 1302 0 0 0 http2-featured-image https://code-maze.com/http2-featured-image/ Wed, 31 Jan 2018 12:27:51 +0000 https://code-maze.com/wp-content/uploads/Vlada/HTTP_SERIES/http2-featured-image.png 1303 0 0 0 http3-featured-image https://code-maze.com/http3-featured-image/ Wed, 31 Jan 2018 12:28:05 +0000 https://code-maze.com/wp-content/uploads/Vlada/HTTP_SERIES/http3-featured-image.png 1304 0 0 0 http4-featured-image https://code-maze.com/http4-featured-image/ Wed, 31 Jan 2018 12:28:21 +0000 https://code-maze.com/wp-content/uploads/Vlada/HTTP_SERIES/http4-featured-image.png 1305 0 0 0 http5-featured-image https://code-maze.com/http5-featured-image/ Wed, 31 Jan 2018 12:28:39 +0000 https://code-maze.com/wp-content/uploads/Vlada/HTTP_SERIES/http5-featured-image.png 1306 0 0 0 http-series https://code-maze.com/http-series/http-series-2/ Wed, 31 Jan 2018 12:28:53 +0000 https://code-maze.com/wp-content/uploads/Vlada/HTTP_SERIES/http-series.png 1307 607 0 0 Top-8-ci-tools-featured-2 https://code-maze.com/top-8-ci-tools-featured/ Wed, 31 Jan 2018 13:16:33 +0000 https://code-maze.com/wp-content/uploads/Vlada/Top8CITools/Top-8-ci-tools-featured.png 1319 0 0 0 ci-teamcity-featured https://code-maze.com/ci-teamcity-featured/ Wed, 31 Jan 2018 13:21:13 +0000 https://code-maze.com/wp-content/uploads/Vlada/TeamCity/ci-teamcity-featured.png 1322 0 0 0 what-is-ci-featured https://code-maze.com/what-is-ci-featured/ Wed, 31 Jan 2018 13:25:10 +0000 https://code-maze.com/wp-content/uploads/Vlada/WhatIsCI/what-is-ci-featured.png 1324 0 0 0 octopus-featured https://code-maze.com/octopus-featured/ Wed, 31 Jan 2018 13:29:34 +0000 https://code-maze.com/wp-content/uploads/Vlada/HowToLevelUpDeployment/octopus-featured.png 1328 0 0 0 CIfeatured https://code-maze.com/cifeatured-2/ Fri, 02 Feb 2018 07:28:23 +0000 https://code-maze.com/wp-content/uploads/2017/06/CIfeatured.png 1352 1350 0 0 http part 1 title https://code-maze.com/title1-2/ Fri, 02 Feb 2018 07:28:23 +0000 https://code-maze.com/wp-content/uploads/2017/06/Title1.png 1353 1350 0 0 Network stack https://code-maze.com/network-stack-2/ Fri, 02 Feb 2018 07:28:23 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/Network-stack.png 1354 1350 0 0 HTTPtitle https://code-maze.com/http-2/ Fri, 02 Feb 2018 07:28:23 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/HTTP.png 1355 1350 0 0 resource2darkcontrast https://code-maze.com/resource2darkcontrast-2/ Fri, 02 Feb 2018 07:28:23 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/resource2darkcontrast.png 1356 1350 0 0 resource2dark https://code-maze.com/resource2dark-2/ Fri, 02 Feb 2018 07:28:23 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/resource2dark.png 1357 1350 0 0 resource3 https://code-maze.com/original-image-1920x1280-2/ Fri, 02 Feb 2018 07:28:23 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/Original-image-1920x1280.jpg 1358 1350 0 0 digital-media-1 https://code-maze.com/digital-media-1-2/ Fri, 02 Feb 2018 07:28:23 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/digital-media-1.jpg 1359 1350 0 0 resource2 https://code-maze.com/resource2-2/ Fri, 02 Feb 2018 07:28:23 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/resource2.png 1360 1350 0 0 resource https://code-maze.com/resource-2/ Fri, 02 Feb 2018 07:28:23 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/resource.jpg 1361 1350 0 0 404batman https://code-maze.com/tumblr_ntqxdocovo1udik9co2_1280-2/ Fri, 02 Feb 2018 07:28:23 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/tumblr_ntqxdoCovo1udik9co2_1280.jpg 1362 1350 0 0 401wizard https://code-maze.com/tumblr_ntqxnerhqr1udik9co2_1280-2/ Fri, 02 Feb 2018 07:28:23 +0000 http://www.code-maze.com/wp-content/uploads/2017/06/tumblr_ntqxneRHQr1udik9co2_1280.jpg 1363 1350 0 0 http part 2 title https://code-maze.com/title-2/ Fri, 02 Feb 2018 07:28:54 +0000 https://code-maze.com/wp-content/uploads/2017/06/Title.png 1366 1365 0 0 http-part-2-title https://code-maze.com/title2generate-rasterized2-2/ Fri, 02 Feb 2018 07:28:54 +0000 https://code-maze.com/wp-content/uploads/2017/06/Title2Generate-Rasterized2.png 1367 1365 0 0 http-part-2-title https://code-maze.com/title2generate-rasterized-2/ Fri, 02 Feb 2018 07:28:54 +0000 https://code-maze.com/wp-content/uploads/2017/06/Title2Generate-Rasterized.png 1368 1365 0 0 Web-crawler https://code-maze.com/web-crawler-2/ Fri, 02 Feb 2018 07:28:54 +0000 https://code-maze.com/wp-content/uploads/2017/06/Web-crawler.png 1369 1365 0 0 reverse proxy server https://code-maze.com/proxy-server-2-2/ Fri, 02 Feb 2018 07:28:54 +0000 https://code-maze.com/wp-content/uploads/2017/06/proxy-server-1.png 1370 1365 0 0 proxy server https://code-maze.com/proxy-server-3/ Fri, 02 Feb 2018 07:28:54 +0000 https://code-maze.com/wp-content/uploads/2017/06/proxy-server.png 1371 1365 0 0 servers2 https://code-maze.com/managed-server-hosting-your-server-in-the-cloud-523968604-2/ Fri, 02 Feb 2018 07:28:54 +0000 https://code-maze.com/wp-content/uploads/2017/06/managed-server-hosting-your-server-in-the-cloud-523968604.jpg 1372 1365 0 0 servers https://code-maze.com/servers-2/ Fri, 02 Feb 2018 07:28:54 +0000 https://code-maze.com/wp-content/uploads/2017/06/servers.jpg 1373 1365 0 0 CacheFlow https://code-maze.com/cacheflow-2/ Fri, 02 Feb 2018 07:28:54 +0000 https://code-maze.com/wp-content/uploads/2017/06/CacheFlow.png 1374 1365 0 0 20-creating-ownercontroller https://code-maze.com/net-core-web-development-part5/20-creating-ownercontroller/ Tue, 06 Feb 2018 14:44:16 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/20-creating-ownercontroller.png 1399 1406 0 0 21-convetion-based-routing https://code-maze.com/net-core-web-development-part5/21-convetion-based-routing-2/ Tue, 06 Feb 2018 14:44:36 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/21-convetion-based-routing-1.png 1400 1406 0 0 22-getallowners-postman https://code-maze.com/net-core-web-development-part5/22-getallowners-postman/ Tue, 06 Feb 2018 14:45:17 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/22-getallowners-postman.png 1401 1406 0 0 23-valid-request-ownerbyid https://code-maze.com/net-core-web-development-part5/23-valid-request-ownerbyid/ Tue, 06 Feb 2018 14:45:31 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/23-valid-request-ownerbyid.png 1402 1406 0 0 24-invalid-request-ownerbyid https://code-maze.com/net-core-web-development-part5/24-invalid-request-ownerbyid/ Tue, 06 Feb 2018 14:45:40 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/24-invalid-request-ownerbyid.png 1403 1406 0 0 25-ownerdetails-request https://code-maze.com/net-core-web-development-part5/25-ownerdetails-request/ Tue, 06 Feb 2018 14:46:02 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/25-ownerdetails-request.png 1404 1406 0 0 Postman406NotAcceptable https://code-maze.com/content-negotiation-dotnet-core/postman406notacceptable/ Tue, 13 Feb 2018 09:19:03 +0000 https://code-maze.com/wp-content/uploads/Vlada/ContentNegotiationDotnetCore/Postman406NotAcceptable.png 1493 1377 0 0 PostmanDefaultResultJson https://code-maze.com/content-negotiation-dotnet-core/postmandefaultresultjson/ Tue, 13 Feb 2018 10:45:17 +0000 https://code-maze.com/wp-content/uploads/Vlada/ContentNegotiationDotnetCore/PostmanDefaultResultJson.png 1496 1377 0 0 PostmanDefaultResultXml https://code-maze.com/content-negotiation-dotnet-core/postmandefaultresultxml/ Tue, 13 Feb 2018 10:45:24 +0000 https://code-maze.com/wp-content/uploads/Vlada/ContentNegotiationDotnetCore/PostmanDefaultResultXml.png 1497 1377 0 0 PostmanDefaultResultCsv https://code-maze.com/content-negotiation-dotnet-core/postmandefaultresultcsv/ Tue, 13 Feb 2018 11:00:44 +0000 https://code-maze.com/wp-content/uploads/Vlada/ContentNegotiationDotnetCore/PostmanDefaultResultCsv.png 1499 1377 0 0 content-negotiation-featured https://code-maze.com/content-negotiation-featured/ Tue, 13 Feb 2018 11:43:33 +0000 https://code-maze.com/wp-content/uploads/Vlada/ContentNegotiationDotnetCore/content-negotiation-featured.png 1504 0 0 0 The Complete Guide to HTTP Book Mini https://code-maze.com/the-complete-guide-to-http-book-mini/ Tue, 13 Feb 2018 22:54:25 +0000 https://code-maze.com/wp-content/uploads/Vlada/The-Complete-Guide-to-HTTP-Book-Mini.png 1508 0 0 0 Subtitle https://code-maze.com/subtitle/ Wed, 14 Feb 2018 12:38:53 +0000 https://code-maze.com/wp-content/uploads/Vlada/Subtitle.png 1537 0 0 0 consume-rest-csharp-featured https://code-maze.com/consume-rest-csharp-featured/ Wed, 14 Feb 2018 13:30:35 +0000 https://code-maze.com/wp-content/uploads/Vlada/ConsumeRestfulApi/consume-rest-csharp-featured.png 1549 0 0 0 26-post-request https://code-maze.com/net-core-web-development-part6/26-post-request/ Thu, 15 Feb 2018 08:35:25 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/26-post-request.png 1550 1552 0 0 27-put-request https://code-maze.com/net-core-web-development-part6/27-put-request/ Thu, 15 Feb 2018 08:35:37 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/27-put-request.png 1551 1552 0 0 28-new-angular-project https://code-maze.com/net-core-web-development-part7/28-new-angular-project/ Tue, 20 Feb 2018 11:52:46 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/28-new-angular-project.png 1624 1627 0 0 30-bootstrap-types https://code-maze.com/net-core-web-development-part7/30-bootstrap-types/ Tue, 20 Feb 2018 11:53:36 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/30-bootstrap-types.png 1626 1627 0 0 32-menu https://code-maze.com/net-core-web-development-part8/32-menu/ Tue, 27 Feb 2018 09:10:29 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/32-menu.png 1696 1701 0 0 33-home-router https://code-maze.com/net-core-web-development-part8/33-home-router/ Tue, 27 Feb 2018 09:11:06 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/33-home-router.png 1698 1701 0 0 34-not-found-component https://code-maze.com/34-not-found-component/ Tue, 27 Feb 2018 09:11:18 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/34-not-found-component.png 1699 0 0 0 35-not-found-page https://code-maze.com/net-core-web-development-part8/35-not-found-page/ Tue, 27 Feb 2018 09:11:35 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/35-not-found-page.png 1700 1701 0 0 write-for-codemaze https://code-maze.com/write-for-codemaze/girl-1100-620/ Fri, 02 Mar 2018 22:07:51 +0000 https://code-maze.com/wp-content/uploads/2018/03/girl-1100-620.jpg 1728 1723 0 0 36-angular-version https://code-maze.com/net-core-web-development-part9/36-angular-version/ Tue, 06 Mar 2018 10:41:16 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/36-angular-version.png 1780 1782 0 0 37-environment-service-sidebar https://code-maze.com/net-core-web-development-part9/37-environment-service-sidebar/ Tue, 06 Mar 2018 10:41:27 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/37-environment-service-sidebar.png 1781 1782 0 0 38-owner-list-component https://code-maze.com/net-core-web-development-part10/38-owner-list-component/ Tue, 13 Mar 2018 06:00:07 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/38-owner-list-component.png 1845 1848 0 0 39-owner-list-navigated https://code-maze.com/net-core-web-development-part10/39-owner-list-navigated/ Tue, 13 Mar 2018 06:00:16 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/39-owner-list-navigated.png 1846 1848 0 0 25-ownerdetails-request https://code-maze.com/net-core-web-development-part5/25-ownerdetails-request-2/ Thu, 15 Mar 2018 10:48:26 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/25-ownerdetails-request-1.png 1866 1406 0 0 40-list-owners https://code-maze.com/net-core-web-development-part10/40-list-owners/ Sat, 17 Mar 2018 13:18:57 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/40-list-owners.png 1892 1848 0 0 Swagger UI https://code-maze.com/how-to-prepare-aspnetcore-app-dockerization/swagger-ui/ Sat, 17 Mar 2018 15:13:52 +0000 https://code-maze.com/wp-content/uploads/Vlada/DockerKubernetesSeries/Swagger-UI.png 1906 1857 0 0 TestResults https://code-maze.com/how-to-prepare-aspnetcore-app-dockerization/testresults/ Sat, 17 Mar 2018 18:06:36 +0000 https://code-maze.com/wp-content/uploads/Vlada/DockerKubernetesSeries/TestResults.png 1911 1857 0 0 Docker build steps https://code-maze.com/how-to-prepare-aspnetcore-app-dockerization/docker-build-steps/ Sun, 18 Mar 2018 22:34:19 +0000 https://code-maze.com/wp-content/uploads/Vlada/DockerKubernetesSeries/Docker-build-steps.png 1924 1857 0 0 Docker images https://code-maze.com/how-to-prepare-aspnetcore-app-dockerization/docker-images/ Sun, 18 Mar 2018 22:45:03 +0000 https://code-maze.com/wp-content/uploads/Vlada/DockerKubernetesSeries/Docker-images.png 1926 1857 0 0 41-owner-details https://code-maze.com/net-core-web-development-part11/41-owner-details/ Tue, 20 Mar 2018 07:05:30 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/41-owner-details.png 1937 1936 0 0 43-advanced-user-details https://code-maze.com/43-advanced-user-details/ Tue, 20 Mar 2018 07:05:54 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/43-advanced-user-details.png 1939 0 0 0 default-user-image https://code-maze.com/default-user-image/ Wed, 21 Mar 2018 12:26:40 +0000 https://code-maze.com/wp-content/uploads/2018/03/default-user-image.png 1952 0 0 0 TestResultsFailed https://code-maze.com/how-to-prepare-aspnetcore-app-dockerization/testresultsfailed/ Wed, 21 Mar 2018 20:05:52 +0000 https://code-maze.com/wp-content/uploads/Vlada/DockerKubernetesSeries/TestResultsFailed.png 1960 1857 0 0 44-error-modal-component-created https://code-maze.com/net-core-web-development-part12/44-error-modal-component-created/ Tue, 27 Mar 2018 06:49:35 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/44-error-modal-component-created.png 2013 2015 0 0 45-success-modal-component-created https://code-maze.com/45-success-modal-component-created/ Tue, 27 Mar 2018 06:49:49 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/45-success-modal-component-created.png 2014 0 0 0 Docker Hub Create Repo https://code-maze.com/why-docker-docker-cli-examples/2018-03-28-11_45_01-create-repository-docker-hub/ Wed, 28 Mar 2018 09:45:53 +0000 https://code-maze.com/wp-content/uploads/Vlada/DockerKubernetesSeries/2018-03-28-11_45_01-Create-Repository-Docker-Hub.png 2045 1988 0 0 Docker push result https://code-maze.com/why-docker-docker-cli-examples/docker-push/ Wed, 28 Mar 2018 12:16:41 +0000 https://code-maze.com/wp-content/uploads/Vlada/DockerKubernetesSeries/Docker-push.png 2048 1988 0 0 Docker Hub Tags https://code-maze.com/docker-hub-tags/ Wed, 28 Mar 2018 12:17:21 +0000 https://code-maze.com/wp-content/uploads/Vlada/DockerKubernetesSeries/Docker-Hub-Tags.png 2049 0 0 0 runtime image build result https://code-maze.com/aspnetcore-app-dockerfiles/runtime-image-build-result/ Wed, 28 Mar 2018 22:09:02 +0000 https://code-maze.com/wp-content/uploads/Vlada/DockerKubernetesSeries/runtime-image-build-result.png 2063 1990 0 0 get owners response https://code-maze.com/mysql-aspnetcore-docker-compose/swagger-ui-response/ Sun, 01 Apr 2018 14:58:22 +0000 https://code-maze.com/wp-content/uploads/Vlada/DockerKubernetesSeries/Part4/Swagger-UI-response.png 2095 2068 0 0 46-owner-create-component-created https://code-maze.com/net-core-web-development-part13/46-owner-create-component-created/ Tue, 03 Apr 2018 09:03:06 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/46-owner-create-component-created.png 2119 2118 0 0 02-mysql_models_view https://code-maze.com/02-mysql_models_view-2/ Tue, 03 Apr 2018 11:16:39 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/02-mysql_models_view.png 2133 0 0 0 05-first_table_created https://code-maze.com/05-first_table_created-2/ Tue, 03 Apr 2018 11:16:40 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/05-first_table_created..png 2134 0 0 0 08-generate-sql-script https://code-maze.com/08-generate-sql-script-2/ Tue, 03 Apr 2018 11:16:41 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/08-generate-sql-script.png 2135 0 0 0 09-database-view https://code-maze.com/09-database-view-2/ Tue, 03 Apr 2018 11:16:43 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/09-database-view.png 2136 0 0 0 47-create-form-errors_input https://code-maze.com/net-core-web-development-part13/47-create-form-errors_input/ Tue, 03 Apr 2018 11:20:02 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/47-create-form-errors_input.png 2143 2118 0 0 48-create-form-additional-errors_input https://code-maze.com/48-create-form-additional-errors_input/ Tue, 03 Apr 2018 11:20:18 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/48-create-form-additional-errors_input.png 2144 0 0 0 49-create-form-valid_input https://code-maze.com/net-core-web-development-part13/49-create-form-valid_input/ Tue, 03 Apr 2018 11:20:35 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/49-create-form-valid_input.png 2145 2118 0 0 50-create-success-modal https://code-maze.com/net-core-web-development-part13/50-create-success-modal/ Tue, 03 Apr 2018 11:20:42 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/50-create-success-modal.png 2146 2118 0 0 51-create-form-error-message-modal https://code-maze.com/51-create-form-error-message-modal/ Tue, 03 Apr 2018 11:20:48 +0000 https://code-maze.com/wp-content/uploads/Marinko/NetCoreAng4MySql/51-create-form-error-message-modal.png 2147 0 0 0 LogoWithoutLetters2 https://code-maze.com/logowithoutletters2/ Tue, 03 Apr 2018 16:38:56 +0000 https://code-maze.com/wp-content/uploads/2018/04/LogoWithoutLetters2.png 2163 0 0 0 1j+ojlxKOMkX9WyteRe4hGfa0aLVt2oe0kvbizxmfGwQoQJkkyQvgvBu471ifhAA9w9exBkOZINj0WU+BI0UiRpn...zyIMYpE https://code-maze.com/1jojlxkomkx9wytere4hgfa0alvt2oe0kvbizxmfgwqoqjkkyqvgvbu471ifhaa9w9exbkozinj0wubi0uirpn-zyimype/ Tue, 03 Apr 2018 16:50:19 +0000 https://code-maze.com/wp-content/uploads/2018/04/1jojlxKOMkX9WyteRe4hGfa0aLVt2oe0kvbizxmfGwQoQJkkyQvgvBu471ifhAA9w9exBkOZINj0WUBI0UiRpn...zyIMYpE.png 2165 0 0 0 authentication big picture https://code-maze.com/picture_1/ Tue, 03 Apr 2018 17:26:10 +0000 https://code-maze.com/wp-content/uploads/2018/04/picture_1.png 2169 0 0 0 Logo za .NET Core series https://code-maze.com/net-core-series/logo-za-net-core-series/ Sat, 07 Apr 2018 17:34:31 +0000 https://code-maze.com/wp-content/uploads/2018/04/Logo-za-.NET-Core-series-.jpg 2346 1008 0 0 JSON web token https://code-maze.com/authentication-aspnetcore-jwt-1/attachment/2/ Sun, 08 Apr 2018 15:31:58 +0000 https://code-maze.com/wp-content/uploads/2018/04/2.png 2371 2370 0 0 JWT signature https://code-maze.com/authentication-aspnetcore-jwt-1/attachment/3/ Sun, 08 Apr 2018 15:38:46 +0000 https://code-maze.com/wp-content/uploads/2018/04/3.png 2372 2370 0 0 http GET request https://code-maze.com/authentication-aspnetcore-jwt-1/attachment/4/ Sun, 08 Apr 2018 15:44:27 +0000 https://code-maze.com/wp-content/uploads/2018/04/4.png 2373 2370 0 0 http 401 response https://code-maze.com/authentication-aspnetcore-jwt-1/attachment/5/ Sun, 08 Apr 2018 15:55:06 +0000 https://code-maze.com/wp-content/uploads/2018/04/5.png 2374 2370 0 0 Login post request https://code-maze.com/6/ Sun, 08 Apr 2018 16:07:27 +0000 https://code-maze.com/wp-content/uploads/2018/04/6.png 2375 0 0 0 7 https://code-maze.com/authentication-aspnetcore-jwt-1/attachment/7/ Sun, 08 Apr 2018 16:09:41 +0000 https://code-maze.com/wp-content/uploads/2018/04/7.png 2376 2370 0 0 HTTP login POST response https://code-maze.com/authentication-aspnetcore-jwt-1/attachment/8/ Sun, 08 Apr 2018 16:10:52 +0000 https://code-maze.com/wp-content/uploads/2018/04/8.png 2377 2370 0 0 web authentication https://code-maze.com/authentication-aspnetcore-jwt-1/picture_1-2/ Sun, 08 Apr 2018 16:22:03 +0000 https://code-maze.com/wp-content/uploads/2018/04/picture_1-1.png 2378 2370 0 0 local docker registry empty https://code-maze.com/docker-hub-vs-creating-docker-registry/local-docker-registry-empty/ Mon, 09 Apr 2018 17:00:24 +0000 https://code-maze.com/wp-content/uploads/2018/04/local-docker-registry-empty.png 2411 2107 0 0 codemazeblog_accountownerapp_tags_list https://code-maze.com/docker-hub-vs-creating-docker-registry/codemazeblog_accountownerapp_tags_list/ Mon, 09 Apr 2018 18:08:18 +0000 https://code-maze.com/wp-content/uploads/2018/04/codemazeblog_accountownerapp_tags_list.png 2413 2107 0 0 52-update-owner-files https://code-maze.com/net-core-web-development-part14/52-update-owner-files/ Tue, 10 Apr 2018 09:20:30 +0000 https://code-maze.com/wp-content/uploads/2018/04/52-update-owner-files.png 2420 2421 0 0 Ray Carneiro https://code-maze.com/544785/ Wed, 11 Apr 2018 16:09:53 +0000 https://code-maze.com/wp-content/uploads/2018/04/544785.jpg 2440 0 0 0 docker-series-featured https://code-maze.com/docker-series/docker-series-featured/ Sat, 14 Apr 2018 17:57:31 +0000 https://code-maze.com/wp-content/uploads/2018/04/docker-series-featured.png 2493 1995 0 0 docker-part1 https://code-maze.com/how-to-prepare-aspnetcore-app-dockerization/docker-part1/ Sat, 14 Apr 2018 18:58:07 +0000 https://code-maze.com/wp-content/uploads/2018/03/docker-part1.png 2495 1857 0 0 docker-part2 https://code-maze.com/why-docker-docker-cli-examples/docker-part2/ Sat, 14 Apr 2018 19:00:14 +0000 https://code-maze.com/wp-content/uploads/2018/03/docker-part2.png 2496 1988 0 0 docker-part3 https://code-maze.com/aspnetcore-app-dockerfiles/docker-part3/ Sat, 14 Apr 2018 19:01:37 +0000 https://code-maze.com/wp-content/uploads/2018/03/docker-part3.png 2497 1990 0 0 docker-part4 https://code-maze.com/mysql-aspnetcore-docker-compose/docker-part4/ Sat, 14 Apr 2018 19:02:37 +0000 https://code-maze.com/wp-content/uploads/2018/04/docker-part4.png 2498 2068 0 0 docker-part5 https://code-maze.com/docker-hub-vs-creating-docker-registry/docker-part5/ Sat, 14 Apr 2018 19:03:20 +0000 https://code-maze.com/wp-content/uploads/2018/04/docker-part5.png 2499 2107 0 0 Auth-Login-POST https://code-maze.com/authentication-aspnetcore-jwt-1/auth-login-post/ Sun, 15 Apr 2018 07:10:50 +0000 https://code-maze.com/wp-content/uploads/2018/04/Auth-Login-POST.png 2509 2370 0 0 53-delete-owner-component-created https://code-maze.com/53-delete-owner-component-created/ Mon, 16 Apr 2018 18:17:40 +0000 https://code-maze.com/wp-content/uploads/2018/04/53-delete-owner-component-created.png 2525 0 0 0 54-angular-build-prod https://code-maze.com/net-core-web-development-part16/54-angular-build-prod/ Thu, 19 Apr 2018 20:52:43 +0000 https://code-maze.com/wp-content/uploads/2018/04/54-angular-build-prod.png 2545 2556 0 0 55-publish-window-solution-explorer https://code-maze.com/net-core-web-development-part16/55-publish-window-solution-explorer/ Thu, 19 Apr 2018 20:52:44 +0000 https://code-maze.com/wp-content/uploads/2018/04/55-publish-window-solution-explorer.png 2546 2556 0 0 57-installing-iis https://code-maze.com/57-installing-iis/ Thu, 19 Apr 2018 20:52:45 +0000 https://code-maze.com/wp-content/uploads/2018/04/57-installing-iis.png 2548 0 0 0 58-inetmgr https://code-maze.com/net-core-web-development-part16/58-inetmgr/ Thu, 19 Apr 2018 20:52:46 +0000 https://code-maze.com/wp-content/uploads/2018/04/58-inetmgr.png 2549 2556 0 0 59-iis-create-website https://code-maze.com/net-core-web-development-part16/59-iis-create-website/ Thu, 19 Apr 2018 20:52:47 +0000 https://code-maze.com/wp-content/uploads/2018/04/59-iis-create-website.png 2550 2556 0 0 60-iis-add-website-window https://code-maze.com/net-core-web-development-part16/60-iis-add-website-window/ Thu, 19 Apr 2018 20:52:49 +0000 https://code-maze.com/wp-content/uploads/2018/04/60-iis-add-website-window.png 2551 2556 0 0 61-iis-basic-settings https://code-maze.com/net-core-web-development-part16/61-iis-basic-settings/ Thu, 19 Apr 2018 20:52:50 +0000 https://code-maze.com/wp-content/uploads/2018/04/61-iis-basic-settings.png 2552 2556 0 0 62-iis-basic-settings-setup https://code-maze.com/net-core-web-development-part16/62-iis-basic-settings-setup/ Thu, 19 Apr 2018 20:52:52 +0000 https://code-maze.com/wp-content/uploads/2018/04/62-iis-basic-settings-setup.png 2553 2556 0 0 63-deployed-app-home https://code-maze.com/net-core-web-development-part16/63-deployed-app-home/ Thu, 19 Apr 2018 20:52:53 +0000 https://code-maze.com/wp-content/uploads/2018/04/63-deployed-app-home.png 2554 2556 0 0 64-deployed-app-list https://code-maze.com/net-core-web-development-part16/64-deployed-app-list/ Thu, 19 Apr 2018 20:52:54 +0000 https://code-maze.com/wp-content/uploads/2018/04/64-deployed-app-list.png 2555 2556 0 0 56-publish-screen VS https://code-maze.com/56-publish-screen-vs/ Sat, 21 Apr 2018 19:06:38 +0000 https://code-maze.com/wp-content/uploads/2018/04/56-publish-screen-VS.png 2573 0 0 0 Create azure directory https://code-maze.com/createazuredirectory/ Mon, 23 Apr 2018 18:59:58 +0000 https://code-maze.com/wp-content/uploads/2018/04/CreateAzureDirectory.png 2591 0 0 0 CreateAppRegistration https://code-maze.com/createappregistration/ Mon, 23 Apr 2018 19:00:14 +0000 https://code-maze.com/wp-content/uploads/2018/04/CreateAppRegistration.png 2592 0 0 0 AppRegistrationProperties https://code-maze.com/appregistrationproperties/ Mon, 23 Apr 2018 19:00:19 +0000 https://code-maze.com/wp-content/uploads/2018/04/AppRegistrationProperties.png 2593 0 0 0 CreateUser https://code-maze.com/createuser/ Mon, 23 Apr 2018 19:00:23 +0000 https://code-maze.com/wp-content/uploads/2018/04/CreateUser.png 2594 0 0 0 EndPoints-TenantId https://code-maze.com/endpoints-tenantid/ Mon, 23 Apr 2018 19:00:28 +0000 https://code-maze.com/wp-content/uploads/2018/04/EndPoints-TenantId.png 2595 0 0 0 RegisteredApp https://code-maze.com/registeredapp/ Mon, 23 Apr 2018 19:00:40 +0000 https://code-maze.com/wp-content/uploads/2018/04/RegisteredApp.png 2596 0 0 0 https://code-maze.com/webprojecttemplate/ Tue, 24 Apr 2018 20:43:46 +0000 https://code-maze.com/wp-content/uploads/2018/04/WebProjectTemplate.png 2622 0 0 0 https://code-maze.com/adauthentication/ Tue, 24 Apr 2018 20:59:15 +0000 https://code-maze.com/wp-content/uploads/2018/04/ADAuthentication.png 2623 0 0 0 65-terminal https://code-maze.com/net-core-web-development-part17/65-terminal/ Wed, 25 Apr 2018 17:04:48 +0000 https://code-maze.com/wp-content/uploads/2018/04/65-terminal.png 2631 2630 0 0 66-ip-address https://code-maze.com/net-core-web-development-part17/66-ip-address/ Wed, 25 Apr 2018 17:04:55 +0000 https://code-maze.com/wp-content/uploads/2018/04/66-ip-address.png 2632 2630 0 0 68-tables-in-mysql https://code-maze.com/68-tables-in-mysql/ Wed, 25 Apr 2018 17:05:00 +0000 https://code-maze.com/wp-content/uploads/2018/04/68-tables-in-mysql.png 2634 0 0 0 69-lower-case-names-mysql https://code-maze.com/net-core-web-development-part17/69-lower-case-names-mysql/ Wed, 25 Apr 2018 17:05:00 +0000 https://code-maze.com/wp-content/uploads/2018/04/69-lower-case-names-mysql.png 2635 2630 0 0 70-created-var-publish-foders https://code-maze.com/70-created-var-publish-foders/ Wed, 25 Apr 2018 17:05:01 +0000 https://code-maze.com/wp-content/uploads/2018/04/70-created-var-publish-foders.png 2636 0 0 0 71-started-project https://code-maze.com/net-core-web-development-part17/71-started-project/ Wed, 25 Apr 2018 17:05:02 +0000 https://code-maze.com/wp-content/uploads/2018/04/71-started-project.png 2637 2630 0 0 72-configuring-kestrel-service https://code-maze.com/72-configuring-kestrel-service/ Wed, 25 Apr 2018 17:05:03 +0000 https://code-maze.com/wp-content/uploads/2018/04/72-configuring-kestrel-service.png 2638 0 0 0 73-started-kestrel-service https://code-maze.com/net-core-web-development-part17/73-started-kestrel-service/ Wed, 25 Apr 2018 17:05:03 +0000 https://code-maze.com/wp-content/uploads/2018/04/73-started-kestrel-service.png 2639 2630 0 0 74-nginx-config https://code-maze.com/74-nginx-config/ Wed, 25 Apr 2018 17:05:04 +0000 https://code-maze.com/wp-content/uploads/2018/04/74-nginx-config.png 2640 0 0 0 75-home-deployed-linux https://code-maze.com/net-core-web-development-part17/75-home-deployed-linux/ Wed, 25 Apr 2018 17:05:05 +0000 https://code-maze.com/wp-content/uploads/2018/04/75-home-deployed-linux.png 2641 2630 0 0 76-owner-list-deployed https://code-maze.com/net-core-web-development-part17/76-owner-list-deployed/ Wed, 25 Apr 2018 17:05:06 +0000 https://code-maze.com/wp-content/uploads/2018/04/76-owner-list-deployed.png 2642 2630 0 0 67-filezilla depl https://code-maze.com/net-core-web-development-part17/67-filezilla-depl/ Fri, 27 Apr 2018 14:07:36 +0000 https://code-maze.com/wp-content/uploads/2018/04/67-filezilla-depl.png 2695 2630 0 0 cost of bug fixing https://code-maze.com/top-mobile-continuous-integration-tools/cost-of-bug-fixing/ Sat, 28 Apr 2018 17:18:45 +0000 https://code-maze.com/wp-content/uploads/2018/04/cost-of-bug-fixing.jpg 2725 2719 0 0 buddybuild page https://code-maze.com/top-mobile-continuous-integration-tools/buddybuild-page/ Sat, 28 Apr 2018 17:43:11 +0000 https://code-maze.com/wp-content/uploads/2018/04/buddybuild-page.png 2729 2719 0 0 fastlane page https://code-maze.com/top-mobile-continuous-integration-tools/fastlane-page/ Sat, 28 Apr 2018 17:43:14 +0000 https://code-maze.com/wp-content/uploads/2018/04/fastlane-page.png 2730 2719 0 0 Nevercode page https://code-maze.com/top-mobile-continuous-integration-tools/nevercode-page/ Sat, 28 Apr 2018 17:43:19 +0000 https://code-maze.com/wp-content/uploads/2018/04/Nevercode-page.png 2731 2719 0 0 Visual Studio App Center page https://code-maze.com/top-mobile-continuous-integration-tools/visual-studio-app-center-page/ Sat, 28 Apr 2018 17:43:23 +0000 https://code-maze.com/wp-content/uploads/2018/04/Visual-Studio-App-Center-page.png 2732 2719 0 0 buddybuild sign-in fail https://code-maze.com/top-mobile-continuous-integration-tools/buddybuild-sign-in-fail/ Sun, 29 Apr 2018 11:03:49 +0000 https://code-maze.com/wp-content/uploads/2018/04/buddybuild-sign-in-fail.png 2749 2719 0 0 troy hunt interview https://code-maze.com/interview-with-troy-hunt/troy-hunt-interview/ Sun, 29 Apr 2018 19:54:11 +0000 https://code-maze.com/wp-content/uploads/2018/04/troy-hunt-interview.png 2768 2677 0 0 troy hunt interview transcript https://code-maze.com/troy-hunt-interview-transcript/troy-hunt-interview-transcript-2/ Sun, 29 Apr 2018 20:34:29 +0000 https://code-maze.com/wp-content/uploads/2018/04/troy-hunt-interview-transcript.png 2770 2670 0 0 React series https://code-maze.com/react-series/logo-za-react-series/ Mon, 30 Apr 2018 11:43:31 +0000 https://code-maze.com/wp-content/uploads/2018/04/Logo-za-React-series.png 2779 2778 0 0 pexels-photo-209692 https://code-maze.com/pexels-photo-209692/ Tue, 01 May 2018 22:04:07 +0000 https://code-maze.com/wp-content/uploads/2018/05/pexels-photo-209692.jpeg 2793 0 0 0 notebook-1280538_1920 https://code-maze.com/notebook-1280538_1920-2/ Tue, 01 May 2018 22:05:06 +0000 https://code-maze.com/wp-content/uploads/2018/05/notebook-1280538_1920.jpg 2794 0 0 0 default-user-image https://code-maze.com/default-user-image-2/ Wed, 02 May 2018 14:26:24 +0000 https://code-maze.com/wp-content/uploads/2018/05/default-user-image.png 2828 0 0 0 top mobile ci tools https://code-maze.com/top-mobile-continuous-integration-tools/content-negotiation-featured-2/ Wed, 02 May 2018 15:19:46 +0000 https://code-maze.com/wp-content/uploads/2018/05/content-negotiation-featured.png 2834 2719 0 0 01-Created-Project https://code-maze.com/react-dot-net-core-creating-react-project/01-created-project/ Thu, 03 May 2018 16:39:44 +0000 https://code-maze.com/wp-content/uploads/2018/05/01-Created-Project.png 2865 2863 0 0 02-React Bootstrap https://code-maze.com/react-dot-net-core-creating-react-project/02-react-bootstrap/ Thu, 03 May 2018 16:39:46 +0000 https://code-maze.com/wp-content/uploads/2018/05/02-React-Bootstrap.png 2866 2863 0 0 03-Folder structure https://code-maze.com/react-dot-net-core-creating-react-project/03-folder-structure/ Thu, 03 May 2018 16:39:47 +0000 https://code-maze.com/wp-content/uploads/2018/05/03-Folder-structure.png 2867 2863 0 0 04-Layout created https://code-maze.com/04-layout-created/ Thu, 03 May 2018 16:39:48 +0000 https://code-maze.com/wp-content/uploads/2018/05/04-Layout-created.png 2868 0 0 0 05-First page https://code-maze.com/react-dot-net-core-creating-react-project/05-first-page/ Thu, 03 May 2018 16:39:48 +0000 https://code-maze.com/wp-content/uploads/2018/05/05-First-page.png 2869 2863 0 0 07-Home component https://code-maze.com/07-home-component/ Thu, 03 May 2018 16:39:48 +0000 https://code-maze.com/wp-content/uploads/2018/05/07-Home-component.png 2870 0 0 0 06-Home component folder https://code-maze.com/react-dot-net-core-creating-react-project/06-home-component-folder/ Thu, 03 May 2018 17:06:39 +0000 https://code-maze.com/wp-content/uploads/2018/05/06-Home-component-folder.png 2873 2863 0 0 the complete guide to http book https://code-maze.com/?attachment_id=2894 Fri, 04 May 2018 11:48:06 +0000 https://code-maze.com/wp-content/uploads/dlm_uploads/2018/05/the-complete-guide-to-http-book.pdf 2894 2890 0 0 jwt-forbidden https://code-maze.com/authentication-aspnetcore-jwt-2/jwt-forbidden/ Sat, 05 May 2018 08:50:33 +0000 https://code-maze.com/wp-content/uploads/2018/05/jwt-forbidden.png 2913 2753 0 0 08-React-router-dom installation https://code-maze.com/react-dot-net-core-navigation-routing/08-react-router-dom-installation/ Tue, 08 May 2018 09:15:54 +0000 https://code-maze.com/wp-content/uploads/2018/05/08-React-router-dom-installation.png 2972 2971 0 0 09-Navigation folder structure https://code-maze.com/react-dot-net-core-navigation-routing/09-navigation-folder-structure/ Tue, 08 May 2018 09:15:55 +0000 https://code-maze.com/wp-content/uploads/2018/05/09-Navigation-folder-structure.png 2973 2971 0 0 10-React-router-bootstrap https://code-maze.com/react-dot-net-core-navigation-routing/10-react-router-bootstrap/ Tue, 08 May 2018 09:15:55 +0000 https://code-maze.com/wp-content/uploads/2018/05/10-React-router-bootstrap.png 2974 2971 0 0 11-Navigation menu https://code-maze.com/react-dot-net-core-navigation-routing/11-navigation-menu/ Tue, 08 May 2018 09:15:56 +0000 https://code-maze.com/wp-content/uploads/2018/05/11-Navigation-menu.png 2975 2971 0 0 12-ErrorPages structure https://code-maze.com/react-dot-net-core-navigation-routing/12-errorpages-structure/ Tue, 08 May 2018 09:15:56 +0000 https://code-maze.com/wp-content/uploads/2018/05/12-ErrorPages-structure.png 2976 2971 0 0 13-NotFound https://code-maze.com/react-dot-net-core-navigation-routing/13-notfound/ Tue, 08 May 2018 09:15:57 +0000 https://code-maze.com/wp-content/uploads/2018/05/13-NotFound.png 2977 2971 0 0 Mobile CI vs general purpose CI https://code-maze.com/ci-tools-for-mobile-apps/18-04-26_article-illustrations-20-2/ Tue, 08 May 2018 18:27:18 +0000 https://code-maze.com/wp-content/uploads/2018/05/18-04-26_article-illustrations-20-2.jpg 2992 2818 0 0 Mobile CI_Testing https://code-maze.com/ci-tools-for-mobile-apps/18-04-26_article-illustrations-21-2/ Tue, 08 May 2018 18:33:20 +0000 https://code-maze.com/wp-content/uploads/2018/05/18-04-26_article-illustrations-21-2.jpg 2993 2818 0 0 Mobile CI_Distribution https://code-maze.com/ci-tools-for-mobile-apps/18-04-26_article-illustrations-22-2/ Tue, 08 May 2018 18:36:26 +0000 https://code-maze.com/wp-content/uploads/2018/05/18-04-26_article-illustrations-22-2.jpg 2994 2818 0 0 bitrise front page https://code-maze.com/top-mobile-continuous-integration-tools/bitrise-page-2/ Thu, 10 May 2018 19:55:12 +0000 https://code-maze.com/wp-content/uploads/2018/05/bitrise-page-2.png 3010 2719 0 0 14-Axios install https://code-maze.com/14-axios-install/ Sat, 12 May 2018 16:34:00 +0000 https://code-maze.com/wp-content/uploads/2018/05/14-Axios-install.png 3029 0 0 0 15-Redux installation https://code-maze.com/react-net-core-http-axios-redux/15-redux-installation/ Sat, 12 May 2018 16:34:01 +0000 https://code-maze.com/wp-content/uploads/2018/05/15-Redux-installation.png 3030 3036 0 0 16-React-Redux install https://code-maze.com/16-react-redux-install/ Sat, 12 May 2018 16:34:01 +0000 https://code-maze.com/wp-content/uploads/2018/05/16-React-Redux-install.png 3031 0 0 0 17-Redux folder structure https://code-maze.com/17-redux-folder-structure/ Sat, 12 May 2018 16:34:01 +0000 https://code-maze.com/wp-content/uploads/2018/05/17-Redux-folder-structure.png 3032 0 0 0 18-Reducer file https://code-maze.com/react-net-core-http-axios-redux/18-reducer-file/ Sat, 12 May 2018 16:34:02 +0000 https://code-maze.com/wp-content/uploads/2018/05/18-Reducer-file.png 3033 3036 0 0 19-Redux-thunk https://code-maze.com/react-net-core-http-axios-redux/19-redux-thunk/ Sat, 12 May 2018 16:34:02 +0000 https://code-maze.com/wp-content/uploads/2018/05/19-Redux-thunk.png 3034 3036 0 0 20-Redux diagram https://code-maze.com/react-net-core-http-axios-redux/20-redux-diagram/ Sat, 12 May 2018 16:34:02 +0000 https://code-maze.com/wp-content/uploads/2018/05/20-Redux-diagram.png 3035 3036 0 0 Ana https://code-maze.com/olympus-digital-camera/ Sun, 13 May 2018 10:04:30 +0000 https://code-maze.com/wp-content/uploads/2018/05/P9170022.jpg 3080 0 0 0 01-Project-Structure https://code-maze.com/01-project-structure/ Mon, 14 May 2018 06:47:13 +0000 https://code-maze.com/wp-content/uploads/2018/05/01-Project-Structure.png 3103 0 0 0 02-Appsettings-development https://code-maze.com/02-appsettings-development/ Mon, 14 May 2018 06:47:14 +0000 https://code-maze.com/wp-content/uploads/2018/05/02-Appsettings-development.png 3104 0 0 0 03-Appsettings-production https://code-maze.com/03-appsettings-production/ Mon, 14 May 2018 06:47:14 +0000 https://code-maze.com/wp-content/uploads/2018/05/03-Appsettings-production.png 3105 0 0 0 infographic front https://code-maze.com/infographic-front/ Fri, 18 May 2018 21:50:20 +0000 https://code-maze.com/wp-content/uploads/2018/05/infographic-front.png 3132 0 0 0 Agent authorization https://code-maze.com/preparing-ci-environment-docker/agent-authorization/ Sat, 19 May 2018 23:30:55 +0000 https://code-maze.com/wp-content/uploads/2018/05/Agent-authorization.png 3149 2415 0 0 teamcity project init https://code-maze.com/preparing-ci-environment-docker/teamcity-project-init/ Sun, 20 May 2018 11:59:43 +0000 https://code-maze.com/wp-content/uploads/2018/05/teamcity-project-init.gif 3152 2415 0 0 edit default branch https://code-maze.com/preparing-ci-environment-docker/edit-default-branch/ Sun, 20 May 2018 12:17:05 +0000 https://code-maze.com/wp-content/uploads/2018/05/edit-default-branch.gif 3154 2415 0 0 29-installed-bootstrap-ang6 https://code-maze.com/net-core-web-development-part7/29-installed-bootstrap-ang6/ Sun, 20 May 2018 15:07:50 +0000 https://code-maze.com/wp-content/uploads/2018/05/29-installed-bootstrap-ang6.png 3160 1627 0 0 registry tags https://code-maze.com/preparing-ci-environment-docker/registry-tags/ Sun, 20 May 2018 16:38:57 +0000 https://code-maze.com/wp-content/uploads/2018/05/registry-tags.png 3162 2415 0 0 registry images https://code-maze.com/preparing-ci-environment-docker/registry-images/ Sun, 20 May 2018 16:42:58 +0000 https://code-maze.com/wp-content/uploads/2018/05/registry-images.png 3163 2415 0 0 dotnet-core-best-practices https://code-maze.com/aspnetcore-webapi-best-practices/dotnet-core-best-practices/ Sun, 20 May 2018 21:55:21 +0000 https://code-maze.com/wp-content/uploads/2018/05/dotnet-core-best-practices.png 3174 3106 0 0 21-OwnerList structure https://code-maze.com/react-net-core-lazy-loading/21-ownerlist-structure/ Mon, 21 May 2018 17:16:28 +0000 https://code-maze.com/wp-content/uploads/2018/05/21-OwnerList-structure.png 3186 3193 0 0 22-Auxiliary structure https://code-maze.com/22-auxiliary-structure/ Mon, 21 May 2018 17:16:29 +0000 https://code-maze.com/wp-content/uploads/2018/05/22-Auxiliary-structure.png 3187 0 0 0 23-OwnerActions menu https://code-maze.com/react-net-core-lazy-loading/23-owneractions-menu/ Mon, 21 May 2018 17:16:29 +0000 https://code-maze.com/wp-content/uploads/2018/05/23-OwnerActions-menu.png 3188 3193 0 0 24-OwnerComponent https://code-maze.com/24-ownercomponent/ Mon, 21 May 2018 17:16:29 +0000 https://code-maze.com/wp-content/uploads/2018/05/24-OwnerComponent.png 3189 0 0 0 25-React-moment https://code-maze.com/react-net-core-lazy-loading/25-react-moment/ Mon, 21 May 2018 17:16:30 +0000 https://code-maze.com/wp-content/uploads/2018/05/25-React-moment.png 3190 3193 0 0 26-OwnersList display https://code-maze.com/react-net-core-lazy-loading/26-ownerslist-display/ Mon, 21 May 2018 17:16:30 +0000 https://code-maze.com/wp-content/uploads/2018/05/26-OwnersList-display.png 3191 3193 0 0 27-LazyLoading https://code-maze.com/27-lazyloading/ Mon, 21 May 2018 17:16:31 +0000 https://code-maze.com/wp-content/uploads/2018/05/27-LazyLoading.png 3192 0 0 0 integration tests failed https://code-maze.com/ci-aspnetcoreapp-docker/tests-failed/ Sat, 26 May 2018 16:24:03 +0000 https://code-maze.com/wp-content/uploads/2018/05/tests-failed.png 3220 3183 0 0 preparing continuous integration for docker https://code-maze.com/preparing-ci-environment-docker/docker-part6/ Mon, 28 May 2018 04:52:37 +0000 https://code-maze.com/wp-content/uploads/2018/05/docker-part6.png 3235 2415 0 0 01-Folder-structure https://code-maze.com/angular-best-practices/01-folder-structure/ Mon, 28 May 2018 06:44:43 +0000 https://code-maze.com/wp-content/uploads/2018/05/01-Folder-structure.png 3240 3243 0 0 02-Interface-not-implemented https://code-maze.com/02-interface-not-implemented/ Mon, 28 May 2018 06:44:44 +0000 https://code-maze.com/wp-content/uploads/2018/05/02-Interface-not-implemented.png 3241 0 0 0 03-Interface-intellisense https://code-maze.com/angular-best-practices/03-interface-intellisense/ Mon, 28 May 2018 06:44:44 +0000 https://code-maze.com/wp-content/uploads/2018/05/03-Interface-intellisense.png 3242 3243 0 0 28-Internal server error structure https://code-maze.com/28-internal-server-error-structure/ Wed, 30 May 2018 14:19:25 +0000 https://code-maze.com/wp-content/uploads/2018/05/28-Internal-server-error-structure.png 3281 0 0 0 29-Reducers folder structure https://code-maze.com/29-reducers-folder-structure/ Wed, 30 May 2018 14:19:26 +0000 https://code-maze.com/wp-content/uploads/2018/05/29-Reducers-folder-structure.png 3282 0 0 0 30-OwnersAccounts structure https://code-maze.com/react-net-core-error-handling/30-ownersaccounts-structure/ Wed, 30 May 2018 14:19:26 +0000 https://code-maze.com/wp-content/uploads/2018/05/30-OwnersAccounts-structure.png 3283 3287 0 0 31-OwnerDetails structure https://code-maze.com/31-ownerdetails-structure/ Wed, 30 May 2018 14:19:26 +0000 https://code-maze.com/wp-content/uploads/2018/05/31-OwnerDetails-structure.png 3284 0 0 0 32-Beginner user owner details https://code-maze.com/react-net-core-error-handling/32-beginner-user-owner-details/ Wed, 30 May 2018 14:19:27 +0000 https://code-maze.com/wp-content/uploads/2018/05/32-Beginner-user-owner-details.png 3285 3287 0 0 33-Advanced user owner details https://code-maze.com/33-advanced-user-owner-details/ Wed, 30 May 2018 14:19:27 +0000 https://code-maze.com/wp-content/uploads/2018/05/33-Advanced-user-owner-details.png 3286 0 0 0 tests passed https://code-maze.com/ci-aspnetcoreapp-docker/tests-passed/ Sat, 02 Jun 2018 04:32:12 +0000 https://code-maze.com/wp-content/uploads/2018/06/tests-passed.png 3313 3183 0 0 tc agent docker compose https://code-maze.com/ci-aspnetcoreapp-docker/tc-agent-docker-compose/ Sat, 02 Jun 2018 17:14:12 +0000 https://code-maze.com/wp-content/uploads/2018/06/tc-agent-docker-compose.png 3321 3183 0 0 create integration build configuration https://code-maze.com/ci-aspnetcoreapp-docker/create-integration-build-configuration/ Sat, 02 Jun 2018 17:34:22 +0000 https://code-maze.com/wp-content/uploads/2018/06/create-integration-build-configuration.gif 3323 3183 0 0 tests error message https://code-maze.com/ci-aspnetcoreapp-docker/tests-error-message/ Sat, 02 Jun 2018 17:54:00 +0000 https://code-maze.com/wp-content/uploads/2018/06/tests-error-message.png 3324 3183 0 0 tests failed tc https://code-maze.com/ci-aspnetcoreapp-docker/tests-failed-tc/ Sat, 02 Jun 2018 17:54:16 +0000 https://code-maze.com/wp-content/uploads/2018/06/tests-failed-tc.png 3325 3183 0 0 tests passed tc https://code-maze.com/ci-aspnetcoreapp-docker/tests-passed-tc/ Sat, 02 Jun 2018 20:21:01 +0000 https://code-maze.com/wp-content/uploads/2018/06/tests-passed-tc.png 3327 3183 0 0 Docker Build step https://code-maze.com/ci-aspnetcoreapp-docker/docker-build-step/ Sun, 03 Jun 2018 10:30:05 +0000 https://code-maze.com/wp-content/uploads/2018/06/Docker-Build-step.png 3331 3183 0 0 Docker Push step https://code-maze.com/ci-aspnetcoreapp-docker/docker-push-step/ Sun, 03 Jun 2018 10:33:58 +0000 https://code-maze.com/wp-content/uploads/2018/06/Docker-Push-step.png 3332 3183 0 0 registry https://code-maze.com/ci-aspnetcoreapp-docker/registry/ Sun, 03 Jun 2018 10:37:37 +0000 https://code-maze.com/wp-content/uploads/2018/06/registry.png 3333 3183 0 0 build configurations https://code-maze.com/ci-aspnetcoreapp-docker/build-configurations/ Sun, 03 Jun 2018 10:39:52 +0000 https://code-maze.com/wp-content/uploads/2018/06/build-configurations.png 3334 3183 0 0 create dependency https://code-maze.com/ci-aspnetcoreapp-docker/create-dependency/ Sun, 03 Jun 2018 10:48:00 +0000 https://code-maze.com/wp-content/uploads/2018/06/create-dependency.gif 3335 3183 0 0 build chain https://code-maze.com/ci-aspnetcoreapp-docker/build-chain/ Sun, 03 Jun 2018 11:20:13 +0000 https://code-maze.com/wp-content/uploads/2018/06/build-chain.png 3337 3183 0 0 dependency build number https://code-maze.com/ci-aspnetcoreapp-docker/dependency-build-number/ Sun, 03 Jun 2018 11:26:32 +0000 https://code-maze.com/wp-content/uploads/2018/06/dependency-build-number.png 3338 3183 0 0 create env variable https://code-maze.com/ci-aspnetcoreapp-docker/create-env-variable/ Sun, 03 Jun 2018 11:34:02 +0000 https://code-maze.com/wp-content/uploads/2018/06/create-env-variable.gif 3341 3183 0 0 config result https://code-maze.com/ci-aspnetcoreapp-docker/config-result/ Sun, 03 Jun 2018 11:44:41 +0000 https://code-maze.com/wp-content/uploads/2018/06/config-result.png 3343 3183 0 0 build process finished https://code-maze.com/ci-aspnetcoreapp-docker/build-process-finished/ Sun, 03 Jun 2018 11:55:08 +0000 https://code-maze.com/wp-content/uploads/2018/06/build-process-finished.png 3344 3183 0 0 build process failed https://code-maze.com/ci-aspnetcoreapp-docker/build-process-failed/ Sun, 03 Jun 2018 12:09:13 +0000 https://code-maze.com/wp-content/uploads/2018/06/build-process-failed.png 3345 3183 0 0 tests failed tc visible https://code-maze.com/ci-aspnetcoreapp-docker/tests-failed-tc-visible/ Sun, 03 Jun 2018 20:22:14 +0000 https://code-maze.com/wp-content/uploads/2018/06/tests-failed-tc-visible.png 3347 3183 0 0 docker teamcity featured https://code-maze.com/ci-aspnetcoreapp-docker/docker-part7/ Sun, 03 Jun 2018 21:10:24 +0000 https://code-maze.com/wp-content/uploads/2018/06/docker-part7.png 3354 3183 0 0 AsyncMainDiagram https://code-maze.com/asyncmaindiagram/ Mon, 04 Jun 2018 09:08:43 +0000 https://code-maze.com/wp-content/uploads/2018/06/AsyncMainDiagram.png 3377 0 0 0 AsyncMainDiagram https://code-maze.com/async-generic-repository-pattern/asyncmaindiagram-2/ Mon, 04 Jun 2018 09:09:05 +0000 https://code-maze.com/wp-content/uploads/2018/06/AsyncMainDiagram-1.png 3378 3339 0 0 tests passed tc visible https://code-maze.com/ci-aspnetcoreapp-docker/tests-passed-tc-visible/ Tue, 05 Jun 2018 07:05:35 +0000 https://code-maze.com/wp-content/uploads/2018/06/tests-passed-tc-visible.png 3390 3183 0 0 34-Modal folder structure https://code-maze.com/34-modal-folder-structure/ Wed, 06 Jun 2018 17:01:22 +0000 https://code-maze.com/wp-content/uploads/2018/06/34-Modal-folder-structure.png 3402 0 0 0 35-Input structure https://code-maze.com/react-dotnetcore-dynamic-forms-modals/35-input-structure/ Wed, 06 Jun 2018 17:01:23 +0000 https://code-maze.com/wp-content/uploads/2018/06/35-Input-structure.png 3403 3407 0 0 36-React datepicker https://code-maze.com/react-dotnetcore-dynamic-forms-modals/36-react-datepicker/ Wed, 06 Jun 2018 17:01:23 +0000 https://code-maze.com/wp-content/uploads/2018/06/36-React-datepicker.png 3404 3407 0 0 37-Create form input fields https://code-maze.com/react-dotnetcore-dynamic-forms-modals/37-create-form-input-fields/ Wed, 06 Jun 2018 17:01:24 +0000 https://code-maze.com/wp-content/uploads/2018/06/37-Create-form-input-fields.png 3405 3407 0 0 38-Create form completed https://code-maze.com/38-create-form-completed/ Wed, 06 Jun 2018 17:01:25 +0000 https://code-maze.com/wp-content/uploads/2018/06/38-Create-form-completed.png 3406 0 0 0 dotnet-core-best-practices https://code-maze.com/aspnetcore-webapi-best-practices/dotnet-core-best-practices-2/ Wed, 06 Jun 2018 23:11:41 +0000 https://code-maze.com/wp-content/uploads/2018/05/dotnet-core-best-practices-1.png 3416 3106 0 0 CREATE - solution vs2017 https://code-maze.com/create-solution-vs2017/ Sat, 09 Jun 2018 10:44:27 +0000 https://code-maze.com/wp-content/uploads/2018/06/CREATE-solution-vs2017.png 3445 0 0 0 CREATE - solution structure https://code-maze.com/create-solution-structure/ Sat, 09 Jun 2018 10:50:31 +0000 https://code-maze.com/wp-content/uploads/2018/06/CREATE-solution-structure.png 3446 0 0 0 39-Empty form disabled create https://code-maze.com/react-dotnetcore-form-validation-post-request/39-empty-form-disabled-create/ Thu, 14 Jun 2018 06:00:47 +0000 https://code-maze.com/wp-content/uploads/2018/06/39-Empty-form-disabled-create.png 3469 3475 0 0 40-Create form touched https://code-maze.com/react-dotnetcore-form-validation-post-request/40-create-form-touched/ Thu, 14 Jun 2018 06:00:48 +0000 https://code-maze.com/wp-content/uploads/2018/06/40-Create-form-touched.png 3470 3475 0 0 41-Create form more characters than allowed https://code-maze.com/react-dotnetcore-form-validation-post-request/41-create-form-more-characters-than-allowed/ Thu, 14 Jun 2018 06:00:49 +0000 https://code-maze.com/wp-content/uploads/2018/06/41-Create-form-more-characters-than-allowed.png 3471 3475 0 0 42-Create form valid https://code-maze.com/react-dotnetcore-form-validation-post-request/42-create-form-valid/ Thu, 14 Jun 2018 06:00:50 +0000 https://code-maze.com/wp-content/uploads/2018/06/42-Create-form-valid.png 3472 3475 0 0 01-CreatingProject https://code-maze.com/create-pdf-dotnetcore/01-creatingproject/ Sat, 16 Jun 2018 21:02:09 +0000 https://code-maze.com/wp-content/uploads/2018/06/01-CreatingProject.png 3488 3498 0 0 02-Nuget Package https://code-maze.com/create-pdf-dotnetcore/02-nuget-package/ Sat, 16 Jun 2018 21:02:11 +0000 https://code-maze.com/wp-content/uploads/2018/06/02-Nuget-Package.png 3489 3498 0 0 03-Native Library https://code-maze.com/03-native-library/ Sat, 16 Jun 2018 21:02:12 +0000 https://code-maze.com/wp-content/uploads/2018/06/03-Native-Library.png 3490 0 0 0 04-Utility Folder Structure https://code-maze.com/create-pdf-dotnetcore/04-utility-folder-structure/ Sat, 16 Jun 2018 21:02:12 +0000 https://code-maze.com/wp-content/uploads/2018/06/04-Utility-Folder-Structure.png 3491 3498 0 0 05-Request for PDF creation https://code-maze.com/create-pdf-dotnetcore/05-request-for-pdf-creation/ Sat, 16 Jun 2018 21:02:12 +0000 https://code-maze.com/wp-content/uploads/2018/06/05-Request-for-PDF-creation.png 3492 3498 0 0 06-Created document https://code-maze.com/06-created-document/ Sat, 16 Jun 2018 21:02:13 +0000 https://code-maze.com/wp-content/uploads/2018/06/06-Created-document.png 3493 0 0 0 07-PDF Content https://code-maze.com/create-pdf-dotnetcore/07-pdf-content/ Sat, 16 Jun 2018 21:02:13 +0000 https://code-maze.com/wp-content/uploads/2018/06/07-PDF-Content.png 3494 3498 0 0 08-PDF browser content https://code-maze.com/08-pdf-browser-content/ Sat, 16 Jun 2018 21:02:15 +0000 https://code-maze.com/wp-content/uploads/2018/06/08-PDF-browser-content.png 3495 0 0 0 09-PDF browser HTML page content https://code-maze.com/create-pdf-dotnetcore/09-pdf-browser-html-page-content/ Sat, 16 Jun 2018 21:02:17 +0000 https://code-maze.com/wp-content/uploads/2018/06/09-PDF-browser-HTML-page-content.png 3496 3498 0 0 10-PDF downloaded file https://code-maze.com/create-pdf-dotnetcore/10-pdf-downloaded-file/ Sat, 16 Jun 2018 21:02:19 +0000 https://code-maze.com/wp-content/uploads/2018/06/10-PDF-downloaded-file.png 3497 3498 0 0 create-pdf-featured (2) https://code-maze.com/create-pdf-dotnetcore/create-pdf-featured-2/ Sun, 17 Jun 2018 19:31:24 +0000 https://code-maze.com/wp-content/uploads/2018/06/create-pdf-featured-2.png 3529 3498 0 0 ravi https://code-maze.com/ravi/ Sun, 17 Jun 2018 20:05:13 +0000 https://code-maze.com/wp-content/uploads/2018/06/ravi.jpg 3531 0 0 0 44-Error modal create owner bad request https://code-maze.com/react-dotnetcore-form-validation-post-request/44-error-modal-create-owner-bad-request/ Tue, 19 Jun 2018 17:37:13 +0000 https://code-maze.com/wp-content/uploads/2018/06/44-Error-modal-create-owner-bad-request.png 3586 3475 0 0 Enabling CORS in ASP.NET Core https://code-maze.com/enabling-cors-in-asp-net-core/enabling-cors-in-asp-net-core-1/ Tue, 19 Jun 2018 21:47:27 +0000 https://code-maze.com/wp-content/uploads/2018/06/Enabling-CORS-in-ASP.NET-Core-1.png 3590 3565 0 0 Preflight Request https://code-maze.com/enabling-cors-in-asp-net-core/preflight-request-1/ Tue, 19 Jun 2018 21:47:58 +0000 https://code-maze.com/wp-content/uploads/2018/06/Preflight-Request-1.png 3591 3565 0 0 Actual Request https://code-maze.com/enabling-cors-in-asp-net-core/actual-reques-1t/ Tue, 19 Jun 2018 21:48:48 +0000 https://code-maze.com/wp-content/uploads/2018/06/Actual-Reques-1t.png 3592 3565 0 0 43-Success create owner modal https://code-maze.com/react-dotnetcore-form-validation-post-request/43-success-create-owner-modal/ Tue, 19 Jun 2018 21:56:14 +0000 https://code-maze.com/wp-content/uploads/2018/06/43-Success-create-owner-modal.png 3596 3475 0 0 45-UpdateOwner structure https://code-maze.com/handling-put-request/45-updateowner-structure/ Wed, 20 Jun 2018 20:14:36 +0000 https://code-maze.com/wp-content/uploads/2018/06/45-UpdateOwner-structure.png 3612 3611 0 0 46-UpdateOwner initial view https://code-maze.com/46-updateowner-initial-view/ Wed, 20 Jun 2018 20:14:36 +0000 https://code-maze.com/wp-content/uploads/2018/06/46-UpdateOwner-initial-view.png 3613 0 0 0 47-UpdateOwner populated form https://code-maze.com/47-updateowner-populated-form/ Wed, 20 Jun 2018 20:14:37 +0000 https://code-maze.com/wp-content/uploads/2018/06/47-UpdateOwner-populated-form.png 3614 0 0 0 New Project https://code-maze.com/enabling-cors-in-asp-net-core/new-project-2/ Fri, 22 Jun 2018 14:59:23 +0000 https://code-maze.com/wp-content/uploads/2018/06/New-Project.png 3622 3565 0 0 New Project https://code-maze.com/enabling-cors-in-asp-net-core/new-project-3/ Fri, 22 Jun 2018 14:59:56 +0000 https://code-maze.com/wp-content/uploads/2018/06/New-Project-1.png 3623 3565 0 0 Azure Web App settings https://code-maze.com/enabling-cors-in-asp-net-core/azure-web-app-settings/ Fri, 22 Jun 2018 15:03:41 +0000 https://code-maze.com/wp-content/uploads/2018/06/Azure-Web-App-settings.png 3624 3565 0 0 Live app https://code-maze.com/enabling-cors-in-asp-net-core/live-app/ Fri, 22 Jun 2018 15:04:54 +0000 https://code-maze.com/wp-content/uploads/2018/06/Live-app.png 3625 3565 0 0 Error https://code-maze.com/enabling-cors-in-asp-net-core/error/ Fri, 22 Jun 2018 15:05:58 +0000 https://code-maze.com/wp-content/uploads/2018/06/Error.png 3626 3565 0 0 data from server app https://code-maze.com/enabling-cors-in-asp-net-core/data-from-server-app/ Fri, 22 Jun 2018 15:07:24 +0000 https://code-maze.com/wp-content/uploads/2018/06/data-from-server-app.png 3627 3565 0 0 async generic repository https://code-maze.com/async-generic-repository-pattern/async-generic-repository/ Sun, 24 Jun 2018 21:32:25 +0000 https://code-maze.com/wp-content/uploads/2018/06/async-generic-repository.png 3647 3339 0 0 01-BasicRequest https://code-maze.com/global-error-handling-aspnetcore/01-basicrequest/ Tue, 26 Jun 2018 10:45:46 +0000 https://code-maze.com/wp-content/uploads/2018/06/01-BasicRequest.png 3658 3666 0 0 02-LogBasicRequest https://code-maze.com/02-logbasicrequest/ Tue, 26 Jun 2018 10:45:48 +0000 https://code-maze.com/wp-content/uploads/2018/06/02-LogBasicRequest.png 3659 0 0 0 03-Try-Catche-Error https://code-maze.com/03-try-catche-error/ Tue, 26 Jun 2018 10:45:49 +0000 https://code-maze.com/wp-content/uploads/2018/06/03-Try-Catche-Error.png 3660 0 0 0 04-Log-Try-Catche-Error https://code-maze.com/global-error-handling-aspnetcore/04-log-try-catche-error/ Tue, 26 Jun 2018 10:45:49 +0000 https://code-maze.com/wp-content/uploads/2018/06/04-Log-Try-Catche-Error.png 3661 3666 0 0 05-GlobalHandlerMiddleware https://code-maze.com/05-globalhandlermiddleware/ Tue, 26 Jun 2018 10:45:49 +0000 https://code-maze.com/wp-content/uploads/2018/06/05-GlobalHandlerMiddleware.png 3662 0 0 0 06-LogGlobalHandlerMiddleware https://code-maze.com/global-error-handling-aspnetcore/06-logglobalhandlermiddleware/ Tue, 26 Jun 2018 10:45:50 +0000 https://code-maze.com/wp-content/uploads/2018/06/06-LogGlobalHandlerMiddleware.png 3663 3666 0 0 07-GlobalCustomHandlerMiddleware https://code-maze.com/global-error-handling-aspnetcore/07-globalcustomhandlermiddleware/ Tue, 26 Jun 2018 10:45:51 +0000 https://code-maze.com/wp-content/uploads/2018/06/07-GlobalCustomHandlerMiddleware.png 3664 3666 0 0 08-LogGlobalCustomHandlerMiddleware https://code-maze.com/global-error-handling-aspnetcore/08-logglobalcustomhandlermiddleware/ Tue, 26 Jun 2018 10:45:51 +0000 https://code-maze.com/wp-content/uploads/2018/06/08-LogGlobalCustomHandlerMiddleware.png 3665 3666 0 0 48-DeleteOwner folder structure https://code-maze.com/react-netcore-delete-request/48-deleteowner-folder-structure/ Thu, 28 Jun 2018 06:53:25 +0000 https://code-maze.com/wp-content/uploads/2018/06/48-DeleteOwner-folder-structure.png 3716 3721 0 0 49-DeleteOwner form https://code-maze.com/react-netcore-delete-request/49-deleteowner-form/ Thu, 28 Jun 2018 06:53:26 +0000 https://code-maze.com/wp-content/uploads/2018/06/49-DeleteOwner-form.png 3717 3721 0 0 50-DeleteOwner form with the data https://code-maze.com/react-netcore-delete-request/50-deleteowner-form-with-the-data/ Thu, 28 Jun 2018 06:53:27 +0000 https://code-maze.com/wp-content/uploads/2018/06/50-DeleteOwner-form-with-the-data.png 3718 3721 0 0 01-BadRequestActionFilter https://code-maze.com/action-filters-aspnetcore/01-badrequestactionfilter/ Sun, 01 Jul 2018 09:54:42 +0000 https://code-maze.com/wp-content/uploads/2018/07/01-BadRequestActionFilter.png 3737 3738 0 0 Order of Infocation in Action Filters https://code-maze.com/action-filters-aspnetcore/order-of-infocation-in-action-filters/ Sun, 01 Jul 2018 10:52:50 +0000 https://code-maze.com/wp-content/uploads/2018/07/Order-of-Infocation-in-Action-Filters.png 3741 3738 0 0 angular best practices https://code-maze.com/angular-best-practices/angular-best-practices-2/ Sun, 01 Jul 2018 14:10:28 +0000 https://code-maze.com/wp-content/uploads/2018/07/angular-best-practices.png 3747 3243 0 0 AspNetCoreBestPractices https://code-maze.com/netcorebestpractices7/ Sun, 01 Jul 2018 15:08:21 +0000 https://code-maze.com/wp-content/uploads/2018/07/NetCoreBestPractices7.png 3754 0 0 0 angular best practices 2 https://code-maze.com/angular-best-practices/angular-best-practices-2-2/ Sun, 01 Jul 2018 15:50:59 +0000 https://code-maze.com/wp-content/uploads/2018/07/angular-best-practices-2.png 3755 3243 0 0 AngularBestPractices https://code-maze.com/angularbestpractices/ Sun, 01 Jul 2018 16:01:08 +0000 https://code-maze.com/wp-content/uploads/2018/07/AngularBestPractices.png 3759 0 0 0 NetCoreBestPracticesMini https://code-maze.com/netcorebestpracticesmini/ Sun, 01 Jul 2018 20:23:55 +0000 https://code-maze.com/wp-content/uploads/2018/07/NetCoreBestPracticesMini.png 3762 0 0 0 ravindranath https://code-maze.com/ravindranath/ Mon, 02 Jul 2018 21:25:44 +0000 https://code-maze.com/wp-content/uploads/2018/07/ravindranath.jpg 3766 0 0 0 51-Delete action successfully https://code-maze.com/51-delete-action-successfully/ Tue, 03 Jul 2018 17:38:31 +0000 https://code-maze.com/wp-content/uploads/2018/07/51-Delete-action-successfully.png 3781 0 0 0 NetCoreBestPractices400px https://code-maze.com/netcorebestpractices400px/ Tue, 03 Jul 2018 17:45:19 +0000 https://code-maze.com/wp-content/uploads/2018/07/NetCoreBestPractices400px.png 3784 5557 0 0 AngularBestPractices400px https://code-maze.com/angularbestpractices400px/ Tue, 03 Jul 2018 17:55:12 +0000 https://code-maze.com/wp-content/uploads/2018/07/AngularBestPractices400px.png 3786 0 0 0 AngularBestPracticesMini https://code-maze.com/angularbestpracticesmini/ Tue, 03 Jul 2018 21:09:21 +0000 https://code-maze.com/wp-content/uploads/2018/07/AngularBestPracticesMini.png 3814 0 0 0 CIDefinitiveGuideMini https://code-maze.com/cidefinitiveguidemini/ Thu, 05 Jul 2018 18:28:55 +0000 https://code-maze.com/wp-content/uploads/2018/07/CIDefinitiveGuideMini.png 3822 0 0 0 CIDefinitiveGuide400px https://code-maze.com/cidefinitiveguide400px/ Thu, 05 Jul 2018 18:33:20 +0000 https://code-maze.com/wp-content/uploads/2018/07/CIDefinitiveGuide400px.png 3823 0 0 0 42-beginner-user-details https://code-maze.com/net-core-web-development-part11/42-beginner-user-details/ Sat, 07 Jul 2018 06:42:45 +0000 https://code-maze.com/wp-content/uploads/2018/07/42-beginner-user-details.png 3838 1936 0 0 Add New TEST Project https://code-maze.com/unit-testing-aspnetcore-web-api/add-new-test-project/ Sat, 07 Jul 2018 11:15:09 +0000 https://code-maze.com/wp-content/uploads/2018/07/Add-New-TEST-Project.png 3840 3847 0 0 add reference https://code-maze.com/unit-testing-aspnetcore-web-api/add-reference/ Sat, 07 Jul 2018 11:15:13 +0000 https://code-maze.com/wp-content/uploads/2018/07/add-reference.png 3841 3847 0 0 add-reference-webapi https://code-maze.com/add-reference-webapi/ Sat, 07 Jul 2018 11:15:16 +0000 https://code-maze.com/wp-content/uploads/2018/07/add-reference-webapi.png 3842 0 0 0 add-webapi-reference https://code-maze.com/unit-testing-aspnetcore-web-api/add-webapi-reference/ Sat, 07 Jul 2018 11:15:18 +0000 https://code-maze.com/wp-content/uploads/2018/07/add-webapi-reference.png 3843 3847 0 0 aspnet-core-new-project https://code-maze.com/unit-testing-aspnetcore-web-api/aspnet-core-new-project/ Sat, 07 Jul 2018 11:15:20 +0000 https://code-maze.com/wp-content/uploads/2018/07/aspnet-core-new-project.png 3844 3847 0 0 New ASP.NET Core Web Application - web-api https://code-maze.com/new-asp-net-core-web-application-web-api/ Sat, 07 Jul 2018 11:15:24 +0000 https://code-maze.com/wp-content/uploads/2018/07/New-ASP.NET-Core-Web-Application-web-api.png 3845 0 0 0 show-me-the-source-yoda https://code-maze.com/unit-testing-aspnetcore-web-api/show-me-the-source-yoda/ Sat, 07 Jul 2018 11:15:26 +0000 https://code-maze.com/wp-content/uploads/2018/07/show-me-the-source-yoda.jpg 3846 3847 0 0 jwt2-featured https://code-maze.com/authentication-aspnetcore-jwt-2/jwt2-featured/ Sat, 07 Jul 2018 13:33:14 +0000 https://code-maze.com/wp-content/uploads/2018/05/jwt2-featured.png 3863 2753 0 0 jwt1-featured https://code-maze.com/authentication-aspnetcore-jwt-1/jwt1-featured/ Sat, 07 Jul 2018 13:33:58 +0000 https://code-maze.com/wp-content/uploads/2018/04/jwt1-featured.png 3864 2370 0 0 LogoMini https://code-maze.com/logomini/ Wed, 11 Jul 2018 21:43:37 +0000 https://code-maze.com/wp-content/uploads/2018/07/LogoMini.png 3903 0 0 0 High-level CI Flow https://code-maze.com/ci-jenkins-docker/highlevelflow/ Sat, 14 Jul 2018 02:11:28 +0000 https://code-maze.com/wp-content/uploads/2018/07/HighLevelFlow.png 3929 3834 0 0 Initial Setup Wizard https://code-maze.com/ci-jenkins-docker/jenkinsscreen1/ Sat, 14 Jul 2018 17:26:35 +0000 https://code-maze.com/wp-content/uploads/2018/07/JenkinsScreen1.png 3944 3834 0 0 Initial Admin Password https://code-maze.com/ci-jenkins-docker/jenkinspassword/ Sat, 14 Jul 2018 17:31:19 +0000 https://code-maze.com/wp-content/uploads/2018/07/JenkinsPassword.png 3945 3834 0 0 slave.py https://code-maze.com/ci-jenkins-docker/slave_python/ Sat, 14 Jul 2018 19:45:17 +0000 https://code-maze.com/wp-content/uploads/2018/07/slave_python.png 3956 3834 0 0 docker-compose startup logs https://code-maze.com/ci-jenkins-docker/startuplogs/ Sat, 14 Jul 2018 23:34:04 +0000 https://code-maze.com/wp-content/uploads/2018/07/startuplogs.png 3963 3834 0 0 Agent Connected https://code-maze.com/ci-jenkins-docker/startuplogs2/ Sat, 14 Jul 2018 23:37:45 +0000 https://code-maze.com/wp-content/uploads/2018/07/startuplogs2.png 3964 3834 0 0 Jenkins Logon https://code-maze.com/ci-jenkins-docker/jenkinsscreen1-2/ Sat, 14 Jul 2018 23:40:34 +0000 https://code-maze.com/wp-content/uploads/2018/07/JenkinsScreen1-1.png 3965 3834 0 0 Jenkins Logon https://code-maze.com/ci-jenkins-docker/firstlogin/ Sat, 14 Jul 2018 23:41:09 +0000 https://code-maze.com/wp-content/uploads/2018/07/FirstLogin.png 3966 3834 0 0 Jenkins dashboard https://code-maze.com/ci-jenkins-docker/jenkinsdashboard/ Sat, 14 Jul 2018 23:43:43 +0000 https://code-maze.com/wp-content/uploads/2018/07/Jenkinsdashboard.png 3968 3834 0 0 profile_150_150 https://code-maze.com/profile_150_150/ Sun, 15 Jul 2018 12:40:30 +0000 https://code-maze.com/wp-content/uploads/2018/07/profile_150_150.png 3973 0 0 0 unit-testing-aspnetcore-webapi https://code-maze.com/unit-testing-aspnetcore-web-api/unit-testing-aspnetcore-webapi/ Sun, 15 Jul 2018 12:57:53 +0000 https://code-maze.com/wp-content/uploads/2018/07/unit-testing-aspnetcore-webapi.png 3974 3847 0 0 Create Pipeline Job https://code-maze.com/ci-jenkins-docker/createjob/ Sun, 15 Jul 2018 22:43:34 +0000 https://code-maze.com/wp-content/uploads/2018/07/CreateJob.gif 3975 3834 0 0 Job Config https://code-maze.com/ci-jenkins-docker/jobconfig/ Sun, 15 Jul 2018 22:52:48 +0000 https://code-maze.com/wp-content/uploads/2018/07/JobConfig.gif 3977 3834 0 0 Build Steps https://code-maze.com/ci-jenkins-docker/buildsteps/ Sun, 15 Jul 2018 23:33:11 +0000 https://code-maze.com/wp-content/uploads/2018/07/BuildSteps.png 3979 3834 0 0 Pipline Syntax Generator https://code-maze.com/ci-jenkins-docker/pipelinesyntax/ Mon, 16 Jul 2018 00:52:32 +0000 https://code-maze.com/wp-content/uploads/2018/07/Pipelinesyntax.gif 3983 3834 0 0 Build Progress https://code-maze.com/ci-jenkins-docker/inprogress/ Mon, 16 Jul 2018 01:35:49 +0000 https://code-maze.com/wp-content/uploads/2018/07/InProgress.png 3986 3834 0 0 Stage Logs https://code-maze.com/ci-jenkins-docker/logs/ Mon, 16 Jul 2018 01:35:51 +0000 https://code-maze.com/wp-content/uploads/2018/07/Logs.png 3987 3834 0 0 Successful Build https://code-maze.com/ci-jenkins-docker/stageview/ Mon, 16 Jul 2018 01:35:53 +0000 https://code-maze.com/wp-content/uploads/2018/07/StageView.png 3988 3834 0 0 Build Status https://code-maze.com/ci-jenkins-docker/withtests/ Mon, 16 Jul 2018 02:10:02 +0000 https://code-maze.com/wp-content/uploads/2018/07/WIthTests.png 3992 3834 0 0 Build Status https://code-maze.com/ci-jenkins-docker/stageview-2/ Mon, 16 Jul 2018 02:10:03 +0000 https://code-maze.com/wp-content/uploads/2018/07/StageView-1.png 3993 3834 0 0 Test Results https://code-maze.com/ci-jenkins-docker/testresults-2/ Mon, 16 Jul 2018 02:10:38 +0000 https://code-maze.com/wp-content/uploads/2018/07/TestResults.png 3994 3834 0 0 Vue Init https://code-maze.com/creating-vuejs-project/1-1/ Tue, 17 Jul 2018 19:37:28 +0000 https://code-maze.com/wp-content/uploads/2018/07/1-1.png 4019 4018 0 0 Project Structure https://code-maze.com/creating-vuejs-project/1-2/ Tue, 17 Jul 2018 19:39:50 +0000 https://code-maze.com/wp-content/uploads/2018/07/1-2.png 4020 4018 0 0 Wellcome to Your Vue.js App https://code-maze.com/creating-vuejs-project/1-3/ Tue, 17 Jul 2018 19:46:59 +0000 https://code-maze.com/wp-content/uploads/2018/07/1-3.png 4021 4018 0 0 Welcome to account-owner application https://code-maze.com/vuejs-routing-navigation/2-1/ Wed, 18 Jul 2018 19:55:23 +0000 https://code-maze.com/wp-content/uploads/2018/07/2-1.png 4052 4048 0 0 404 Not Found https://code-maze.com/vuejs-routing-navigation/2-2/ Wed, 18 Jul 2018 19:56:32 +0000 https://code-maze.com/wp-content/uploads/2018/07/2-2.png 4053 4048 0 0 Configure Project https://code-maze.com/ci-jenkins-docker/configure_project/ Fri, 20 Jul 2018 11:23:25 +0000 https://code-maze.com/wp-content/uploads/2018/07/configure_project.gif 4069 3834 0 0 New Item https://code-maze.com/ci-jenkins-docker/createproject/ Fri, 20 Jul 2018 11:23:27 +0000 https://code-maze.com/wp-content/uploads/2018/07/createproject.gif 4070 3834 0 0 Ravindranath Barathy https://code-maze.com/ravindranath-barathy/ Fri, 20 Jul 2018 11:52:41 +0000 https://code-maze.com/wp-content/uploads/2018/07/Ravindranath-Barathy.png 4073 0 0 0 Fetching data from the backend https://code-maze.com/vuejs-axios-http-environment-files/3-1/ Fri, 20 Jul 2018 23:03:12 +0000 https://code-maze.com/wp-content/uploads/2018/07/3-1.png 4080 4078 0 0 global error handling https://code-maze.com/global-error-handling-aspnetcore/global-error-handling/ Sat, 21 Jul 2018 18:46:47 +0000 https://code-maze.com/wp-content/uploads/2018/06/global-error-handling.png 4088 3666 0 0 action filters https://code-maze.com/action-filters-aspnetcore/action-filters/ Sat, 21 Jul 2018 18:55:20 +0000 https://code-maze.com/wp-content/uploads/2018/07/action-filters.png 4091 3738 0 0 Owner List Component Page https://code-maze.com/vuejs-creating-components/4-1/ Sat, 21 Jul 2018 19:19:00 +0000 https://code-maze.com/wp-content/uploads/2018/07/4-1.png 4094 4090 0 0 Owner List Table https://code-maze.com/vuejs-creating-components/4-2/ Sat, 21 Jul 2018 19:19:52 +0000 https://code-maze.com/wp-content/uploads/2018/07/4-2.png 4095 4090 0 0 Logo za Vuet series https://code-maze.com/logo-za-vuet-series/ Sun, 22 Jul 2018 15:23:02 +0000 https://code-maze.com/wp-content/uploads/2018/07/Logo-za-Vuet-series.png 4111 0 0 0 Owner List Table https://code-maze.com/vuejs-props-events/5-1/ Sun, 22 Jul 2018 21:37:23 +0000 https://code-maze.com/wp-content/uploads/2018/07/5-1.png 4121 4119 0 0 On Owner Clicked Event https://code-maze.com/vuejs-props-events/5-2/ Sun, 22 Jul 2018 21:38:46 +0000 https://code-maze.com/wp-content/uploads/2018/07/5-2.png 4122 4119 0 0 All Three Events are Working https://code-maze.com/vuejs-props-events/5-3/ Sun, 22 Jul 2018 21:39:20 +0000 https://code-maze.com/wp-content/uploads/2018/07/5-3.png 4123 4119 0 0 emna https://code-maze.com/profile-150px/ Mon, 23 Jul 2018 20:33:40 +0000 https://code-maze.com/wp-content/uploads/2018/07/profile-150px.jpg 4130 0 0 0 01-NewProjectPage https://code-maze.com/csharp-basics-ide-introduction/01-newprojectpage/ Tue, 24 Jul 2018 12:01:27 +0000 https://code-maze.com/wp-content/uploads/2018/07/01-NewProjectPage.png 4138 4142 0 0 02-CreatingConsoleProject https://code-maze.com/csharp-basics-ide-introduction/02-creatingconsoleproject/ Tue, 24 Jul 2018 12:01:28 +0000 https://code-maze.com/wp-content/uploads/2018/07/02-CreatingConsoleProject.png 4139 4142 0 0 03.1-Brakepoint https://code-maze.com/csharp-basics-ide-introduction/03-1-brakepoint/ Tue, 24 Jul 2018 12:01:31 +0000 https://code-maze.com/wp-content/uploads/2018/07/03.1-Brakepoint.png 4140 4142 0 0 03.2-Watch_window https://code-maze.com/csharp-basics-ide-introduction/03-2-watch_window/ Tue, 24 Jul 2018 12:01:31 +0000 https://code-maze.com/wp-content/uploads/2018/07/03.2-Watch_window.png 4141 4142 0 0 03-Program-cs-file https://code-maze.com/csharp-basics-ide-introduction/03-program-cs-file/ Tue, 24 Jul 2018 12:15:21 +0000 https://code-maze.com/wp-content/uploads/2018/07/03-Program-cs-file.png 4143 4142 0 0 04.2-TableDecimalNumbers https://code-maze.com/csharp-basics-data-types-variables/04-2-tabledecimalnumbers/ Tue, 24 Jul 2018 18:03:08 +0000 https://code-maze.com/wp-content/uploads/2018/07/04.2-TableDecimalNumbers.png 4152 4150 0 0 04.3-TableCharBool https://code-maze.com/csharp-basics-data-types-variables/04-3-tablecharbool/ Tue, 24 Jul 2018 18:03:09 +0000 https://code-maze.com/wp-content/uploads/2018/07/04.3-TableCharBool.png 4153 4150 0 0 04-ValueType https://code-maze.com/csharp-basics-data-types-variables/04-valuetype/ Tue, 24 Jul 2018 18:03:10 +0000 https://code-maze.com/wp-content/uploads/2018/07/04-ValueType.png 4154 4150 0 0 05-ReferenceType https://code-maze.com/csharp-basics-data-types-variables/05-referencetype/ Tue, 24 Jul 2018 18:03:11 +0000 https://code-maze.com/wp-content/uploads/2018/07/05-ReferenceType.png 4155 4150 0 0 04.1-WholeNumbersTable https://code-maze.com/csharp-basics-data-types-variables/04-1-wholenumberstable/ Tue, 24 Jul 2018 18:06:20 +0000 https://code-maze.com/wp-content/uploads/2018/07/04.1-WholeNumbersTable.png 4156 4150 0 0 6-1 https://code-maze.com/vuejs-details-deleting/6-1/ Tue, 24 Jul 2018 21:47:48 +0000 https://code-maze.com/wp-content/uploads/2018/07/6-1.png 4170 4159 0 0 6-2 https://code-maze.com/vuejs-details-deleting/6-2/ Tue, 24 Jul 2018 21:48:02 +0000 https://code-maze.com/wp-content/uploads/2018/07/6-2.png 4171 4159 0 0 6-3 https://code-maze.com/vuejs-details-deleting/6-3/ Tue, 24 Jul 2018 21:48:38 +0000 https://code-maze.com/wp-content/uploads/2018/07/6-3.png 4172 4159 0 0 6-4 https://code-maze.com/vuejs-details-deleting/6-4/ Tue, 24 Jul 2018 21:48:55 +0000 https://code-maze.com/wp-content/uploads/2018/07/6-4.png 4173 4159 0 0 6-5 https://code-maze.com/vuejs-details-deleting/6-5/ Tue, 24 Jul 2018 21:49:24 +0000 https://code-maze.com/wp-content/uploads/2018/07/6-5.png 4174 4159 0 0 6-6 https://code-maze.com/vuejs-details-deleting/6-6/ Tue, 24 Jul 2018 21:49:41 +0000 https://code-maze.com/wp-content/uploads/2018/07/6-6.png 4175 4159 0 0 06.1-Operators Table https://code-maze.com/csharp-basics-operators/06-1-operators-table/ Wed, 25 Jul 2018 08:40:49 +0000 https://code-maze.com/wp-content/uploads/2018/07/06.1-Operators-Table.png 4187 4192 0 0 06-OperatorsExample https://code-maze.com/csharp-basics-operators/06-operatorsexample/ Wed, 25 Jul 2018 08:40:50 +0000 https://code-maze.com/wp-content/uploads/2018/07/06-OperatorsExample.png 4188 4192 0 0 07-OperatorsEqualityRefExample https://code-maze.com/csharp-basics-operators/07-operatorsequalityrefexample/ Wed, 25 Jul 2018 08:40:52 +0000 https://code-maze.com/wp-content/uploads/2018/07/07-OperatorsEqualityRefExample.png 4189 4192 0 0 08-DecrementPrefixPostfix https://code-maze.com/csharp-basics-operators/08-decrementprefixpostfix/ Wed, 25 Jul 2018 08:40:53 +0000 https://code-maze.com/wp-content/uploads/2018/07/08-DecrementPrefixPostfix.png 4190 4192 0 0 09-IncrementPrefixPostfix https://code-maze.com/csharp-basics-operators/09-incrementprefixpostfix/ Wed, 25 Jul 2018 08:40:54 +0000 https://code-maze.com/wp-content/uploads/2018/07/09-IncrementPrefixPostfix.png 4191 4192 0 0 docker jenkins https://code-maze.com/ci-jenkins-docker/docker-part8/ Wed, 25 Jul 2018 21:44:23 +0000 https://code-maze.com/wp-content/uploads/2018/07/docker-part8.png 4205 3834 0 0 top docker monitoring tools https://code-maze.com/top-docker-monitoring-tools/docker-part9/ Wed, 25 Jul 2018 21:52:14 +0000 https://code-maze.com/wp-content/uploads/2018/07/docker-part9.png 4207 4136 0 0 back to basics csharp https://code-maze.com/csharp-basics-ide-introduction/back-to-basics-csharp/ Wed, 25 Jul 2018 22:11:55 +0000 https://code-maze.com/wp-content/uploads/2018/07/back-to-basics-csharp.png 4209 4142 0 0 docker-whale-home-logo https://code-maze.com/top-docker-monitoring-tools/docker-whale-home-logo/ Thu, 26 Jul 2018 06:29:09 +0000 https://code-maze.com/wp-content/uploads/2018/07/docker-whale-home-logo.png 4213 4136 0 0 cadvisor-cpu-usage https://code-maze.com/top-docker-monitoring-tools/cadvisor-cpu-usage/ Thu, 26 Jul 2018 06:33:03 +0000 https://code-maze.com/wp-content/uploads/2018/07/cadvisor-cpu-usage.png 4216 4136 0 0 10-Implicit-Conversion https://code-maze.com/csharp-basics-type-conversion/10-implicit-conversion/ Thu, 26 Jul 2018 07:08:07 +0000 https://code-maze.com/wp-content/uploads/2018/07/10-Implicit-Conversion.png 4220 4219 0 0 11-Missing_cast https://code-maze.com/csharp-basics-type-conversion/11-missing_cast/ Thu, 26 Jul 2018 07:08:08 +0000 https://code-maze.com/wp-content/uploads/2018/07/11-Missing_cast.png 4221 4219 0 0 12-Success_cast https://code-maze.com/csharp-basics-type-conversion/12-success_cast/ Thu, 26 Jul 2018 07:08:09 +0000 https://code-maze.com/wp-content/uploads/2018/07/12-Success_cast.png 4222 4219 0 0 13-ToString_conversion https://code-maze.com/csharp-basics-type-conversion/13-tostring_conversion/ Thu, 26 Jul 2018 07:08:10 +0000 https://code-maze.com/wp-content/uploads/2018/07/13-ToString_conversion.png 4223 4219 0 0 cadvisor-cpu-usage https://code-maze.com/top-docker-monitoring-tools/cadvisor-cpu-usage-2/ Thu, 26 Jul 2018 07:27:40 +0000 https://code-maze.com/wp-content/uploads/2018/07/cadvisor-cpu-usage-1.png 4228 4136 0 0 14-SumGenerator_result https://code-maze.com/csharp-basics-input-output/14-sumgenerator_result/ Thu, 26 Jul 2018 10:27:58 +0000 https://code-maze.com/wp-content/uploads/2018/07/14-SumGenerator_result.png 4233 4226 0 0 15-Name_LastName_Separation https://code-maze.com/csharp-basics-string-methods/15-name_lastname_separation/ Thu, 26 Jul 2018 19:27:19 +0000 https://code-maze.com/wp-content/uploads/2018/07/15-Name_LastName_Separation.png 4239 4241 0 0 16-First-Last-Word-Removed https://code-maze.com/csharp-basics-string-methods/16-first-last-word-removed/ Thu, 26 Jul 2018 19:27:20 +0000 https://code-maze.com/wp-content/uploads/2018/07/16-First-Last-Word-Removed.png 4240 4241 0 0 17-Greater_Number https://code-maze.com/csharp-basics-conditions/17-greater_number/ Fri, 27 Jul 2018 06:15:38 +0000 https://code-maze.com/wp-content/uploads/2018/07/17-Greater_Number.png 4245 4244 0 0 18-Else_If_Example https://code-maze.com/csharp-basics-conditions/18-else_if_example/ Fri, 27 Jul 2018 06:15:39 +0000 https://code-maze.com/wp-content/uploads/2018/07/18-Else_If_Example.png 4246 4244 0 0 19-Nested_Conditions https://code-maze.com/csharp-basics-conditions/19-nested_conditions/ Fri, 27 Jul 2018 06:15:39 +0000 https://code-maze.com/wp-content/uploads/2018/07/19-Nested_Conditions.png 4247 4244 0 0 20-Multiple_Branching https://code-maze.com/csharp-basics-conditions/20-multiple_branching/ Fri, 27 Jul 2018 06:15:40 +0000 https://code-maze.com/wp-content/uploads/2018/07/20-Multiple_Branching.png 4248 4244 0 0 21-WhileExample https://code-maze.com/csharp-basics-loops/21-whileexample/ Fri, 27 Jul 2018 11:48:38 +0000 https://code-maze.com/wp-content/uploads/2018/07/21-WhileExample.png 4253 4258 0 0 22-ForExample https://code-maze.com/csharp-basics-loops/22-forexample/ Fri, 27 Jul 2018 11:48:40 +0000 https://code-maze.com/wp-content/uploads/2018/07/22-ForExample.png 4254 4258 0 0 23-ForExample2 https://code-maze.com/csharp-basics-loops/23-forexample2/ Fri, 27 Jul 2018 11:48:40 +0000 https://code-maze.com/wp-content/uploads/2018/07/23-ForExample2.png 4255 4258 0 0 24-Do_while_Example1 https://code-maze.com/csharp-basics-loops/24-do_while_example1/ Fri, 27 Jul 2018 11:48:41 +0000 https://code-maze.com/wp-content/uploads/2018/07/24-Do_while_Example1.png 4256 4258 0 0 25-Do_while_Example2 https://code-maze.com/csharp-basics-loops/25-do_while_example2/ Fri, 27 Jul 2018 11:48:43 +0000 https://code-maze.com/wp-content/uploads/2018/07/25-Do_while_Example2.png 4257 4258 0 0 7-1 https://code-maze.com/vuejs-create-and-update/7-1/ Fri, 27 Jul 2018 21:43:40 +0000 https://code-maze.com/wp-content/uploads/2018/07/7-1.png 4267 4262 0 0 7-2 https://code-maze.com/vuejs-create-and-update/7-2/ Fri, 27 Jul 2018 21:44:00 +0000 https://code-maze.com/wp-content/uploads/2018/07/7-2.png 4268 4262 0 0 7-3 https://code-maze.com/vuejs-create-and-update/7-3/ Fri, 27 Jul 2018 21:44:24 +0000 https://code-maze.com/wp-content/uploads/2018/07/7-3.png 4269 4262 0 0 7-4 https://code-maze.com/vuejs-create-and-update/7-4/ Fri, 27 Jul 2018 21:44:53 +0000 https://code-maze.com/wp-content/uploads/2018/07/7-4.png 4270 4262 0 0 26-Unhandled_Exception https://code-maze.com/csharp-basics-handling-exceptions/26-unhandled_exception/ Sat, 28 Jul 2018 14:29:07 +0000 https://code-maze.com/wp-content/uploads/2018/07/26-Unhandled_Exception.png 4279 4282 0 0 27-Handled_Exception https://code-maze.com/csharp-basics-handling-exceptions/27-handled_exception/ Sat, 28 Jul 2018 14:29:09 +0000 https://code-maze.com/wp-content/uploads/2018/07/27-Handled_Exception.png 4280 4282 0 0 28-Invalid_Exception https://code-maze.com/csharp-basics-handling-exceptions/28-invalid_exception/ Sat, 28 Jul 2018 14:29:10 +0000 https://code-maze.com/wp-content/uploads/2018/07/28-Invalid_Exception.png 4281 4282 0 0 Ravindranath Barathy 2 https://code-maze.com/ravindranath-barathy-2/ Mon, 30 Jul 2018 05:03:24 +0000 https://code-maze.com/wp-content/uploads/2018/07/Ravindranath-Barathy-2.png 4309 0 0 0 29-Method_signitures https://code-maze.com/csharp-basics-methods/29-method_signitures/ Mon, 30 Jul 2018 05:52:00 +0000 https://code-maze.com/wp-content/uploads/2018/07/29-Method_signitures.png 4311 4313 0 0 30-Method_Example1 https://code-maze.com/csharp-basics-methods/30-method_example1/ Mon, 30 Jul 2018 05:52:01 +0000 https://code-maze.com/wp-content/uploads/2018/07/30-Method_Example1.png 4312 4313 0 0 31-RefOut-SendingValueParamsWithRefOut https://code-maze.com/cshrap-basics-ref-out-keywords/31-refout-sendingvalueparamswithrefout/ Tue, 31 Jul 2018 13:15:57 +0000 https://code-maze.com/wp-content/uploads/2018/07/31-RefOut-SendingValueParamsWithRefOut.png 4320 4319 0 0 32-RefOut-SendingRefParamsWithRefOut https://code-maze.com/cshrap-basics-ref-out-keywords/32-refout-sendingrefparamswithrefout/ Tue, 31 Jul 2018 13:15:58 +0000 https://code-maze.com/wp-content/uploads/2018/07/32-RefOut-SendingRefParamsWithRefOut.png 4321 4319 0 0 31-RefOut-SendingValueParams https://code-maze.com/cshrap-basics-ref-out-keywords/31-refout-sendingvalueparams/ Tue, 31 Jul 2018 13:30:19 +0000 https://code-maze.com/wp-content/uploads/2018/07/31-RefOut-SendingValueParams.png 4322 4319 0 0 33-Recursion https://code-maze.com/csharp-basics-recursion/33-recursion/ Wed, 01 Aug 2018 06:10:22 +0000 https://code-maze.com/wp-content/uploads/2018/08/33-Recursion.png 4332 4334 0 0 34-RecursionGraph https://code-maze.com/csharp-basics-recursion/34-recursiongraph/ Wed, 01 Aug 2018 06:10:23 +0000 https://code-maze.com/wp-content/uploads/2018/08/34-RecursionGraph.png 4333 4334 0 0 35-ArraysExample1 https://code-maze.com/csharp-basics-arrays/35-arraysexample1/ Thu, 02 Aug 2018 05:50:16 +0000 https://code-maze.com/wp-content/uploads/2018/08/35-ArraysExample1.png 4337 4339 0 0 36-TwoDimensionalArrays https://code-maze.com/csharp-basics-arrays/36-twodimensionalarrays/ Thu, 02 Aug 2018 05:50:17 +0000 https://code-maze.com/wp-content/uploads/2018/08/36-TwoDimensionalArrays.png 4338 4339 0 0 10.1-Implicit-Conversion2 https://code-maze.com/csharp-basics-type-conversion/10-1-implicit-conversion2/ Sat, 04 Aug 2018 06:48:01 +0000 https://code-maze.com/wp-content/uploads/2018/08/10.1-Implicit-Conversion2.png 4356 4219 0 0 properties-for-dll-file https://code-maze.com/create-pdf-dotnetcore/properties-for-dll-file/ Thu, 06 Sep 2018 07:42:36 +0000 https://code-maze.com/wp-content/uploads/2018/09/properties-for-dll-file.png 4430 3498 0 0 published https://code-maze.com/create-pdf-dotnetcore/published/ Thu, 06 Sep 2018 07:49:34 +0000 https://code-maze.com/wp-content/uploads/2018/09/published.png 4431 3498 0 0 01-Adding_new_item https://code-maze.com/csharp-classes-constructors/01-adding_new_item/ Sun, 09 Sep 2018 12:34:59 +0000 https://code-maze.com/wp-content/uploads/2018/09/01-Adding_new_item.png 4450 4449 0 0 02-Adding_new_class https://code-maze.com/csharp-classes-constructors/02-adding_new_class/ Sun, 09 Sep 2018 12:35:03 +0000 https://code-maze.com/wp-content/uploads/2018/09/02-Adding_new_class.png 4451 4449 0 0 03-Read_only_prop_error https://code-maze.com/csharp-properties/03-read_only_prop_error/ Sun, 09 Sep 2018 17:13:46 +0000 https://code-maze.com/wp-content/uploads/2018/09/03-Read_only_prop_error.png 4454 4458 0 0 04-Write_only_prop_error https://code-maze.com/csharp-properties/04-write_only_prop_error/ Sun, 09 Sep 2018 17:13:47 +0000 https://code-maze.com/wp-content/uploads/2018/09/04-Write_only_prop_error.png 4455 4458 0 0 05-Private_set_accessor_error https://code-maze.com/csharp-properties/05-private_set_accessor_error/ Sun, 09 Sep 2018 17:13:49 +0000 https://code-maze.com/wp-content/uploads/2018/09/05-Private_set_accessor_error.png 4456 4458 0 0 06-Auto-property_suggestion https://code-maze.com/csharp-properties/06-auto-property_suggestion/ Sun, 09 Sep 2018 17:13:50 +0000 https://code-maze.com/wp-content/uploads/2018/09/06-Auto-property_suggestion.png 4457 4458 0 0 csharp intermediate https://code-maze.com/csharp-intermediate-tutorial-oop/csharp-intermediate/ Sun, 09 Sep 2018 18:07:41 +0000 https://code-maze.com/wp-content/uploads/2018/09/csharp-intermediate.png 4461 4439 0 0 Migrations https://code-maze.com/net-core-web-api-ef-core-code-first/migrations/ Mon, 10 Sep 2018 06:51:01 +0000 https://code-maze.com/wp-content/uploads/2018/09/Migrations.jpg 4486 4467 0 0 DB_Table https://code-maze.com/net-core-web-api-ef-core-code-first/db_table/ Mon, 10 Sep 2018 07:00:28 +0000 https://code-maze.com/wp-content/uploads/2018/09/DB_Table.jpg 4489 4467 0 0 07-Constant https://code-maze.com/csharp-static-members-constants-extension-methods/07-constant/ Mon, 10 Sep 2018 08:54:59 +0000 https://code-maze.com/wp-content/uploads/2018/09/07-Constant.png 4493 4495 0 0 08-ExtensionMethod https://code-maze.com/csharp-static-members-constants-extension-methods/08-extensionmethod/ Mon, 10 Sep 2018 08:55:00 +0000 https://code-maze.com/wp-content/uploads/2018/09/08-ExtensionMethod.png 4494 4495 0 0 09-Nullable_Error https://code-maze.com/csharp-anonymous-nullable-types/09-nullable_error/ Mon, 10 Sep 2018 12:07:05 +0000 https://code-maze.com/wp-content/uploads/2018/09/09-Nullable_Error.png 4499 4500 0 0 Screenshot_20180910-210511__01 https://code-maze.com/net-core-web-api-ef-core-code-first/screenshot_20180910-210511__01/ Mon, 10 Sep 2018 15:49:31 +0000 https://code-maze.com/wp-content/uploads/2018/09/Screenshot_20180910-210511__01.jpg 4507 4467 0 0 Screenshot_20180910-210457__01 https://code-maze.com/net-core-web-api-ef-core-code-first/screenshot_20180910-210457__01/ Mon, 10 Sep 2018 15:49:41 +0000 https://code-maze.com/wp-content/uploads/2018/09/Screenshot_20180910-210457__01.jpg 4508 4467 0 0 Screenshot_20180910-210503__01 https://code-maze.com/net-core-web-api-ef-core-code-first/screenshot_20180910-210503__01/ Mon, 10 Sep 2018 15:49:44 +0000 https://code-maze.com/wp-content/uploads/2018/09/Screenshot_20180910-210503__01.jpg 4509 4467 0 0 Screenshot_20180910-210433__01 https://code-maze.com/net-core-web-api-ef-core-code-first/screenshot_20180910-210433__01/ Mon, 10 Sep 2018 15:49:49 +0000 https://code-maze.com/wp-content/uploads/2018/09/Screenshot_20180910-210433__01.jpg 4510 4467 0 0 Screenshot_20180910-210446__01 https://code-maze.com/net-core-web-api-ef-core-code-first/screenshot_20180910-210446__01/ Mon, 10 Sep 2018 15:49:53 +0000 https://code-maze.com/wp-content/uploads/2018/09/Screenshot_20180910-210446__01.jpg 4511 4467 0 0 POST https://code-maze.com/net-core-web-api-ef-core-code-first/post/ Tue, 11 Sep 2018 04:58:02 +0000 https://code-maze.com/wp-content/uploads/2018/09/POST.jpg 4515 4467 0 0 GET1 https://code-maze.com/net-core-web-api-ef-core-code-first/get1/ Tue, 11 Sep 2018 04:58:03 +0000 https://code-maze.com/wp-content/uploads/2018/09/GET1.jpg 4516 4467 0 0 PUT https://code-maze.com/net-core-web-api-ef-core-code-first/put/ Tue, 11 Sep 2018 04:58:05 +0000 https://code-maze.com/wp-content/uploads/2018/09/PUT.jpg 4517 4467 0 0 GET2 https://code-maze.com/net-core-web-api-ef-core-code-first/get2/ Tue, 11 Sep 2018 04:58:06 +0000 https://code-maze.com/wp-content/uploads/2018/09/GET2.jpg 4518 4467 0 0 SQL https://code-maze.com/net-core-web-api-ef-core-code-first/sql/ Tue, 11 Sep 2018 04:58:07 +0000 https://code-maze.com/wp-content/uploads/2018/09/SQL.jpg 4519 4467 0 0 1 https://code-maze.com/1/ Tue, 11 Sep 2018 12:02:10 +0000 https://code-maze.com/wp-content/uploads/2018/09/1.png 4535 0 0 0 PostmanImage https://code-maze.com/postmanimage/ Tue, 11 Sep 2018 12:14:33 +0000 https://code-maze.com/wp-content/uploads/2018/09/PostmanImage.png 4538 0 0 0 Nuget1 https://code-maze.com/nuget1/ Tue, 11 Sep 2018 12:19:37 +0000 https://code-maze.com/wp-content/uploads/2018/09/Nuget1.png 4539 0 0 0 IdentityServerInstall https://code-maze.com/identityserverinstall/ Tue, 11 Sep 2018 12:29:14 +0000 https://code-maze.com/wp-content/uploads/2018/09/IdentityServerInstall.png 4540 0 0 0 LaunchSettings1 https://code-maze.com/launchsettings1/ Tue, 11 Sep 2018 12:34:02 +0000 https://code-maze.com/wp-content/uploads/2018/09/LaunchSettings1.png 4541 0 0 0 claims_supported https://code-maze.com/claims_supported/ Tue, 11 Sep 2018 12:45:23 +0000 https://code-maze.com/wp-content/uploads/2018/09/claims_supported.png 4542 0 0 0 postmanget https://code-maze.com/postmanget/ Tue, 11 Sep 2018 12:56:44 +0000 https://code-maze.com/wp-content/uploads/2018/09/postmanget.png 4543 0 0 0 QuickStartAdded https://code-maze.com/quickstartadded/ Tue, 11 Sep 2018 13:04:24 +0000 https://code-maze.com/wp-content/uploads/2018/09/QuickStartAdded.png 4544 0 0 0 mmc https://code-maze.com/mmc/ Tue, 11 Sep 2018 13:07:32 +0000 https://code-maze.com/wp-content/uploads/2018/09/mmc.png 4545 0 0 0 SSLGif https://code-maze.com/sslgif/ Tue, 11 Sep 2018 13:08:47 +0000 https://code-maze.com/wp-content/uploads/2018/09/SSLGif.gif 4546 0 0 0 ssl1 https://code-maze.com/ssl1/ Tue, 11 Sep 2018 13:11:27 +0000 https://code-maze.com/wp-content/uploads/2018/09/ssl1.png 4547 0 0 0 ssl1 https://code-maze.com/ssl1-2/ Tue, 11 Sep 2018 13:12:49 +0000 https://code-maze.com/wp-content/uploads/2018/09/ssl1-1.png 4548 0 0 0 ssl2 https://code-maze.com/ssl2/ Tue, 11 Sep 2018 13:13:59 +0000 https://code-maze.com/wp-content/uploads/2018/09/ssl2.png 4549 0 0 0 fin1 https://code-maze.com/fin1/ Tue, 11 Sep 2018 13:19:15 +0000 https://code-maze.com/wp-content/uploads/2018/09/fin1.png 4550 0 0 0 10-Structure_Initialization_Error https://code-maze.com/csharp-structures/10-structure_initialization_error/ Wed, 12 Sep 2018 07:26:45 +0000 https://code-maze.com/wp-content/uploads/2018/09/10-Structure_Initialization_Error.png 4557 4559 0 0 11-Structure_Private_Field_init_error https://code-maze.com/csharp-structures/11-structure_private_field_init_error/ Wed, 12 Sep 2018 07:26:46 +0000 https://code-maze.com/wp-content/uploads/2018/09/11-Structure_Private_Field_init_error.png 4558 4559 0 0 10.1-Table Structures https://code-maze.com/csharp-structures/10-1-table-structures/ Wed, 12 Sep 2018 07:47:39 +0000 https://code-maze.com/wp-content/uploads/2018/09/10.1-Table-Structures.png 4565 4559 0 0 multiple_projects https://code-maze.com/multiple_projects/ Wed, 12 Sep 2018 12:54:23 +0000 https://code-maze.com/wp-content/uploads/2018/09/multiple_projects.png 4572 0 0 0 reultBrowser https://code-maze.com/reultbrowser/ Wed, 12 Sep 2018 13:06:15 +0000 https://code-maze.com/wp-content/uploads/2018/09/reultBrowser.png 4575 0 0 0 AccountResult https://code-maze.com/accountresult/ Wed, 12 Sep 2018 13:12:11 +0000 https://code-maze.com/wp-content/uploads/2018/09/AccountResult.png 4576 0 0 0 SSLAccountOwner https://code-maze.com/sslaccountowner/ Wed, 12 Sep 2018 13:13:53 +0000 https://code-maze.com/wp-content/uploads/2018/09/SSLAccountOwner.png 4577 0 0 0 api-owner https://code-maze.com/api-owner/ Wed, 12 Sep 2018 13:15:30 +0000 https://code-maze.com/wp-content/uploads/2018/09/api-owner.png 4578 0 0 0 environment https://code-maze.com/environment/ Wed, 12 Sep 2018 13:17:04 +0000 https://code-maze.com/wp-content/uploads/2018/09/environment.png 4579 0 0 0 certificateAdded https://code-maze.com/certificateadded/ Wed, 12 Sep 2018 13:29:05 +0000 https://code-maze.com/wp-content/uploads/2018/09/certificateAdded.png 4580 0 0 0 runSSL https://code-maze.com/runssl/ Wed, 12 Sep 2018 13:30:54 +0000 https://code-maze.com/wp-content/uploads/2018/09/runSSL.png 4581 0 0 0 runssl https://code-maze.com/runssl-2/ Wed, 12 Sep 2018 13:32:05 +0000 https://code-maze.com/wp-content/uploads/2018/09/runssl.png 4582 0 0 0 clinetRunning https://code-maze.com/clinetrunning/ Wed, 12 Sep 2018 13:34:09 +0000 https://code-maze.com/wp-content/uploads/2018/09/clinetRunning.png 4583 0 0 0 install oidc https://code-maze.com/install-oidc/ Wed, 12 Sep 2018 13:44:23 +0000 https://code-maze.com/wp-content/uploads/2018/09/install-oidc.png 4587 0 0 0 OpenIDConnect https://code-maze.com/openidconnect/ Wed, 12 Sep 2018 13:53:07 +0000 https://code-maze.com/wp-content/uploads/2018/09/OpenIDConnect.png 4588 0 0 0 compare https://code-maze.com/compare/ Wed, 12 Sep 2018 14:01:15 +0000 https://code-maze.com/wp-content/uploads/2018/09/compare.png 4590 0 0 0 OidcComponent https://code-maze.com/oidccomponent/ Wed, 12 Sep 2018 14:09:30 +0000 https://code-maze.com/wp-content/uploads/2018/09/OidcComponent.png 4591 0 0 0 LoginTOIdp https://code-maze.com/logintoidp/ Wed, 12 Sep 2018 14:13:42 +0000 https://code-maze.com/wp-content/uploads/2018/09/LoginTOIdp.png 4592 0 0 0 Credentials https://code-maze.com/credentials/ Wed, 12 Sep 2018 14:15:42 +0000 https://code-maze.com/wp-content/uploads/2018/09/Credentials.png 4593 0 0 0 Success https://code-maze.com/success/ Wed, 12 Sep 2018 14:16:34 +0000 https://code-maze.com/wp-content/uploads/2018/09/Success.png 4594 0 0 0 token https://code-maze.com/token/ Wed, 12 Sep 2018 14:18:13 +0000 https://code-maze.com/wp-content/uploads/2018/09/token.png 4595 0 0 0 jwtio https://code-maze.com/jwtio/ Wed, 12 Sep 2018 14:19:41 +0000 https://code-maze.com/wp-content/uploads/2018/09/jwtio.png 4596 0 0 0 SignOutTry https://code-maze.com/signouttry/ Wed, 12 Sep 2018 14:43:38 +0000 https://code-maze.com/wp-content/uploads/2018/09/SignOutTry.png 4599 0 0 0 LogoutSuccessful https://code-maze.com/logoutsuccessful/ Wed, 12 Sep 2018 14:45:11 +0000 https://code-maze.com/wp-content/uploads/2018/09/LogoutSuccessful.png 4600 0 0 0 GET_request https://code-maze.com/net-core-web-api-ef-core-code-first/get_request/ Thu, 13 Sep 2018 05:24:52 +0000 https://code-maze.com/wp-content/uploads/2018/09/GET_request.jpg 4605 4467 0 0 GET_request_after_update https://code-maze.com/net-core-web-api-ef-core-code-first/get_request_after_update/ Thu, 13 Sep 2018 05:24:55 +0000 https://code-maze.com/wp-content/uploads/2018/09/GET_request_after_update.jpg 4606 4467 0 0 POST_request https://code-maze.com/net-core-web-api-ef-core-code-first/post_request/ Thu, 13 Sep 2018 05:24:56 +0000 https://code-maze.com/wp-content/uploads/2018/09/POST_request.jpg 4607 4467 0 0 PUT_request https://code-maze.com/net-core-web-api-ef-core-code-first/put_request/ Thu, 13 Sep 2018 05:24:57 +0000 https://code-maze.com/wp-content/uploads/2018/09/PUT_request.jpg 4608 4467 0 0 SQL_Results https://code-maze.com/net-core-web-api-ef-core-code-first/sql_results/ Thu, 13 Sep 2018 05:24:58 +0000 https://code-maze.com/wp-content/uploads/2018/09/SQL_Results.jpg 4609 4467 0 0 12-Hiding_implementation_warning https://code-maze.com/csharp-inheritance/12-hiding_implementation_warning/ Fri, 14 Sep 2018 21:02:15 +0000 https://code-maze.com/wp-content/uploads/2018/09/12-Hiding_implementation_warning.png 4631 4632 0 0 13-Loosely_Coupled_Objects https://code-maze.com/csharp-interfaces/13-loosely_coupled_objects/ Sun, 16 Sep 2018 09:49:24 +0000 https://code-maze.com/wp-content/uploads/2018/09/13-Loosely_Coupled_Objects.png 4649 4648 0 0 launchSettings https://code-maze.com/launchsettings/ Tue, 18 Sep 2018 11:29:53 +0000 https://code-maze.com/wp-content/uploads/2018/09/launchSettings.png 4665 0 0 0 IS4 https://code-maze.com/is4/ Tue, 18 Sep 2018 11:44:46 +0000 https://code-maze.com/wp-content/uploads/2018/09/IS4.png 4666 0 0 0 mmc https://code-maze.com/mmc-2/ Tue, 18 Sep 2018 11:48:13 +0000 https://code-maze.com/wp-content/uploads/2018/09/mmc-1.png 4667 0 0 0 123 https://code-maze.com/123/ Tue, 18 Sep 2018 11:52:01 +0000 https://code-maze.com/wp-content/uploads/2018/09/123.png 4668 0 0 0 IS4New https://code-maze.com/is4new/ Tue, 18 Sep 2018 12:02:33 +0000 https://code-maze.com/wp-content/uploads/2018/09/IS4New.png 4669 0 0 0 TestRun https://code-maze.com/testrun/ Tue, 18 Sep 2018 12:07:28 +0000 https://code-maze.com/wp-content/uploads/2018/09/TestRun.png 4670 0 0 0 Database_With_Seed https://code-maze.com/net-core-web-api-ef-core-code-first/database_with_seed/ Tue, 18 Sep 2018 15:50:05 +0000 https://code-maze.com/wp-content/uploads/2018/09/Database_With_Seed.jpg 4676 4467 0 0 Table_With_Gender https://code-maze.com/net-core-web-api-ef-core-code-first/table_with_gender/ Tue, 18 Sep 2018 15:50:06 +0000 https://code-maze.com/wp-content/uploads/2018/09/Table_With_Gender.jpg 4677 4467 0 0 Table_Without_Gender https://code-maze.com/net-core-web-api-ef-core-code-first/table_without_gender/ Tue, 18 Sep 2018 15:50:08 +0000 https://code-maze.com/wp-content/uploads/2018/09/Table_Without_Gender.jpg 4678 4467 0 0 14-Abstract_instance_error https://code-maze.com/csharp-abstract-classes/14-abstract_instance_error/ Thu, 20 Sep 2018 08:55:22 +0000 https://code-maze.com/wp-content/uploads/2018/09/14-Abstract_instance_error.png 4694 4696 0 0 15-Abstrac_method_error https://code-maze.com/csharp-abstract-classes/15-abstrac_method_error/ Thu, 20 Sep 2018 08:55:24 +0000 https://code-maze.com/wp-content/uploads/2018/09/15-Abstrac_method_error.png 4695 4696 0 0 15.1-Sealed classes error https://code-maze.com/15-1-sealed-classes-error/ Thu, 20 Sep 2018 09:21:55 +0000 https://code-maze.com/wp-content/uploads/2018/09/15.1-Sealed-classes-error.png 4698 0 0 0 15.1-Sealed classes error https://code-maze.com/csharp-abstract-classes/15-1-sealed-classes-error-2/ Thu, 20 Sep 2018 09:22:20 +0000 https://code-maze.com/wp-content/uploads/2018/09/15.1-Sealed-classes-error-1.png 4699 4696 0 0 16-Generic_example https://code-maze.com/csharp-generics/16-generic_example/ Thu, 20 Sep 2018 09:40:21 +0000 https://code-maze.com/wp-content/uploads/2018/09/16-Generic_example.png 4708 4710 0 0 17-Generic_example_classes https://code-maze.com/csharp-generics/17-generic_example_classes/ Thu, 20 Sep 2018 09:40:21 +0000 https://code-maze.com/wp-content/uploads/2018/09/17-Generic_example_classes.png 4709 4710 0 0 MultipleStartup https://code-maze.com/multiplestartup/ Tue, 25 Sep 2018 15:05:58 +0000 https://code-maze.com/wp-content/uploads/2018/09/MultipleStartup.png 4747 0 0 0 OwnerList https://code-maze.com/ownerlist/ Tue, 25 Sep 2018 15:23:29 +0000 https://code-maze.com/wp-content/uploads/2018/09/OwnerList.png 4748 0 0 0 sslbackend https://code-maze.com/sslbackend/ Tue, 25 Sep 2018 15:37:10 +0000 https://code-maze.com/wp-content/uploads/2018/09/sslbackend.png 4749 0 0 0 postman get https://code-maze.com/postman-get/ Tue, 25 Sep 2018 15:42:33 +0000 https://code-maze.com/wp-content/uploads/2018/09/postman-get.png 4750 0 0 0 cer https://code-maze.com/cer/ Tue, 25 Sep 2018 15:55:24 +0000 https://code-maze.com/wp-content/uploads/2018/09/cer.png 4752 0 0 0 welcome https://code-maze.com/welcome/ Tue, 25 Sep 2018 16:20:03 +0000 https://code-maze.com/wp-content/uploads/2018/09/welcome.png 4759 0 0 0 login https://code-maze.com/login/ Tue, 25 Sep 2018 16:20:32 +0000 https://code-maze.com/wp-content/uploads/2018/09/login.png 4760 0 0 0 cors aspnetcore examples https://code-maze.com/enabling-cors-in-asp-net-core/cors-aspnetcore-examples-cover/ Sun, 07 Oct 2018 10:20:02 +0000 https://code-maze.com/wp-content/uploads/2018/06/cors-aspnetcore-examples-cover.png 4827 3565 0 0 01-Material-Animations-CDK-Installation https://code-maze.com/get-started-angular-material/01-material-animations-cdk-installation/ Wed, 10 Oct 2018 09:04:24 +0000 https://code-maze.com/wp-content/uploads/2018/10/01-Material-Animations-CDK-Installation.png 4841 4850 0 0 02-Material-Animations-CDK-installed https://code-maze.com/get-started-angular-material/02-material-animations-cdk-installed/ Wed, 10 Oct 2018 09:04:26 +0000 https://code-maze.com/wp-content/uploads/2018/10/02-Material-Animations-CDK-installed.png 4842 4850 0 0 03-Hammerjs-installed https://code-maze.com/get-started-angular-material/03-hammerjs-installed/ Wed, 10 Oct 2018 09:04:27 +0000 https://code-maze.com/wp-content/uploads/2018/10/03-Hammerjs-installed.png 4843 4850 0 0 04-LayoutComponent https://code-maze.com/get-started-angular-material/04-layoutcomponent/ Wed, 10 Oct 2018 09:04:29 +0000 https://code-maze.com/wp-content/uploads/2018/10/04-LayoutComponent.png 4844 4850 0 0 05-FlexLayout https://code-maze.com/get-started-angular-material/05-flexlayout/ Wed, 10 Oct 2018 09:04:31 +0000 https://code-maze.com/wp-content/uploads/2018/10/05-FlexLayout.png 4845 4850 0 0 06-HomeComponent https://code-maze.com/get-started-angular-material/06-homecomponent/ Wed, 10 Oct 2018 09:04:32 +0000 https://code-maze.com/wp-content/uploads/2018/10/06-HomeComponent.png 4846 4850 0 0 07-Home-Component-finished https://code-maze.com/get-started-angular-material/07-home-component-finished/ Wed, 10 Oct 2018 09:04:34 +0000 https://code-maze.com/wp-content/uploads/2018/10/07-Home-Component-finished.gif 4847 4850 0 0 08-Tab-Center-Content https://code-maze.com/get-started-angular-material/08-tab-center-content/ Wed, 10 Oct 2018 09:04:36 +0000 https://code-maze.com/wp-content/uploads/2018/10/08-Tab-Center-Content.png 4848 4850 0 0 09-TabChangeEventLogged https://code-maze.com/get-started-angular-material/09-tabchangeeventlogged/ Wed, 10 Oct 2018 09:04:38 +0000 https://code-maze.com/wp-content/uploads/2018/10/09-TabChangeEventLogged.png 4849 4850 0 0 10-RoutingModule https://code-maze.com/angular-material-navigation/10-routingmodule/ Thu, 11 Oct 2018 08:14:33 +0000 https://code-maze.com/wp-content/uploads/2018/10/10-RoutingModule.png 4871 4870 0 0 11-Header-component https://code-maze.com/angular-material-navigation/11-header-component/ Thu, 11 Oct 2018 08:14:35 +0000 https://code-maze.com/wp-content/uploads/2018/10/11-Header-component.png 4872 4870 0 0 12-Navigation-menu-started https://code-maze.com/angular-material-navigation/12-navigation-menu-started/ Thu, 11 Oct 2018 08:14:37 +0000 https://code-maze.com/wp-content/uploads/2018/10/12-Navigation-menu-started.gif 4873 4870 0 0 13-Header-component-styled https://code-maze.com/angular-material-navigation/13-header-component-styled/ Thu, 11 Oct 2018 08:14:39 +0000 https://code-maze.com/wp-content/uploads/2018/10/13-Header-component-styled.png 4874 4870 0 0 14-Navigation-menu-completed https://code-maze.com/angular-material-navigation/14-navigation-menu-completed/ Thu, 11 Oct 2018 08:14:41 +0000 https://code-maze.com/wp-content/uploads/2018/10/14-Navigation-menu-completed.gif 4875 4870 0 0 17-OwnerList-Component-Created https://code-maze.com/angular-material-table/17-ownerlist-component-created/ Fri, 12 Oct 2018 06:32:21 +0000 https://code-maze.com/wp-content/uploads/2018/10/17-OwnerList-Component-Created.png 4892 4899 0 0 18-Owner-Routing-Module https://code-maze.com/angular-material-table/18-owner-routing-module/ Fri, 12 Oct 2018 06:32:23 +0000 https://code-maze.com/wp-content/uploads/2018/10/18-Owner-Routing-Module.png 4893 4899 0 0 19-Mat-Table https://code-maze.com/angular-material-table/19-mat-table/ Fri, 12 Oct 2018 06:32:27 +0000 https://code-maze.com/wp-content/uploads/2018/10/19-Mat-Table.png 4895 4899 0 0 22-Paging https://code-maze.com/angular-material-table/22-paging/ Fri, 12 Oct 2018 06:32:33 +0000 https://code-maze.com/wp-content/uploads/2018/10/22-Paging.gif 4898 4899 0 0 ef core code first cover https://code-maze.com/net-core-web-api-ef-core-code-first/ef-code-first-cover/ Sun, 14 Oct 2018 07:32:15 +0000 https://code-maze.com/wp-content/uploads/2018/09/ef-code-first-cover.png 4910 4467 0 0 23-Not-Found-Component-Creation https://code-maze.com/angular-material-error-details-pages/23-not-found-component-creation/ Mon, 15 Oct 2018 10:16:11 +0000 https://code-maze.com/wp-content/uploads/2018/10/23-Not-Found-Component-Creation.png 4921 4928 0 0 25-Server-Error-Creation https://code-maze.com/25-server-error-creation/ Mon, 15 Oct 2018 10:16:14 +0000 https://code-maze.com/wp-content/uploads/2018/10/25-Server-Error-Creation.png 4923 0 0 0 26-Server-Error-Finished https://code-maze.com/angular-material-error-details-pages/26-server-error-finished/ Mon, 15 Oct 2018 10:16:17 +0000 https://code-maze.com/wp-content/uploads/2018/10/26-Server-Error-Finished.gif 4924 4928 0 0 27-Owner-Details-Creation https://code-maze.com/angular-material-error-details-pages/27-owner-details-creation/ Mon, 15 Oct 2018 10:16:18 +0000 https://code-maze.com/wp-content/uploads/2018/10/27-Owner-Details-Creation.png 4925 4928 0 0 28-Owner-details-part1 https://code-maze.com/angular-material-error-details-pages/28-owner-details-part1/ Mon, 15 Oct 2018 10:16:21 +0000 https://code-maze.com/wp-content/uploads/2018/10/28-Owner-details-part1.gif 4926 4928 0 0 29-Owner-details-part2 https://code-maze.com/angular-material-error-details-pages/29-owner-details-part2/ Mon, 15 Oct 2018 10:16:23 +0000 https://code-maze.com/wp-content/uploads/2018/10/29-Owner-details-part2.gif 4927 4928 0 0 24-Not-Found-Finished https://code-maze.com/angular-material-error-details-pages/24-not-found-finished-2/ Mon, 15 Oct 2018 10:44:48 +0000 https://code-maze.com/wp-content/uploads/2018/10/24-Not-Found-Finished-1.gif 4931 4928 0 0 30-Owner-Create-Creation https://code-maze.com/angular-material-form-validation/30-owner-create-creation/ Tue, 16 Oct 2018 07:06:27 +0000 https://code-maze.com/wp-content/uploads/2018/10/30-Owner-Create-Creation.png 4935 4941 0 0 31-Owner-List-Added-Create-Link https://code-maze.com/angular-material-form-validation/31-owner-list-added-create-link/ Tue, 16 Oct 2018 07:06:29 +0000 https://code-maze.com/wp-content/uploads/2018/10/31-Owner-List-Added-Create-Link.png 4936 4941 0 0 32-Owner-Create-Finished https://code-maze.com/angular-material-form-validation/32-owner-create-finished/ Tue, 16 Oct 2018 07:06:33 +0000 https://code-maze.com/wp-content/uploads/2018/10/32-Owner-Create-Finished.gif 4937 4941 0 0 33-Dialog components - created https://code-maze.com/angular-material-form-validation/33-dialog-components-created/ Tue, 16 Oct 2018 07:06:35 +0000 https://code-maze.com/wp-content/uploads/2018/10/33-Dialog-components-created.png 4938 4941 0 0 34-Success Dialog https://code-maze.com/angular-material-form-validation/34-success-dialog/ Tue, 16 Oct 2018 07:06:38 +0000 https://code-maze.com/wp-content/uploads/2018/10/34-Success-Dialog.gif 4939 4941 0 0 35-Error Dialog https://code-maze.com/angular-material-form-validation/35-error-dialog/ Tue, 16 Oct 2018 07:06:40 +0000 https://code-maze.com/wp-content/uploads/2018/10/35-Error-Dialog.gif 4940 4941 0 0 angular material https://code-maze.com/angular-material-series/angular-material/ Wed, 17 Oct 2018 19:09:52 +0000 https://code-maze.com/wp-content/uploads/2018/10/angular-material.png 4999 4837 0 0 angular material getting started https://code-maze.com/get-started-angular-material/angular-material-getting-started/ Wed, 17 Oct 2018 19:11:33 +0000 https://code-maze.com/wp-content/uploads/2018/10/angular-material-getting-started.png 5001 4850 0 0 Angular Series https://code-maze.com/logo-1100x620-2/ Wed, 17 Oct 2018 20:52:11 +0000 https://code-maze.com/wp-content/uploads/2018/10/Logo-1100x620.png 5010 0 0 0 2018-10-22 10_08_35-claims_supported.png - Paint https://code-maze.com/2018-10-22-10_08_35-claims_supported-png-paint/ Mon, 22 Oct 2018 08:10:11 +0000 https://code-maze.com/wp-content/uploads/2018/10/2018-10-22-10_08_35-claims_supported.png-Paint.png 5027 0 0 0 2018-10-22 10_24_36-OwnerList.png - Paint https://code-maze.com/2018-10-22-10_24_36-ownerlist-png-paint/ Mon, 22 Oct 2018 08:26:35 +0000 https://code-maze.com/wp-content/uploads/2018/10/2018-10-22-10_24_36-OwnerList.png-Paint.png 5029 0 0 0 15-Sidenav-Menu-Completed https://code-maze.com/angular-material-navigation/15-sidenav-menu-completed/ Tue, 23 Oct 2018 06:27:16 +0000 https://code-maze.com/wp-content/uploads/2018/10/15-Sidenav-Menu-Completed.gif 5043 4870 0 0 16-Sidenav-Multi-Menu-completed https://code-maze.com/angular-material-navigation/16-sidenav-multi-menu-completed/ Tue, 23 Oct 2018 06:27:18 +0000 https://code-maze.com/wp-content/uploads/2018/10/16-Sidenav-Multi-Menu-completed.gif 5044 4870 0 0 DB_Diagram https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/db_diagram/ Wed, 24 Oct 2018 06:56:59 +0000 https://code-maze.com/wp-content/uploads/2018/10/DB_Diagram.jpg 5051 5050 0 0 Tables With Data https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/tables_with_data/ Wed, 24 Oct 2018 07:28:28 +0000 https://code-maze.com/wp-content/uploads/2018/10/Tables_With_Data.jpg 5052 5050 0 0 Generated Classes https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/generated-classes/ Wed, 24 Oct 2018 10:18:54 +0000 https://code-maze.com/wp-content/uploads/2018/10/Generated-Classes.jpg 5055 5050 0 0 GET_All_Authors https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/get_all_authors/ Wed, 24 Oct 2018 14:09:53 +0000 https://code-maze.com/wp-content/uploads/2018/10/GET_All_Authors.jpg 5056 5050 0 0 GetAll_Profiler https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/getall_profiler/ Wed, 24 Oct 2018 14:11:07 +0000 https://code-maze.com/wp-content/uploads/2018/10/GetAll_Profiler.jpg 5057 5050 0 0 Get_Author https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/get_author/ Wed, 24 Oct 2018 14:16:32 +0000 https://code-maze.com/wp-content/uploads/2018/10/Get_Author.jpg 5058 5050 0 0 GET_Book_Profiler https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/get_book_profiler/ Wed, 24 Oct 2018 14:19:06 +0000 https://code-maze.com/wp-content/uploads/2018/10/GET_Book_Profiler.jpg 5059 5050 0 0 GET_Book https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/get_book/ Wed, 24 Oct 2018 14:20:06 +0000 https://code-maze.com/wp-content/uploads/2018/10/GET_Book.jpg 5060 5050 0 0 Get_Author https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/get_author-2/ Wed, 24 Oct 2018 14:22:37 +0000 https://code-maze.com/wp-content/uploads/2018/10/Get_Author-1.jpg 5061 5050 0 0 Get_Author_Profiler https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/get_author_profiler/ Wed, 24 Oct 2018 14:30:54 +0000 https://code-maze.com/wp-content/uploads/2018/10/Get_Author_Profiler.jpg 5063 5050 0 0 POST_Author https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/post_author/ Wed, 24 Oct 2018 14:31:45 +0000 https://code-maze.com/wp-content/uploads/2018/10/POST_Author.jpg 5064 5050 0 0 POST_Author https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/post_author-2/ Wed, 24 Oct 2018 14:33:22 +0000 https://code-maze.com/wp-content/uploads/2018/10/POST_Author-1.jpg 5065 5050 0 0 INSERT_Profiler https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/insert_profiler/ Wed, 24 Oct 2018 14:34:05 +0000 https://code-maze.com/wp-content/uploads/2018/10/INSERT_Profiler.jpg 5066 5050 0 0 DB_Result_After_PUT https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/db_result_after_post/ Wed, 24 Oct 2018 14:34:54 +0000 https://code-maze.com/wp-content/uploads/2018/10/DB_Result_After_POST.jpg 5067 5050 0 0 DB_Result_After_POST https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/db_result_after_post-2/ Wed, 24 Oct 2018 14:37:52 +0000 https://code-maze.com/wp-content/uploads/2018/10/DB_Result_After_POST-1.jpg 5068 5050 0 0 UPDATE_Profiler https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/update_profiler/ Wed, 24 Oct 2018 14:41:43 +0000 https://code-maze.com/wp-content/uploads/2018/10/UPDATE_Profiler.jpg 5069 5050 0 0 DELETE_Publisher https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/delete_publisher/ Wed, 24 Oct 2018 14:45:18 +0000 https://code-maze.com/wp-content/uploads/2018/10/DELETE_Publisher.jpg 5070 5050 0 0 DELETE_Profiler https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/delete_profiler/ Wed, 24 Oct 2018 14:49:20 +0000 https://code-maze.com/wp-content/uploads/2018/10/DELETE_Profiler.jpg 5071 5050 0 0 DB_Result_After_DELETE https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/db_result_after_delete/ Wed, 24 Oct 2018 14:51:05 +0000 https://code-maze.com/wp-content/uploads/2018/10/DB_Result_After_DELETE.jpg 5072 5050 0 0 POST_Author https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/post_author-3/ Sun, 28 Oct 2018 04:37:38 +0000 https://code-maze.com/wp-content/uploads/2018/10/POST_Author-2.jpg 5079 5050 0 0 POST_Author https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/post_author-4/ Sun, 28 Oct 2018 04:39:06 +0000 https://code-maze.com/wp-content/uploads/2018/10/POST_Author-3.jpg 5080 5050 0 0 Angular Material Navigation Menu https://code-maze.com/angular-material-navigation/angular-material-navigation-menu/ Sun, 28 Oct 2018 19:51:34 +0000 https://code-maze.com/wp-content/uploads/2018/10/Angular-Material-Navigation-Menu.png 5086 4870 0 0 01-Created StaticFiles Folder https://code-maze.com/upload-files-dot-net-core-angular/01-created-staticfiles-folder/ Tue, 30 Oct 2018 10:06:53 +0000 https://code-maze.com/wp-content/uploads/2018/10/01-Created-StaticFiles-Folder.png 5095 5090 0 0 02-Upload finished https://code-maze.com/upload-files-dot-net-core-angular/02-upload-finished/ Tue, 30 Oct 2018 10:06:55 +0000 https://code-maze.com/wp-content/uploads/2018/10/02-Upload-finished.gif 5096 5090 0 0 03-FileUploadedInFolder https://code-maze.com/upload-files-dot-net-core-angular/03-fileuploadedinfolder/ Tue, 30 Oct 2018 10:06:57 +0000 https://code-maze.com/wp-content/uploads/2018/10/03-FileUploadedInFolder.png 5097 5090 0 0 04-UsedDownloadedPicutre https://code-maze.com/upload-files-dot-net-core-angular/04-useddownloadedpicutre/ Tue, 30 Oct 2018 10:06:58 +0000 https://code-maze.com/wp-content/uploads/2018/10/04-UsedDownloadedPicutre.gif 5098 5090 0 0 Angular Material Table Paging, Filtering, Sorting https://code-maze.com/angular-material-table/angular-material-table-paging-filtering-sorting/ Sun, 04 Nov 2018 07:36:15 +0000 https://code-maze.com/wp-content/uploads/2018/11/Angular-Material-Table-Paging-Filtering-Sorting.png 5116 4899 0 0 18-Routing-completed_Owner-Module https://code-maze.com/angular-material-table/18-routing-completed_owner-module/ Sun, 04 Nov 2018 08:33:41 +0000 https://code-maze.com/wp-content/uploads/2018/11/18-Routing-completed_Owner-Module.gif 5118 4899 0 0 20-Sorting_Functionality https://code-maze.com/angular-material-table/20-sorting_functionality/ Sun, 04 Nov 2018 08:37:48 +0000 https://code-maze.com/wp-content/uploads/2018/11/20-Sorting_Functionality.gif 5120 4899 0 0 21-Filtering_Functionality https://code-maze.com/angular-material-table/21-filtering_functionality/ Sun, 04 Nov 2018 08:42:05 +0000 https://code-maze.com/wp-content/uploads/2018/11/21-Filtering_Functionality.gif 5122 4899 0 0 ProjectSetup-1 https://code-maze.com/projectsetup-1/ Mon, 05 Nov 2018 15:16:07 +0000 https://code-maze.com/wp-content/uploads/2018/11/ProjectSetup-1.png 5128 0 0 0 ProjectSetup-1 https://code-maze.com/projectsetup-1-2/ Wed, 07 Nov 2018 14:43:14 +0000 https://code-maze.com/wp-content/uploads/2018/11/ProjectSetup-1-1.png 5159 0 0 0 2018-11-07 15_42_55-ProjectSetup-1.png - Paint https://code-maze.com/2018-11-07-15_42_55-projectsetup-1-png-paint/ Wed, 07 Nov 2018 14:44:14 +0000 https://code-maze.com/wp-content/uploads/2018/11/2018-11-07-15_42_55-ProjectSetup-1.png-Paint.png 5160 0 0 0 01-InstallingSignalR https://code-maze.com/netcore-signalr-angular/01-installingsignalr/ Fri, 09 Nov 2018 09:50:49 +0000 https://code-maze.com/wp-content/uploads/2018/11/01-InstallingSignalR.png 5196 5194 0 0 02-InstallingNg2Charts https://code-maze.com/netcore-signalr-angular/02-installingng2charts/ Fri, 09 Nov 2018 09:50:50 +0000 https://code-maze.com/wp-content/uploads/2018/11/02-InstallingNg2Charts.png 5197 5194 0 0 03-InstallingChartjs https://code-maze.com/netcore-signalr-angular/03-installingchartjs/ Fri, 09 Nov 2018 09:50:51 +0000 https://code-maze.com/wp-content/uploads/2018/11/03-InstallingChartjs.png 5198 5194 0 0 04-SignalR-Connected https://code-maze.com/netcore-signalr-angular/04-signalr-connected/ Fri, 09 Nov 2018 09:50:53 +0000 https://code-maze.com/wp-content/uploads/2018/11/04-SignalR-Connected.gif 5199 5194 0 0 05-SignalR-Chart-Completed https://code-maze.com/netcore-signalr-angular/05-signalr-chart-completed/ Fri, 09 Nov 2018 09:50:56 +0000 https://code-maze.com/wp-content/uploads/2018/11/05-SignalR-Chart-Completed.gif 5200 5194 0 0 06-SignalR-Chart-TwoWay-Communication https://code-maze.com/netcore-signalr-angular/06-signalr-chart-twoway-communication/ Fri, 09 Nov 2018 09:50:58 +0000 https://code-maze.com/wp-content/uploads/2018/11/06-SignalR-Chart-TwoWay-Communication.gif 5201 5194 0 0 openid-configuration https://code-maze.com/openid-configuration/ Sat, 10 Nov 2018 20:04:00 +0000 https://code-maze.com/wp-content/uploads/2018/10/openid-configuration.png 5226 0 0 0 Error and Details Pages With Material Components https://code-maze.com/angular-material-error-details-pages/error-and-details-pages-with-material-components/ Sun, 11 Nov 2018 18:06:38 +0000 https://code-maze.com/wp-content/uploads/2018/11/Error-and-Details-Pages-With-Material-Components.png 5245 4928 0 0 certificates import export https://code-maze.com/certificates-import-export/ Tue, 13 Nov 2018 13:01:40 +0000 https://code-maze.com/wp-content/uploads/2018/10/certificates-import-export.gif 5283 0 0 0 ClientCertificate-Exported https://code-maze.com/clientcertificate-exported/ Fri, 16 Nov 2018 08:25:45 +0000 https://code-maze.com/wp-content/uploads/2018/10/ClientCertificate-Exported.gif 5297 0 0 0 ClientCertificate-Imported https://code-maze.com/clientcertificate-imported/ Fri, 16 Nov 2018 08:38:44 +0000 https://code-maze.com/wp-content/uploads/2018/10/ClientCertificate-Imported.gif 5298 0 0 0 logout-page https://code-maze.com/logout-page/ Fri, 16 Nov 2018 10:09:36 +0000 https://code-maze.com/wp-content/uploads/2018/10/logout-page.png 5305 0 0 0 Authentication - OwnerList https://code-maze.com/authentication-ownerlist/ Fri, 16 Nov 2018 21:47:48 +0000 https://code-maze.com/wp-content/uploads/2018/11/Authentication-OwnerList.gif 5311 0 0 0 Redirection to the Required page https://code-maze.com/redirection-to-the-required-page/ Fri, 16 Nov 2018 22:08:33 +0000 https://code-maze.com/wp-content/uploads/2018/11/Redirection-to-the-Required-page.gif 5312 0 0 0 Angular Material Form Validation, Input, Datepicker and Modal https://code-maze.com/angular-material-form-validation/angular-material-form-validation-input-datepicker-and-modal/ Sun, 18 Nov 2018 10:14:05 +0000 https://code-maze.com/wp-content/uploads/2018/11/Angular-Material-Form-Validation-Input-Datepicker-and-Modal.png 5318 4941 0 0 FlurlHttpException https://code-maze.com/consuming-github-api-rest-with-flurl/flurlhttpexception-exe/ Sun, 18 Nov 2018 11:08:17 +0000 https://code-maze.com/wp-content/uploads/2018/10/FlurlHttpException.exe_.png 5321 4822 0 0 Personal Access Tokens https://code-maze.com/consuming-github-api-rest-with-flurl/personal-access-tokens/ Sun, 18 Nov 2018 12:11:07 +0000 https://code-maze.com/wp-content/uploads/2018/10/Personal-Access-Tokens.png 5322 4822 0 0 flurl featured image https://code-maze.com/consuming-github-api-rest-with-flurl/flurl-featured-image/ Sun, 18 Nov 2018 14:23:15 +0000 https://code-maze.com/wp-content/uploads/2018/10/flurl-featured-image.png 5326 4822 0 0 ef db first https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/ef-db-first-cover/ Sat, 24 Nov 2018 17:06:04 +0000 https://code-maze.com/wp-content/uploads/2018/11/ef-db-first-cover.png 5340 5050 0 0 dotnet core signalr angular https://code-maze.com/netcore-signalr-angular/dotnet-core-signalr-angular/ Sun, 02 Dec 2018 17:47:11 +0000 https://code-maze.com/wp-content/uploads/2018/12/dotnet-core-signalr-angular.png 5353 5194 0 0 dotnet core file upload https://code-maze.com/upload-files-dot-net-core-angular/dotnet-core-file-upload/ Sun, 02 Dec 2018 20:08:13 +0000 https://code-maze.com/wp-content/uploads/2018/12/dotnet-core-file-upload.png 5362 5090 0 0 servicestack http utils https://code-maze.com/?attachment_id=5367 Sun, 02 Dec 2018 20:33:19 +0000 https://code-maze.com/wp-content/uploads/2018/12/servicestack-http-utils.png 5367 5363 0 0 01-Created Resources Folder https://code-maze.com/upload-files-dot-net-core-angular/01-created-resources-folder/ Mon, 03 Dec 2018 07:13:29 +0000 https://code-maze.com/wp-content/uploads/2018/12/01-Created-Resources-Folder.png 5371 5090 0 0 03-FilesUploadedInFolder https://code-maze.com/upload-files-dot-net-core-angular/03-filesuploadedinfolder/ Mon, 03 Dec 2018 07:13:30 +0000 https://code-maze.com/wp-content/uploads/2018/12/03-FilesUploadedInFolder.png 5372 5090 0 0 Logo-1100x620 https://code-maze.com/logo-1100x620-3/ Mon, 03 Dec 2018 08:45:07 +0000 https://code-maze.com/wp-content/uploads/2018/12/Logo-1100x620.png 5374 0 0 0 React-series-logo-1024x577 https://code-maze.com/react-series-logo-1024x577/ Mon, 03 Dec 2018 08:55:53 +0000 https://code-maze.com/wp-content/uploads/2018/12/React-series-logo-1024x577.png 5380 0 0 0 dotnet-core-signalr-angular https://code-maze.com/dotnet-core-signalr-angular-2/ Mon, 03 Dec 2018 21:20:51 +0000 https://code-maze.com/wp-content/uploads/2018/12/dotnet-core-signalr-angular-1.png 5385 0 0 0 mvc architecture https://code-maze.com/asp-net-core-mvc-series/mvc-architecture/ Wed, 19 Dec 2018 16:39:17 +0000 https://code-maze.com/wp-content/uploads/2018/12/mvc-architecture.jpg 5404 5403 0 0 SOLID https://code-maze.com/asp-net-core-mvc-series/solid-cover/ Sun, 23 Dec 2018 09:38:23 +0000 https://code-maze.com/wp-content/uploads/2018/12/SOLID-cover.png 5418 5403 0 0 dependency inversion principle https://code-maze.com/dependency-inversion-principle/ Sun, 23 Dec 2018 09:40:48 +0000 https://code-maze.com/wp-content/uploads/2018/12/dependency-inversion-principle.png 5421 0 0 0 interface segregation principle https://code-maze.com/interface-segregation-principle/ Sun, 23 Dec 2018 09:40:57 +0000 https://code-maze.com/wp-content/uploads/2018/12/interface-segregation-principle.png 5422 0 0 0 liskov substitution principle https://code-maze.com/liskov-substitution-principle/ Sun, 23 Dec 2018 09:41:07 +0000 https://code-maze.com/wp-content/uploads/2018/12/liskov-substitution-principle.png 5423 0 0 0 open closed principle https://code-maze.com/open-closed-principle/ Sun, 23 Dec 2018 09:41:16 +0000 https://code-maze.com/wp-content/uploads/2018/12/open-closed-principle.png 5424 0 0 0 single responsibility principle https://code-maze.com/single-responsibility-principle-2/ Sun, 23 Dec 2018 09:41:25 +0000 https://code-maze.com/wp-content/uploads/2018/12/single-responsibility-principle.png 5425 0 0 0 01-SRP finished https://code-maze.com/single-responsibility-principle/01-srp-finished/ Sun, 23 Dec 2018 09:51:11 +0000 https://code-maze.com/wp-content/uploads/2018/12/01-SRP-finished.png 5428 5414 0 0 02-OCP first example result https://code-maze.com/open-closed-principle/02-ocp-first-example-result/ Sun, 30 Dec 2018 14:03:42 +0000 https://code-maze.com/wp-content/uploads/2018/12/02-OCP-first-example-result.png 5438 5436 0 0 03-OCP second example result https://code-maze.com/open-closed-principle/03-ocp-second-example-result-2/ Sun, 30 Dec 2018 14:20:49 +0000 https://code-maze.com/wp-content/uploads/2018/12/03-OCP-second-example-result-1.png 5444 5436 0 0 04-LSP correct result but not good solution https://code-maze.com/liskov-substitution-principle/04-lsp-correct-result-but-not-good-solution/ Sat, 05 Jan 2019 07:43:58 +0000 https://code-maze.com/wp-content/uploads/2019/01/04-LSP-correct-result-but-not-good-solution.png 5474 5476 0 0 05-LSP wrong result https://code-maze.com/liskov-substitution-principle/05-lsp-wrong-result/ Sat, 05 Jan 2019 07:44:00 +0000 https://code-maze.com/wp-content/uploads/2019/01/05-LSP-wrong-result.png 5475 5476 0 0 Create New Project https://code-maze.com/working-with-data-in-asp-net-core-mvc/create-new-project-3/ Sun, 13 Jan 2019 12:35:22 +0000 https://code-maze.com/wp-content/uploads/2019/01/Create-New-Project.jpg 5504 5500 0 0 New web application template https://code-maze.com/working-with-data-in-asp-net-core-mvc/new-web-application-template/ Sun, 13 Jan 2019 12:37:34 +0000 https://code-maze.com/wp-content/uploads/2019/01/New-web-application-template.jpg 5505 5500 0 0 Solution Explorer https://code-maze.com/working-with-data-in-asp-net-core-mvc/solution-explorer/ Sun, 13 Jan 2019 12:51:24 +0000 https://code-maze.com/wp-content/uploads/2019/01/Solution-Explorer.jpg 5506 5500 0 0 default mvc app https://code-maze.com/working-with-data-in-asp-net-core-mvc/default-mvc-app/ Sun, 13 Jan 2019 12:55:00 +0000 https://code-maze.com/wp-content/uploads/2019/01/default-mvc-app.jpg 5507 5500 0 0 add controller https://code-maze.com/working-with-data-in-asp-net-core-mvc/add-controller/ Sun, 13 Jan 2019 12:59:12 +0000 https://code-maze.com/wp-content/uploads/2019/01/add-controller.jpg 5508 5500 0 0 add scaffold https://code-maze.com/working-with-data-in-asp-net-core-mvc/add-scaffold/ Sun, 13 Jan 2019 13:01:19 +0000 https://code-maze.com/wp-content/uploads/2019/01/add-scaffold.jpg 5509 5500 0 0 add empty mvc controller https://code-maze.com/working-with-data-in-asp-net-core-mvc/add-empty-mvc-controller/ Sun, 13 Jan 2019 13:50:57 +0000 https://code-maze.com/wp-content/uploads/2019/01/add-empty-mvc-controller.jpg 5510 5500 0 0 books index page https://code-maze.com/working-with-data-in-asp-net-core-mvc/books-index-page/ Sun, 13 Jan 2019 13:54:29 +0000 https://code-maze.com/wp-content/uploads/2019/01/books-index-page.jpg 5511 5500 0 0 books details https://code-maze.com/working-with-data-in-asp-net-core-mvc/books-details/ Sun, 13 Jan 2019 13:57:01 +0000 https://code-maze.com/wp-content/uploads/2019/01/books-details.jpg 5512 5500 0 0 add view https://code-maze.com/getting-started-with-asp-net-core-mvc/add-view/ Sun, 13 Jan 2019 14:10:14 +0000 https://code-maze.com/wp-content/uploads/2019/01/add-view.jpg 5515 5513 0 0 add mvc view https://code-maze.com/getting-started-with-asp-net-core-mvc/add-mvc-view/ Sun, 13 Jan 2019 14:12:13 +0000 https://code-maze.com/wp-content/uploads/2019/01/add-mvc-view.jpg 5517 5513 0 0 solution explorer view https://code-maze.com/getting-started-with-asp-net-core-mvc/solution-explorer-view/ Sun, 13 Jan 2019 14:14:50 +0000 https://code-maze.com/wp-content/uploads/2019/01/solution-explorer-view.jpg 5518 5513 0 0 book details view page https://code-maze.com/getting-started-with-asp-net-core-mvc/book-details-view-page/ Sun, 13 Jan 2019 14:20:24 +0000 https://code-maze.com/wp-content/uploads/2019/01/book-details-view-page.jpg 5519 5513 0 0 dalsoft.restclient featured https://code-maze.com/dalsoft-restclient-consume-any-rest-api/dalsoft-restclient-featured/ Sun, 13 Jan 2019 14:21:32 +0000 https://code-maze.com/wp-content/uploads/2019/01/dalsoft-restclient-featured.png 5520 5492 0 0 book details with view https://code-maze.com/getting-started-with-asp-net-core-mvc/book-details-with-view/ Sun, 13 Jan 2019 14:37:21 +0000 https://code-maze.com/wp-content/uploads/2019/01/book-details-with-view.jpg 5522 5513 0 0 book details view page with annotations https://code-maze.com/getting-started-with-asp-net-core-mvc/book-details-view-page-with-annotations/ Sun, 13 Jan 2019 14:42:59 +0000 https://code-maze.com/wp-content/uploads/2019/01/book-details-view-page-with-annotations.jpg 5525 5513 0 0 Book create page with validation errors https://code-maze.com/getting-started-with-asp-net-core-mvc/book-create-page-with-validation-errors/ Sun, 13 Jan 2019 14:48:04 +0000 https://code-maze.com/wp-content/uploads/2019/01/Book-create-page-with-validation-errors.jpg 5527 5513 0 0 Runtime Binder Exception https://code-maze.com/advanced-csharp-dynamic-type/runtimebinderexception/ Mon, 21 Jan 2019 20:51:00 +0000 https://code-maze.com/wp-content/uploads/2019/01/RuntimeBinderException.png 5571 5488 0 0 Runtime Binder Exception LogTrace https://code-maze.com/advanced-csharp-dynamic-type/runtimebinderexception2/ Wed, 23 Jan 2019 06:05:42 +0000 https://code-maze.com/wp-content/uploads/2019/01/RuntimeBinderException2.png 5577 5488 0 0 dlr-archoverview https://code-maze.com/advanced-csharp-dynamic-type/dlr-archoverview/ Sat, 26 Jan 2019 17:33:04 +0000 https://code-maze.com/wp-content/uploads/2019/01/dlr-archoverview.png 5629 5488 0 0 06-DIP finished https://code-maze.com/dependency-inversion-principle/06-dip-finished/ Sat, 26 Jan 2019 18:42:00 +0000 https://code-maze.com/wp-content/uploads/2019/01/06-DIP-finished.png 5633 5631 0 0 int array count runtimebinderexception https://code-maze.com/advanced-csharp-dynamic-type/int-array-count-runtimebinderexception/ Sun, 27 Jan 2019 17:32:36 +0000 https://code-maze.com/wp-content/uploads/2019/01/int-array-count-runtimebinderexception.png 5710 5488 0 0 advanced csharp https://code-maze.com/advanced-csharp-dynamic-type/advanced-csharp/ Sun, 27 Jan 2019 20:14:38 +0000 https://code-maze.com/wp-content/uploads/2019/01/advanced-csharp.png 5724 5488 0 0 nemanja-dukic https://code-maze.com/nemanja-dukic/ Tue, 29 Jan 2019 18:07:00 +0000 https://code-maze.com/wp-content/uploads/2019/01/nemanja-dukic.jpg 5735 0 0 0 add scaffolded item https://code-maze.com/working-with-data-in-asp-net-core-mvc/add-scaffolded-item/ Wed, 30 Jan 2019 18:27:36 +0000 https://code-maze.com/wp-content/uploads/2019/01/add-scaffolded-item.jpg 5738 5500 0 0 add scaffold controller with view using ef https://code-maze.com/working-with-data-in-asp-net-core-mvc/add-scaffold-controller-with-view-using-ef/ Wed, 30 Jan 2019 18:28:37 +0000 https://code-maze.com/wp-content/uploads/2019/01/add-scaffold-controller-with-view-using-ef.jpg 5739 5500 0 0 Add.]]> add data context https://code-maze.com/working-with-data-in-asp-net-core-mvc/add-data-context/ Wed, 30 Jan 2019 18:29:33 +0000 https://code-maze.com/wp-content/uploads/2019/01/add-data-context.jpg 5740 5500 0 0 add data context https://code-maze.com/working-with-data-in-asp-net-core-mvc/add-data-context-2/ Wed, 30 Jan 2019 18:31:37 +0000 https://code-maze.com/wp-content/uploads/2019/01/add-data-context-1.jpg 5741 5500 0 0 Add view and controller details https://code-maze.com/working-with-data-in-asp-net-core-mvc/add-view-and-controller-details/ Wed, 30 Jan 2019 18:34:31 +0000 https://code-maze.com/wp-content/uploads/2019/01/Add-view-and-controller-details.jpg 5742 5500 0 0 database and columns https://code-maze.com/working-with-data-in-asp-net-core-mvc/database-and-columns/ Wed, 30 Jan 2019 18:36:28 +0000 https://code-maze.com/wp-content/uploads/2019/01/database-and-columns.jpg 5743 5500 0 0 database table with data https://code-maze.com/working-with-data-in-asp-net-core-mvc/database-table-with-data/ Wed, 30 Jan 2019 18:38:46 +0000 https://code-maze.com/wp-content/uploads/2019/01/database-table-with-data.jpg 5744 5500 0 0 details page https://code-maze.com/working-with-data-in-asp-net-core-mvc/details-page/ Wed, 30 Jan 2019 18:39:42 +0000 https://code-maze.com/wp-content/uploads/2019/01/details-page.jpg 5745 5500 0 0 create page https://code-maze.com/working-with-data-in-asp-net-core-mvc/create-page/ Wed, 30 Jan 2019 18:40:42 +0000 https://code-maze.com/wp-content/uploads/2019/01/create-page.jpg 5746 5500 0 0 details page https://code-maze.com/working-with-data-in-asp-net-core-mvc/details-page-2/ Wed, 30 Jan 2019 18:42:48 +0000 https://code-maze.com/wp-content/uploads/2019/01/details-page-1.jpg 5747 5500 0 0 edit page https://code-maze.com/working-with-data-in-asp-net-core-mvc/edit-page/ Wed, 30 Jan 2019 18:44:22 +0000 https://code-maze.com/wp-content/uploads/2019/01/edit-page.jpg 5748 5500 0 0 delete confirmation page https://code-maze.com/working-with-data-in-asp-net-core-mvc/delete-confirmation-page/ Wed, 30 Jan 2019 18:45:14 +0000 https://code-maze.com/wp-content/uploads/2019/01/delete-confirmation-page.jpg 5749 5500 0 0 scaffolding generated files https://code-maze.com/working-with-data-in-asp-net-core-mvc/scaffolding-generated-files/ Wed, 30 Jan 2019 18:46:24 +0000 https://code-maze.com/wp-content/uploads/2019/01/scaffolding-generated-files.jpg 5750 5500 0 0 list page https://code-maze.com/working-with-data-in-asp-net-core-mvc/list-page/ Wed, 30 Jan 2019 19:02:21 +0000 https://code-maze.com/wp-content/uploads/2019/01/list-page.jpg 5753 5500 0 0 homepage-weekly_reviews https://code-maze.com/start-here/homepage-weekly_reviews/ Wed, 30 Jan 2019 20:46:13 +0000 https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg 5776 5755 0 0 homepage-latest_articles https://code-maze.com/start-here/homepage-latest_articles/ Fri, 01 Feb 2019 12:24:45 +0000 https://code-maze.com/wp-content/uploads/2019/02/homepage-latest_articles.jpg 5800 5755 0 0 homepage-rest_with_spring https://code-maze.com/start-here/homepage-rest_with_spring/ Fri, 01 Feb 2019 12:25:22 +0000 https://code-maze.com/wp-content/uploads/2019/02/homepage-rest_with_spring.jpg 5801 5755 0 0 Overview audience https://code-maze.com/sponsor/overview-audience/ Fri, 01 Feb 2019 18:13:18 +0000 https://code-maze.com/wp-content/uploads/2019/02/Overview-audience.png 5807 5804 0 0 Sponsor message desktop https://code-maze.com/sponsor/sponsor-message-desktop/ Fri, 01 Feb 2019 19:55:08 +0000 https://code-maze.com/wp-content/uploads/2019/02/Sponsor-message-desktop.png 5814 5804 0 0 Sponsor message mobile https://code-maze.com/sponsor/sponsor-message-mobile/ Fri, 01 Feb 2019 19:55:38 +0000 https://code-maze.com/wp-content/uploads/2019/02/Sponsor-message-mobile.png 5815 5804 0 0 sponsorship featured https://code-maze.com/sponsor/sponsorship-featured/ Fri, 01 Feb 2019 20:39:28 +0000 https://code-maze.com/wp-content/uploads/2019/02/sponsorship-featured.png 5818 5804 0 0 coding-background-texture https://code-maze.com/coding-background-texture/ Sun, 03 Feb 2019 09:05:50 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg 5839 0 0 0 coding-icon_15 https://code-maze.com/coding-icon_15/ Sun, 03 Feb 2019 09:05:52 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_15.jpg 5840 0 0 0 coding-icon_20 https://code-maze.com/coding-icon_20/ Sun, 03 Feb 2019 09:05:52 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_20.jpg 5841 0 0 0 coding-iconArtboard-19-copy-10 https://code-maze.com/coding-iconartboard-19-copy-10/ Sun, 03 Feb 2019 09:05:52 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-10.jpg 5842 0 0 0 coding-icon_4 https://code-maze.com/coding-icon_4/ Sun, 03 Feb 2019 09:05:52 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_4.jpg 5843 0 0 0 coding-icon_21 https://code-maze.com/coding-icon_21/ Sun, 03 Feb 2019 09:05:52 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_21.jpg 5844 0 0 0 coding-icon_16 https://code-maze.com/coding-icon_16/ Sun, 03 Feb 2019 09:05:52 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_16.jpg 5845 0 0 0 coding-isometric-11 https://code-maze.com/coding-isometric-11/ Sun, 03 Feb 2019 09:05:52 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-11.png 5846 0 0 0 coding-icon_2 https://code-maze.com/coding-icon_2/ Sun, 03 Feb 2019 09:05:54 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg 5847 0 0 0 coding-icon_8 https://code-maze.com/coding-icon_8/ Sun, 03 Feb 2019 09:05:54 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg 5848 0 0 0 video-overlay https://code-maze.com/video-overlay/ Sun, 03 Feb 2019 09:13:57 +0000 https://code-maze.com/wp-content/uploads/2019/02/video-overlay.jpg 47113 0 0 0 coding-icon_19 https://code-maze.com/coding-icon_19/ Sun, 03 Feb 2019 09:13:58 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_19.jpg 47114 0 0 0 coding-isometric-06 https://code-maze.com/coding-isometric-06/ Sun, 03 Feb 2019 09:13:58 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-06.png 47115 0 0 0 coding-isometric-07 https://code-maze.com/coding-isometric-07/ Sun, 03 Feb 2019 09:13:59 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-07.png 47116 0 0 0 coding-isometric-08 https://code-maze.com/coding-isometric-08/ Sun, 03 Feb 2019 09:14:02 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-08.png 47117 0 0 0 coding-isometric-09 https://code-maze.com/coding-isometric-09/ Sun, 03 Feb 2019 09:14:04 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-09.png 47118 0 0 0 portraits-circle-small_2 https://code-maze.com/portraits-circle-small_2/ Sun, 03 Feb 2019 09:14:05 +0000 https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_2.png 47119 0 0 0 portraits-circle-small_6 https://code-maze.com/portraits-circle-small_6/ Sun, 03 Feb 2019 09:14:06 +0000 https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_6.png 47120 0 0 0 portraits-circle-small_1 https://code-maze.com/portraits-circle-small_1/ Sun, 03 Feb 2019 09:14:06 +0000 https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_1.png 47121 0 0 0 portraits-circle-small_5 https://code-maze.com/portraits-circle-small_5/ Sun, 03 Feb 2019 09:14:06 +0000 https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_5.png 47122 0 0 0 portraits-circle-small_3 https://code-maze.com/portraits-circle-small_3/ Sun, 03 Feb 2019 09:14:06 +0000 https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_3.png 47123 0 0 0 portraits-circle-small_4 https://code-maze.com/portraits-circle-small_4/ Sun, 03 Feb 2019 09:14:06 +0000 https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_4.png 47124 0 0 0 coding-isometric-10 https://code-maze.com/coding-isometric-10/ Sun, 03 Feb 2019 09:14:06 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-10.png 47125 0 0 0 logo_3 https://code-maze.com/logo_3/ Sun, 03 Feb 2019 09:14:08 +0000 https://code-maze.com/wp-content/uploads/2019/02/logo_3.jpg 47126 0 0 0 logo_2 https://code-maze.com/logo_2/ Sun, 03 Feb 2019 09:14:08 +0000 https://code-maze.com/wp-content/uploads/2019/02/logo_2.jpg 47127 0 0 0 logo_4 https://code-maze.com/logo_4/ Sun, 03 Feb 2019 09:14:08 +0000 https://code-maze.com/wp-content/uploads/2019/02/logo_4.jpg 47128 0 0 0 logo_1 https://code-maze.com/logo_1/ Sun, 03 Feb 2019 09:14:08 +0000 https://code-maze.com/wp-content/uploads/2019/02/logo_1.jpg 47129 0 0 0 coding-isometric-12 https://code-maze.com/coding-isometric-12/ Sun, 03 Feb 2019 09:14:41 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-12.png 47130 0 0 0 coding-icon_12 https://code-maze.com/coding-icon_12/ Sun, 03 Feb 2019 09:14:42 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_12.jpg 47131 0 0 0 coding-icon_13 https://code-maze.com/coding-icon_13/ Sun, 03 Feb 2019 09:14:42 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_13.jpg 47132 0 0 0 coding-icon_14 https://code-maze.com/coding-icon_14/ Sun, 03 Feb 2019 09:14:42 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_14.jpg 47133 0 0 0 coding-icon_17 https://code-maze.com/coding-icon_17/ Sun, 03 Feb 2019 09:14:42 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_17.jpg 47134 0 0 0 coding-icon_18 https://code-maze.com/coding-icon_18/ Sun, 03 Feb 2019 09:14:43 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_18.jpg 47135 0 0 0 coding-isometric-01 https://code-maze.com/coding-isometric-01/ Sun, 03 Feb 2019 09:14:43 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-01.png 47136 0 0 0 coding-icon_1 https://code-maze.com/coding-icon_1/ Sun, 03 Feb 2019 09:14:45 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_1.jpg 47137 0 0 0 coding-icon_3 https://code-maze.com/coding-icon_3/ Sun, 03 Feb 2019 09:14:45 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_3.jpg 47138 0 0 0 icon database https://code-maze.com/coding-icon_5/ Sun, 03 Feb 2019 09:14:46 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_5.jpg 47139 0 0 0 coding-icon_7 https://code-maze.com/coding-icon_7/ Sun, 03 Feb 2019 09:14:46 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_7.jpg 47140 0 0 0 coding-icon_6 https://code-maze.com/coding-icon_6/ Sun, 03 Feb 2019 09:14:46 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-icon_6.jpg 47141 0 0 0 coding-isometric-02 https://code-maze.com/coding-isometric-02/ Sun, 03 Feb 2019 09:14:46 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-02.png 47142 0 0 0 coding-iconArtboard-19-copy-8 https://code-maze.com/coding-iconartboard-19-copy-8/ Sun, 03 Feb 2019 09:14:47 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-8.jpg 47143 0 0 0 coding-isometric-03 https://code-maze.com/coding-isometric-03/ Sun, 03 Feb 2019 09:14:48 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-03.png 47144 0 0 0 coding-isometric-04 https://code-maze.com/coding-isometric-04/ Sun, 03 Feb 2019 09:14:49 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-04.png 47145 0 0 0 coding-iconArtboard-19-copy-9 https://code-maze.com/coding-iconartboard-19-copy-9/ Sun, 03 Feb 2019 09:14:51 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-9.jpg 47146 0 0 0 coding-isometric-05 https://code-maze.com/coding-isometric-05/ Sun, 03 Feb 2019 09:14:51 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-05.png 47147 0 0 0 coding-dot-bg https://code-maze.com/coding-dot-bg/ Sun, 03 Feb 2019 09:14:54 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-dot-bg.png 47148 0 0 0 testimonial-avatar https://code-maze.com/testimonial-avatar/ Sun, 03 Feb 2019 09:14:55 +0000 https://code-maze.com/wp-content/uploads/2019/02/testimonial-avatar.png 47149 0 0 0 coding-dots https://code-maze.com/coding-dots/ Sun, 03 Feb 2019 09:14:55 +0000 https://code-maze.com/wp-content/uploads/2019/02/coding-dots.png 47150 0 0 0 01-Builder finished https://code-maze.com/builder-design-pattern/01-builder-finished/ Sat, 09 Feb 2019 10:50:24 +0000 https://code-maze.com/wp-content/uploads/2019/02/01-Builder-finished.png 47230 47228 0 0 02-Recursive Builder inheritance error https://code-maze.com/fluent-builder-recursive-generics/02-recursive-builder-inheritance-error/ Sat, 09 Feb 2019 15:25:50 +0000 https://code-maze.com/wp-content/uploads/2019/02/02-Recursive-Builder-inheritance-error.png 47237 47234 0 0 builder 2 https://code-maze.com/builder-2/ Sun, 10 Feb 2019 16:59:58 +0000 https://code-maze.com/wp-content/uploads/2019/02/builder-2.png 47252 0 0 0 builder https://code-maze.com/builder/ Sun, 10 Feb 2019 17:00:09 +0000 https://code-maze.com/wp-content/uploads/2019/02/builder.png 47253 0 0 0 design patterns https://code-maze.com/design-patterns-csharp/design-patterns/ Sun, 10 Feb 2019 17:00:21 +0000 https://code-maze.com/wp-content/uploads/2019/02/design-patterns.png 47254 47667 0 0 faceted builder https://code-maze.com/faceted-builder-2/ Sun, 10 Feb 2019 17:00:32 +0000 https://code-maze.com/wp-content/uploads/2019/02/faceted-builder.png 47255 0 0 0 geometric-1732847_1920 https://code-maze.com/?attachment_id=47258 Sun, 10 Feb 2019 17:33:20 +0000 https://code-maze.com/wp-content/uploads/2019/02/geometric-1732847_1920.jpg 47258 47109 0 0 marinko profile https://code-maze.com/?attachment_id=47264 Sun, 10 Feb 2019 18:23:16 +0000 https://code-maze.com/wp-content/uploads/2019/02/marinko-profile.jpg 47264 47109 0 0 factory method https://code-maze.com/factory-method/ Sun, 17 Feb 2019 08:00:08 +0000 https://code-maze.com/wp-content/uploads/2019/02/factory-method.png 47298 0 0 0 mvc series https://code-maze.com/asp-net-core-mvc-series/mvc-series/ Sun, 17 Feb 2019 09:02:47 +0000 https://code-maze.com/wp-content/uploads/2019/02/mvc-series.png 47299 5403 0 0 01 getting started https://code-maze.com/getting-started-with-asp-net-core-mvc/01-getting-started/ Sun, 17 Feb 2019 09:07:32 +0000 https://code-maze.com/wp-content/uploads/2019/01/01-getting-started.png 47302 5513 0 0 02 working with data https://code-maze.com/working-with-data-in-asp-net-core-mvc/02-working-with-data/ Sun, 17 Feb 2019 09:10:31 +0000 https://code-maze.com/wp-content/uploads/2019/01/02-working-with-data.png 47305 5500 0 0 01-FactoryDictionaryPopulated https://code-maze.com/factory-method/01-factorydictionarypopulated/ Sun, 17 Feb 2019 12:55:15 +0000 https://code-maze.com/wp-content/uploads/2019/02/01-FactoryDictionaryPopulated.png 47325 47324 0 0 02-Factory finished https://code-maze.com/factory-method/02-factory-finished/ Sun, 17 Feb 2019 12:55:16 +0000 https://code-maze.com/wp-content/uploads/2019/02/02-Factory-finished.png 47326 47324 0 0 12-creating-netcore-project 2.2 https://code-maze.com/net-core-web-development-part2/12-creating-netcore-project-2-2/ Sun, 17 Feb 2019 18:01:20 +0000 https://code-maze.com/wp-content/uploads/2018/01/12-creating-netcore-project-2.2.png 47333 1053 0 0 13-choosing-webapi-project 2.2 https://code-maze.com/net-core-web-development-part2/13-choosing-webapi-project-2-2/ Sun, 17 Feb 2019 18:01:22 +0000 https://code-maze.com/wp-content/uploads/2018/01/13-choosing-webapi-project-2.2.png 47334 1053 0 0 profile image vladimir https://code-maze.com/?attachment_id=47358 Mon, 18 Feb 2019 12:39:52 +0000 https://code-maze.com/wp-content/uploads/2019/02/profile_image_sqare.png 47358 47109 0 0 goals orange https://code-maze.com/?attachment_id=47370 Mon, 18 Feb 2019 13:47:26 +0000 https://code-maze.com/wp-content/uploads/2019/02/goals-orange.png 47370 47109 0 0 new-project https://code-maze.com/unit-testing-aspnetcore-web-api/new-project-4/ Tue, 19 Feb 2019 08:00:07 +0000 https://code-maze.com/wp-content/uploads/2018/07/new-project.png 47433 3847 0 0 aspnetcore.app https://code-maze.com/unit-testing-aspnetcore-web-api/aspnetcore-app/ Tue, 19 Feb 2019 08:36:05 +0000 https://code-maze.com/wp-content/uploads/2018/07/aspnetcore.app_.png 47434 3847 0 0 folders and files https://code-maze.com/views-partial-views-and-layouts-in-asp-net-core-mvc/folders-and-files/ Wed, 20 Feb 2019 06:58:48 +0000 https://code-maze.com/wp-content/uploads/2019/02/folders-and-files.jpg 47444 47443 0 0 validation without summary https://code-maze.com/views-partial-views-and-layouts-in-asp-net-core-mvc/validation-without-summary/ Wed, 20 Feb 2019 07:41:57 +0000 https://code-maze.com/wp-content/uploads/2019/02/validation-without-summary.jpg 47445 47443 0 0 validation with summary https://code-maze.com/views-partial-views-and-layouts-in-asp-net-core-mvc/validation-with-summary/ Wed, 20 Feb 2019 07:42:54 +0000 https://code-maze.com/wp-content/uploads/2019/02/validation-with-summary.jpg 47446 47443 0 0 Add partial views https://code-maze.com/views-partial-views-and-layouts-in-asp-net-core-mvc/add-partial-views/ Wed, 20 Feb 2019 07:47:30 +0000 https://code-maze.com/wp-content/uploads/2019/02/Add-partial-views.jpg 47447 47443 0 0 Add partial views2 https://code-maze.com/views-partial-views-and-layouts-in-asp-net-core-mvc/add-partial-views2/ Wed, 20 Feb 2019 07:50:12 +0000 https://code-maze.com/wp-content/uploads/2019/02/Add-partial-views2.jpg 47449 47443 0 0 details page with authors section https://code-maze.com/views-partial-views-and-layouts-in-asp-net-core-mvc/details-page-with-authors-section/ Wed, 20 Feb 2019 07:54:55 +0000 https://code-maze.com/wp-content/uploads/2019/02/details-page-with-authors-section.jpg 47452 47443 0 0 layout file in solution explorer https://code-maze.com/views-partial-views-and-layouts-in-asp-net-core-mvc/layout-file-in-solution-explorer/ Wed, 20 Feb 2019 07:58:09 +0000 https://code-maze.com/wp-content/uploads/2019/02/layout-file-in-solution-explorer.png 47453 47443 0 0 pdf web api https://code-maze.com/create-pdf-dotnetcore/pdf-web-api/ Wed, 20 Feb 2019 17:40:00 +0000 https://code-maze.com/wp-content/uploads/2018/06/pdf-web-api.png 47465 3498 0 0 goals orange 2 https://code-maze.com/goals-orange-2/ Sat, 23 Feb 2019 07:18:57 +0000 https://code-maze.com/wp-content/uploads/2019/02/goals-orange-1.png 47479 0 0 0 01-Singleton call https://code-maze.com/singleton/01-singleton-call/ Sat, 23 Feb 2019 13:44:52 +0000 https://code-maze.com/wp-content/uploads/2019/02/01-Singleton-call.png 47491 47488 0 0 singleton https://code-maze.com/singleton/singleton-2/ Sat, 23 Feb 2019 15:47:23 +0000 https://code-maze.com/wp-content/uploads/2019/02/singleton.png 47494 47488 0 0 adapter https://code-maze.com/adapter/ Sat, 02 Mar 2019 07:41:59 +0000 https://code-maze.com/wp-content/uploads/2019/03/adapter.png 47502 0 0 0 01 - Xml document https://code-maze.com/adapter/01-xml-document/ Sat, 02 Mar 2019 17:02:27 +0000 https://code-maze.com/wp-content/uploads/2019/03/01-Xml-document.png 47507 47505 0 0 02 - Final result https://code-maze.com/adapter/02-final-result/ Sat, 02 Mar 2019 17:02:29 +0000 https://code-maze.com/wp-content/uploads/2019/03/02-Final-result.png 47508 47505 0 0 MasteringAngularMaterial400px https://code-maze.com/masteringangularmaterial400px/ Sat, 02 Mar 2019 22:07:17 +0000 https://code-maze.com/wp-content/uploads/2019/03/MasteringAngularMaterial400px.png 47514 0 0 0 composite https://code-maze.com/composite/ Sun, 10 Mar 2019 08:33:14 +0000 https://code-maze.com/wp-content/uploads/2019/03/composite.png 47528 0 0 0 views layouts mvc https://code-maze.com/views-partial-views-and-layouts-in-asp-net-core-mvc/03-views-layouts/ Sun, 10 Mar 2019 08:43:58 +0000 https://code-maze.com/wp-content/uploads/2019/02/03-views-layouts.png 47530 47443 0 0 01-Composit implemented https://code-maze.com/composite/01-composit-implemented/ Sun, 10 Mar 2019 12:31:02 +0000 https://code-maze.com/wp-content/uploads/2019/03/01-Composit-implemented.png 47535 47532 0 0 page without cookie https://code-maze.com/state-management-in-asp-net-core-mvc/page-without-cookie/ Sat, 16 Mar 2019 17:55:55 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-without-cookie.jpg 47553 47552 0 0 page with cookie https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-cookie/ Sat, 16 Mar 2019 17:57:03 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-cookie.jpg 47554 47552 0 0 inspect cookie https://code-maze.com/state-management-in-asp-net-core-mvc/inspect-cookie/ Sat, 16 Mar 2019 17:58:00 +0000 https://code-maze.com/wp-content/uploads/2019/03/inspect-cookie.jpg 47555 47552 0 0 get values from session https://code-maze.com/state-management-in-asp-net-core-mvc/get-values-from-session/ Sat, 16 Mar 2019 18:05:18 +0000 https://code-maze.com/wp-content/uploads/2019/03/get-values-from-session.jpg 47556 47552 0 0 page with query string values https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-query-string-values/ Sat, 16 Mar 2019 18:33:35 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-query-string-values.jpg 47557 47552 0 0 reading hidden field value https://code-maze.com/state-management-in-asp-net-core-mvc/reading-hidden-field-value/ Sat, 16 Mar 2019 18:43:59 +0000 https://code-maze.com/wp-content/uploads/2019/03/reading-hidden-field-value.jpg 47558 47552 0 0 page with viewbag https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-viewbag/ Sat, 16 Mar 2019 19:06:15 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-viewbag.jpg 47560 47552 0 0 page with view data https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-view-data/ Sat, 16 Mar 2019 19:07:46 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-view-data.jpg 47561 47552 0 0 page with valid tempdata https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-valid-tempdata/ Sat, 16 Mar 2019 19:09:20 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-valid-tempdata.jpg 47562 47552 0 0 page with invalid tempdata https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-invalid-tempdata/ Sat, 16 Mar 2019 19:10:01 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-invalid-tempdata.jpg 47563 47552 0 0 page with hidden field value https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-hidden-field-value/ Sat, 16 Mar 2019 19:14:22 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-hidden-field-value.jpg 47564 47552 0 0 01 - navigation active windows https://code-maze.com/visual-studio-productivity/01-navigation-active-windows/ Sun, 17 Mar 2019 07:27:12 +0000 https://code-maze.com/wp-content/uploads/2019/03/01-navigation-active-windows.gif 47574 47573 0 0 02 - navigation active tabs https://code-maze.com/visual-studio-productivity/02-navigation-active-tabs/ Sun, 17 Mar 2019 07:27:15 +0000 https://code-maze.com/wp-content/uploads/2019/03/02-navigation-active-tabs.gif 47575 47573 0 0 03 - Closing current tab https://code-maze.com/visual-studio-productivity/03-closing-current-tab/ Sun, 17 Mar 2019 07:27:18 +0000 https://code-maze.com/wp-content/uploads/2019/03/03-Closing-current-tab.gif 47576 47573 0 0 10-VS Multithread debug https://code-maze.com/visual-studio-productivity/10-vs-multithread-debug/ Sun, 17 Mar 2019 07:27:20 +0000 https://code-maze.com/wp-content/uploads/2019/03/10-VS-Multithread-debug.png 47577 47573 0 0 11 - Immediate window https://code-maze.com/visual-studio-productivity/11-immediate-window/ Sun, 17 Mar 2019 07:27:23 +0000 https://code-maze.com/wp-content/uploads/2019/03/11-Immediate-window.gif 47578 47573 0 0 12 - ctor https://code-maze.com/visual-studio-productivity/12-ctor/ Sun, 17 Mar 2019 07:27:26 +0000 https://code-maze.com/wp-content/uploads/2019/03/12-ctor.gif 47579 47573 0 0 19 - Bookmars Rename Suggestions https://code-maze.com/visual-studio-productivity/19-bookmars-rename-suggestions/ Sun, 17 Mar 2019 07:27:29 +0000 https://code-maze.com/wp-content/uploads/2019/03/19-Bookmars-Rename-Suggestions.gif 47580 47573 0 0 20 - Working with content https://code-maze.com/visual-studio-productivity/20-working-with-content/ Sun, 17 Mar 2019 07:27:32 +0000 https://code-maze.com/wp-content/uploads/2019/03/20-Working-with-content.gif 47581 47573 0 0 04 - Using Class Navigation Menu https://code-maze.com/visual-studio-productivity/04-using-class-navigation-menu/ Sun, 17 Mar 2019 07:29:02 +0000 https://code-maze.com/wp-content/uploads/2019/03/04-Using-Class-Navigation-Menu.gif 47582 47573 0 0 05 - Using GoTo https://code-maze.com/visual-studio-productivity/05-using-goto/ Sun, 17 Mar 2019 07:29:05 +0000 https://code-maze.com/wp-content/uploads/2019/03/05-Using-GoTo.gif 47583 47573 0 0 06 - Definition And Implementation https://code-maze.com/visual-studio-productivity/06-definition-and-implementation/ Sun, 17 Mar 2019 07:29:08 +0000 https://code-maze.com/wp-content/uploads/2019/03/06-Definition-And-Implementation.gif 47584 47573 0 0 07-VS debugging commands https://code-maze.com/visual-studio-productivity/07-vs-debugging-commands/ Sun, 17 Mar 2019 07:29:11 +0000 https://code-maze.com/wp-content/uploads/2019/03/07-VS-debugging-commands.png 47585 47573 0 0 13 - property snippets https://code-maze.com/visual-studio-productivity/13-property-snippets/ Sun, 17 Mar 2019 07:29:50 +0000 https://code-maze.com/wp-content/uploads/2019/03/13-property-snippets.gif 47586 47573 0 0 14 - Conditions TryCatch Using https://code-maze.com/visual-studio-productivity/14-conditions-trycatch-using/ Sun, 17 Mar 2019 07:29:53 +0000 https://code-maze.com/wp-content/uploads/2019/03/14-Conditions-TryCatch-Using.gif 47587 47573 0 0 15 - Loops Console WriteLine https://code-maze.com/visual-studio-productivity/15-loops-console-writeline/ Sun, 17 Mar 2019 07:29:55 +0000 https://code-maze.com/wp-content/uploads/2019/03/15-Loops-Console-WriteLine.gif 47588 47573 0 0 16 - Custom Snippet https://code-maze.com/visual-studio-productivity/16-custom-snippet/ Sun, 17 Mar 2019 07:29:59 +0000 https://code-maze.com/wp-content/uploads/2019/03/16-Custom-Snippet.gif 47589 47573 0 0 17 - New File Outlining Formatting https://code-maze.com/visual-studio-productivity/17-new-file-outlining-formatting/ Sun, 17 Mar 2019 07:30:03 +0000 https://code-maze.com/wp-content/uploads/2019/03/17-New-File-Outlining-Formatting.gif 47590 47573 0 0 18 - Code Extracting https://code-maze.com/visual-studio-productivity/18-code-extracting/ Sun, 17 Mar 2019 07:30:07 +0000 https://code-maze.com/wp-content/uploads/2019/03/18-Code-Extracting.gif 47591 47573 0 0 08 - Stepping and Placeholder https://code-maze.com/visual-studio-productivity/08-stepping-and-placeholder/ Sun, 17 Mar 2019 07:31:30 +0000 https://code-maze.com/wp-content/uploads/2019/03/08-Stepping-and-Placeholder.gif 47592 47573 0 0 09 - Conditional Debugging https://code-maze.com/visual-studio-productivity/09-conditional-debugging/ Sun, 17 Mar 2019 07:31:33 +0000 https://code-maze.com/wp-content/uploads/2019/03/09-Conditional-Debugging.gif 47593 47573 0 0 visual studio productivity https://code-maze.com/visual-studio-productivity/vs-productivity/ Sun, 17 Mar 2019 20:10:15 +0000 https://code-maze.com/wp-content/uploads/2019/03/vs-productivity.png 47606 47573 0 0 mvc state management https://code-maze.com/state-management-in-asp-net-core-mvc/04-state-management/ Mon, 18 Mar 2019 08:07:57 +0000 https://code-maze.com/wp-content/uploads/2019/03/04-state-management.png 47610 47552 0 0 get values from session https://code-maze.com/state-management-in-asp-net-core-mvc/get-values-from-session-2/ Sat, 23 Mar 2019 17:54:48 +0000 https://code-maze.com/wp-content/uploads/2019/03/get-values-from-session-1.jpg 47676 47552 0 0 inspect cookie https://code-maze.com/state-management-in-asp-net-core-mvc/inspect-cookie-2/ Sat, 23 Mar 2019 17:54:51 +0000 https://code-maze.com/wp-content/uploads/2019/03/inspect-cookie-1.jpg 47677 47552 0 0 page with cookie https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-cookie-2/ Sat, 23 Mar 2019 17:54:53 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-cookie-1.jpg 47678 47552 0 0 page with hidden field value https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-hidden-field-value-2/ Sat, 23 Mar 2019 17:54:55 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-hidden-field-value-1.jpg 47679 47552 0 0 page with invalid tempdata https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-invalid-tempdata-2/ Sat, 23 Mar 2019 17:54:57 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-invalid-tempdata-1.jpg 47680 47552 0 0 page with query string values https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-query-string-values-2/ Sat, 23 Mar 2019 17:54:59 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-query-string-values-1.jpg 47681 47552 0 0 page with valid tempdata https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-valid-tempdata-2/ Sat, 23 Mar 2019 17:55:00 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-valid-tempdata-1.jpg 47682 47552 0 0 page with view data https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-view-data-2/ Sat, 23 Mar 2019 17:55:03 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-view-data-1.jpg 47683 47552 0 0 page with viewbag https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-viewbag-2/ Sat, 23 Mar 2019 17:55:05 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-viewbag-1.jpg 47684 47552 0 0 page without cookie https://code-maze.com/state-management-in-asp-net-core-mvc/page-without-cookie-2/ Sat, 23 Mar 2019 17:55:08 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-without-cookie-1.jpg 47685 47552 0 0 reading hidden field value https://code-maze.com/state-management-in-asp-net-core-mvc/reading-hidden-field-value-2/ Sat, 23 Mar 2019 17:55:09 +0000 https://code-maze.com/wp-content/uploads/2019/03/reading-hidden-field-value-1.jpg 47686 47552 0 0 page with valid tempdata https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-valid-tempdata-3/ Sat, 23 Mar 2019 17:56:08 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-valid-tempdata-2.jpg 47687 47552 0 0 page with view data https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-view-data-3/ Sat, 23 Mar 2019 17:56:11 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-view-data-2.jpg 47688 47552 0 0 page with viewbag https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-viewbag-3/ Sat, 23 Mar 2019 17:56:14 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-viewbag-2.jpg 47689 47552 0 0 page without cookie https://code-maze.com/state-management-in-asp-net-core-mvc/page-without-cookie-3/ Sat, 23 Mar 2019 17:56:15 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-without-cookie-2.jpg 47690 47552 0 0 reading hidden field value https://code-maze.com/state-management-in-asp-net-core-mvc/reading-hidden-field-value-3/ Sat, 23 Mar 2019 17:56:17 +0000 https://code-maze.com/wp-content/uploads/2019/03/reading-hidden-field-value-2.jpg 47691 47552 0 0 get values from session https://code-maze.com/state-management-in-asp-net-core-mvc/get-values-from-session-3/ Sat, 23 Mar 2019 17:56:18 +0000 https://code-maze.com/wp-content/uploads/2019/03/get-values-from-session-2.jpg 47692 47552 0 0 inspect cookie https://code-maze.com/state-management-in-asp-net-core-mvc/inspect-cookie-3/ Sat, 23 Mar 2019 17:56:21 +0000 https://code-maze.com/wp-content/uploads/2019/03/inspect-cookie-2.jpg 47693 47552 0 0 page with cookie https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-cookie-3/ Sat, 23 Mar 2019 17:56:24 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-cookie-2.jpg 47694 47552 0 0 page with hidden field value https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-hidden-field-value-3/ Sat, 23 Mar 2019 17:56:25 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-hidden-field-value-2.jpg 47695 47552 0 0 page with invalid tempdata https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-invalid-tempdata-3/ Sat, 23 Mar 2019 17:56:28 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-invalid-tempdata-2.jpg 47696 47552 0 0 page with query string values https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-query-string-values-3/ Sat, 23 Mar 2019 17:56:30 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-query-string-values-2.jpg 47697 47552 0 0 get values from session https://code-maze.com/state-management-in-asp-net-core-mvc/get-values-from-session-4/ Sat, 23 Mar 2019 17:57:57 +0000 https://code-maze.com/wp-content/uploads/2019/03/get-values-from-session-3.jpg 47698 47552 0 0 inspect cookie https://code-maze.com/state-management-in-asp-net-core-mvc/inspect-cookie-4/ Sat, 23 Mar 2019 17:58:00 +0000 https://code-maze.com/wp-content/uploads/2019/03/inspect-cookie-3.jpg 47699 47552 0 0 page with cookie https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-cookie-4/ Sat, 23 Mar 2019 17:58:02 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-cookie-3.jpg 47700 47552 0 0 page with hidden field value https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-hidden-field-value-4/ Sat, 23 Mar 2019 17:58:05 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-hidden-field-value-3.jpg 47701 47552 0 0 page with invalid tempdata https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-invalid-tempdata-4/ Sat, 23 Mar 2019 17:58:07 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-invalid-tempdata-3.jpg 47702 47552 0 0 page with query string values https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-query-string-values-4/ Sat, 23 Mar 2019 17:58:08 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-query-string-values-3.jpg 47703 47552 0 0 page with valid tempdata https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-valid-tempdata-4/ Sat, 23 Mar 2019 17:58:10 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-valid-tempdata-3.jpg 47704 47552 0 0 page with view data https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-view-data-4/ Sat, 23 Mar 2019 17:58:14 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-view-data-3.jpg 47705 47552 0 0 page with viewbag https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-viewbag-4/ Sat, 23 Mar 2019 17:58:15 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-viewbag-3.jpg 47706 47552 0 0 page without cookie https://code-maze.com/state-management-in-asp-net-core-mvc/page-without-cookie-4/ Sat, 23 Mar 2019 17:58:17 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-without-cookie-3.jpg 47707 47552 0 0 reading hidden field value https://code-maze.com/state-management-in-asp-net-core-mvc/reading-hidden-field-value-4/ Sat, 23 Mar 2019 17:58:19 +0000 https://code-maze.com/wp-content/uploads/2019/03/reading-hidden-field-value-3.jpg 47708 47552 0 0 01-Decorator finished https://code-maze.com/decorator-design-pattern/01-decorator-finished/ Sun, 24 Mar 2019 08:24:28 +0000 https://code-maze.com/wp-content/uploads/2019/03/01-Decorator-finished.png 47711 47710 0 0 decorator https://code-maze.com/decorator-design-pattern/decorator/ Sun, 24 Mar 2019 08:44:17 +0000 https://code-maze.com/wp-content/uploads/2019/03/decorator.png 47715 47710 0 0 page without cookie https://code-maze.com/state-management-in-asp-net-core-mvc/page-without-cookie-5/ Sun, 24 Mar 2019 13:50:12 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-without-cookie-4.jpg 47718 47552 0 0 page with viewbag https://code-maze.com/state-management-in-asp-net-core-mvc/page-with-viewbag-5/ Sun, 24 Mar 2019 16:18:17 +0000 https://code-maze.com/wp-content/uploads/2019/03/page-with-viewbag-4.jpg 47720 47552 0 0 01-Command pattern implementation https://code-maze.com/command/01-command-pattern-implementation/ Sun, 31 Mar 2019 07:42:09 +0000 https://code-maze.com/wp-content/uploads/2019/03/01-Command-pattern-implementation.png 47754 47758 0 0 02-Command pattern Undo implementation https://code-maze.com/command/02-command-pattern-undo-implementation/ Sun, 31 Mar 2019 07:42:11 +0000 https://code-maze.com/wp-content/uploads/2019/03/02-Command-pattern-Undo-implementation.png 47755 47758 0 0 03-Command pattern undo flaw https://code-maze.com/command/03-command-pattern-undo-flaw/ Sun, 31 Mar 2019 07:42:12 +0000 https://code-maze.com/wp-content/uploads/2019/03/03-Command-pattern-undo-flaw.png 47756 47758 0 0 04-Command pattern undo fixed https://code-maze.com/command/04-command-pattern-undo-fixed/ Sun, 31 Mar 2019 07:42:13 +0000 https://code-maze.com/wp-content/uploads/2019/03/04-Command-pattern-undo-fixed.png 47757 47758 0 0 command design pattern https://code-maze.com/command/command-2/ Sun, 31 Mar 2019 08:25:20 +0000 https://code-maze.com/wp-content/uploads/2019/04/command.png 47761 47758 0 0 win-acme initial https://code-maze.com/securing-teamcity-https-windows/win-acme-initial/ Sun, 31 Mar 2019 11:17:23 +0000 https://code-maze.com/wp-content/uploads/2019/03/win-acme-initial.png 47770 47766 0 0 securing teamcity letsencrypt https://code-maze.com/securing-teamcity-https-windows/securing-teamcity-letsencrypt/ Sun, 31 Mar 2019 12:37:08 +0000 https://code-maze.com/wp-content/uploads/2019/03/securing-teamcity-letsencrypt.png 47779 47766 0 0 Overview audience april https://code-maze.com/sponsor/overview-audience-april/ Mon, 01 Apr 2019 19:32:50 +0000 https://code-maze.com/wp-content/uploads/2019/04/Overview-audience-april.png 47792 5804 0 0 asp.net core angular https://code-maze.com/start-here/1-06/ Fri, 05 Apr 2019 07:45:04 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-06.png 47831 5755 0 0 csharp https://code-maze.com/start-here/1-01/ Fri, 05 Apr 2019 07:46:07 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-01.png 47832 5755 0 0 http rest https://code-maze.com/start-here/1-11/ Fri, 05 Apr 2019 07:48:17 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-11.png 47833 5755 0 0 design patterns https://code-maze.com/start-here/1-10/ Fri, 05 Apr 2019 07:50:25 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-10.png 47834 5755 0 0 js frameworks https://code-maze.com/start-here/1-12/ Fri, 05 Apr 2019 07:51:18 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-12.png 47835 5755 0 0 best practices https://code-maze.com/start-here/1-09/ Fri, 05 Apr 2019 07:52:32 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-09.png 47836 5755 0 0 docker https://code-maze.com/start-here/1-03/ Fri, 05 Apr 2019 07:54:17 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-03.png 47837 5755 0 0 continuous integration https://code-maze.com/start-here/1-07/ Fri, 05 Apr 2019 07:55:00 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-07.png 47838 5755 0 0 angular stuff https://code-maze.com/start-here/1-02/ Fri, 05 Apr 2019 07:57:54 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-02.png 47839 5755 0 0 angular material https://code-maze.com/start-here/1-05/ Fri, 05 Apr 2019 13:33:04 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-05.png 47867 5755 0 0 1-08 https://code-maze.com/start-here/1-08/ Fri, 05 Apr 2019 16:54:47 +0000 https://code-maze.com/wp-content/uploads/2019/04/1-08.png 47896 5755 0 0 01 - Strategy Pattern Completed https://code-maze.com/strategy/01-strategy-pattern-completed/ Sat, 06 Apr 2019 09:19:33 +0000 https://code-maze.com/wp-content/uploads/2019/04/01-Strategy-Pattern-Completed.png 47928 47926 0 0 strategy https://code-maze.com/strategy/strategy-2/ Sat, 06 Apr 2019 20:05:48 +0000 https://code-maze.com/wp-content/uploads/2019/04/strategy.png 47994 47926 0 0 modified default route https://code-maze.com/routing-asp-net-core-mvc/modified-default-route/ Tue, 09 Apr 2019 17:27:49 +0000 https://code-maze.com/wp-content/uploads/2019/04/modified-default-route.jpg 48124 48120 0 0 upload files screen https://code-maze.com/routing-asp-net-core-mvc/upload-files-screen/ Tue, 09 Apr 2019 17:31:08 +0000 https://code-maze.com/wp-content/uploads/2019/04/upload-files-screen.jpg 48125 48120 0 0 reference fix for the third article https://code-maze.com/net-core-web-development-part3/reference-fix-for-the-third-article/ Wed, 10 Apr 2019 13:52:29 +0000 https://code-maze.com/wp-content/uploads/2018/01/reference-fix-for-the-third-article.png 48137 1164 0 0 01-Starting project structure https://code-maze.com/graphql-aspnetcore-basics/01-starting-project-structure/ Fri, 12 Apr 2019 06:44:34 +0000 https://code-maze.com/wp-content/uploads/2019/04/01-Starting-project-structure.png 48143 48142 0 0 02-GraphQL package https://code-maze.com/graphql-aspnetcore-basics/02-graphql-package/ Fri, 12 Apr 2019 06:44:35 +0000 https://code-maze.com/wp-content/uploads/2019/04/02-GraphQL-package.png 48144 48142 0 0 03-Second package NuGet https://code-maze.com/graphql-aspnetcore-basics/03-second-package-nuget/ Fri, 12 Apr 2019 06:44:36 +0000 https://code-maze.com/wp-content/uploads/2019/04/03-Second-package-NuGet.png 48145 48142 0 0 04-Ui Playground https://code-maze.com/graphql-aspnetcore-basics/04-ui-playground/ Fri, 12 Apr 2019 06:44:37 +0000 https://code-maze.com/wp-content/uploads/2019/04/04-Ui-Playground.png 48146 48142 0 0 05 - GraphQL structure for schema https://code-maze.com/graphql-aspnetcore-basics/05-graphql-structure-for-schema/ Fri, 12 Apr 2019 06:44:38 +0000 https://code-maze.com/wp-content/uploads/2019/04/05-GraphQL-structure-for-schema.png 48147 48142 0 0 posh acme new certificate https://code-maze.com/securing-teamcity-https-windows/posh-acme-new-certificate/ Fri, 12 Apr 2019 10:09:15 +0000 https://code-maze.com/wp-content/uploads/2019/04/posh-acme-new-certificate-.png 48158 47766 0 0 cloudflare api key https://code-maze.com/securing-teamcity-https-windows/cloudflare-api-key/ Fri, 12 Apr 2019 10:30:56 +0000 https://code-maze.com/wp-content/uploads/2019/04/cloudflare-api-key.png 48159 47766 0 0 certificate teamcity https://code-maze.com/securing-teamcity-https-windows/certificate-teamcity/ Fri, 12 Apr 2019 11:04:25 +0000 https://code-maze.com/wp-content/uploads/2019/04/certificate-teamcity.png 48160 47766 0 0 01 graphql getting started https://code-maze.com/graphql-aspnetcore-basics/01-graphql-getting-started/ Sun, 14 Apr 2019 07:34:15 +0000 https://code-maze.com/wp-content/uploads/2019/04/01-graphql-getting-started.png 48171 48142 0 0 06 - First Query https://code-maze.com/graphql-aspnetcore-basics/06-first-query/ Sun, 14 Apr 2019 08:19:30 +0000 https://code-maze.com/wp-content/uploads/2019/04/06-First-Query.gif 48179 48142 0 0 07 - Context.Source https://code-maze.com/advanced-graphql-queries/07-context-source/ Mon, 15 Apr 2019 14:12:25 +0000 https://code-maze.com/wp-content/uploads/2019/04/07-Context.Source.png 48190 48187 0 0 08 - Added accounts to the owner type https://code-maze.com/advanced-graphql-queries/08-added-accounts-to-the-owner-type/ Mon, 15 Apr 2019 14:13:42 +0000 https://code-maze.com/wp-content/uploads/2019/04/08-Added-accounts-to-the-owner-type.gif 48191 48187 0 0 09 - Enumeration type https://code-maze.com/advanced-graphql-queries/09-enumeration-type/ Mon, 15 Apr 2019 14:17:40 +0000 https://code-maze.com/wp-content/uploads/2019/04/09-Enumeration-type.png 48193 48187 0 0 10 - Added enumeration to the query https://code-maze.com/advanced-graphql-queries/10-added-enumeration-to-the-query/ Mon, 15 Apr 2019 14:18:59 +0000 https://code-maze.com/wp-content/uploads/2019/04/10-Added-enumeration-to-the-query.gif 48194 48187 0 0 11 - Not optimized query https://code-maze.com/advanced-graphql-queries/11-not-optimized-query/ Mon, 15 Apr 2019 14:20:24 +0000 https://code-maze.com/wp-content/uploads/2019/04/11-Not-optimized-query.png 48195 48187 0 0 12 - Optimized query https://code-maze.com/advanced-graphql-queries/12-optimized-query/ Mon, 15 Apr 2019 14:20:26 +0000 https://code-maze.com/wp-content/uploads/2019/04/12-Optimized-query.png 48196 48187 0 0 13 - Arguments and Error Handling https://code-maze.com/advanced-graphql-queries/13-arguments-and-error-handling/ Mon, 15 Apr 2019 14:32:17 +0000 https://code-maze.com/wp-content/uploads/2019/04/13-Arguments-and-Error-Handling.gif 48198 48187 0 0 14 - Aliases https://code-maze.com/advanced-graphql-queries/14-aliases/ Mon, 15 Apr 2019 14:32:23 +0000 https://code-maze.com/wp-content/uploads/2019/04/14-Aliases.gif 48199 48187 0 0 15 - Fragments https://code-maze.com/advanced-graphql-queries/15-fragments/ Mon, 15 Apr 2019 14:32:33 +0000 https://code-maze.com/wp-content/uploads/2019/04/15-Fragments.gif 48200 48187 0 0 16 - Named Queries https://code-maze.com/advanced-graphql-queries/16-named-queries/ Mon, 15 Apr 2019 14:32:42 +0000 https://code-maze.com/wp-content/uploads/2019/04/16-Named-Queries.gif 48201 48187 0 0 17 - Directives https://code-maze.com/advanced-graphql-queries/17-directives/ Mon, 15 Apr 2019 14:32:49 +0000 https://code-maze.com/wp-content/uploads/2019/04/17-Directives.gif 48202 48187 0 0 02 graphql https://code-maze.com/advanced-graphql-queries/02-graphql/ Sat, 20 Apr 2019 16:49:55 +0000 https://code-maze.com/wp-content/uploads/2019/04/02-graphql.png 48218 48187 0 0 graphql series cover https://code-maze.com/graphql-asp-net-core-tutorial/graphql-series-cover/ Sat, 20 Apr 2019 16:50:59 +0000 https://code-maze.com/wp-content/uploads/2019/04/graphql-series-cover.png 48219 48139 0 0 18 - Create mutation https://code-maze.com/graphql-mutations/18-create-mutation/ Mon, 22 Apr 2019 04:01:51 +0000 https://code-maze.com/wp-content/uploads/2019/04/18-Create-mutation.png 48228 48225 0 0 20 - Delete mutation https://code-maze.com/graphql-mutations/20-delete-mutation/ Mon, 22 Apr 2019 04:14:26 +0000 https://code-maze.com/wp-content/uploads/2019/04/20-Delete-mutation.png 48231 48225 0 0 19 - Update mutations https://code-maze.com/graphql-mutations/19-update-mutations/ Mon, 22 Apr 2019 04:15:30 +0000 https://code-maze.com/wp-content/uploads/2019/04/19-Update-mutations.png 48232 48225 0 0 21 - GraphQL Client https://code-maze.com/consume-graphql-api-with-asp-net-core/21-graphql-client/ Fri, 26 Apr 2019 06:04:10 +0000 https://code-maze.com/wp-content/uploads/2019/04/21-GraphQL-Client.png 48241 48239 0 0 22 - Query match names https://code-maze.com/consume-graphql-api-with-asp-net-core/22-query-match-names/ Fri, 26 Apr 2019 06:23:32 +0000 https://code-maze.com/wp-content/uploads/2019/04/22-Query-match-names.png 48244 48239 0 0 23 - Postman result https://code-maze.com/consume-graphql-api-with-asp-net-core/23-postman-result/ Fri, 26 Apr 2019 06:27:28 +0000 https://code-maze.com/wp-content/uploads/2019/04/23-Postman-result.png 48245 48239 0 0 03 graphql mutations https://code-maze.com/graphql-mutations/03-graphql-mutations/ Sun, 28 Apr 2019 20:19:10 +0000 https://code-maze.com/wp-content/uploads/2019/04/03-graphql-mutations.png 48262 48225 0 0 consuming graphql https://code-maze.com/consume-graphql-api-with-asp-net-core/04-graphql/ Sun, 05 May 2019 07:42:44 +0000 https://code-maze.com/wp-content/uploads/2019/05/04-graphql.png 48283 48239 0 0 Overview audience may https://code-maze.com/sponsor/overview-audience-may/ Sun, 05 May 2019 08:42:03 +0000 https://code-maze.com/wp-content/uploads/2019/05/Overview-audience-may.png 48285 5804 0 0 24 - Angular query result https://code-maze.com/consuming-graphql-api-angular/24-angular-query-result/ Thu, 09 May 2019 11:37:55 +0000 https://code-maze.com/wp-content/uploads/2019/05/24-Angular-query-result.png 48301 48295 0 0 graphql angular https://code-maze.com/consuming-graphql-api-angular/05-graphql/ Fri, 10 May 2019 20:15:16 +0000 https://code-maze.com/wp-content/uploads/2019/05/05-graphql.png 48309 48295 0 0 book details view page with annotations https://code-maze.com/getting-started-with-asp-net-core-mvc/book-details-view-page-with-annotations-2/ Wed, 22 May 2019 09:21:52 +0000 https://code-maze.com/wp-content/uploads/2019/01/book-details-view-page-with-annotations-1.jpg 48331 5513 0 0 Book create page with validation errors https://code-maze.com/getting-started-with-asp-net-core-mvc/book-create-page-with-validation-errors-2/ Wed, 22 May 2019 09:33:44 +0000 https://code-maze.com/wp-content/uploads/2019/01/Book-create-page-with-validation-errors-1.jpg 48333 5513 0 0 di into view https://code-maze.com/dependency-injection-aspnetcore-mvc/di-into-view/ Sat, 01 Jun 2019 07:33:18 +0000 https://code-maze.com/wp-content/uploads/2019/06/di-into-view.jpg 48349 48347 0 0 add new unit test project https://code-maze.com/dependency-injection-aspnetcore-mvc/add-new-unit-test-project/ Sat, 01 Jun 2019 08:01:49 +0000 https://code-maze.com/wp-content/uploads/2019/06/add-new-unit-test-project.jpg 48350 48347 0 0 test explorer https://code-maze.com/dependency-injection-aspnetcore-mvc/test-explorer/ Sat, 01 Jun 2019 08:02:41 +0000 https://code-maze.com/wp-content/uploads/2019/06/test-explorer.jpg 48351 48347 0 0 Overview audience june https://code-maze.com/sponsor/overview-audience-june/ Sat, 01 Jun 2019 21:04:32 +0000 https://code-maze.com/wp-content/uploads/2019/06/Overview-audience-june.png 48366 5804 0 0 brand-buddy https://code-maze.com/brand-buddy/ Sun, 09 Jun 2019 10:46:32 +0000 https://code-maze.com/wp-content/uploads/2019/06/brand-buddy.svg 48399 0 0 0 mvc routing https://code-maze.com/routing-asp-net-core-mvc/05-routing/ Sun, 23 Jun 2019 13:02:31 +0000 https://code-maze.com/wp-content/uploads/2019/06/05-routing.png 48446 48120 0 0 file upload https://code-maze.com/file-upload-aspnetcore-mvc/06-file-upload/ Sun, 23 Jun 2019 13:04:41 +0000 https://code-maze.com/wp-content/uploads/2019/06/06-file-upload.png 48447 48434 0 0 home-index https://code-maze.com/filters-in-asp-net-core-mvc/home-index/ Mon, 01 Jul 2019 08:11:24 +0000 https://code-maze.com/wp-content/uploads/2019/07/home-index.jpg 48462 48461 0 0 home-read https://code-maze.com/filters-in-asp-net-core-mvc/home-read/ Mon, 01 Jul 2019 08:14:45 +0000 https://code-maze.com/wp-content/uploads/2019/07/home-read.jpg 48463 48461 0 0 home-edit https://code-maze.com/filters-in-asp-net-core-mvc/home-edit/ Mon, 01 Jul 2019 08:16:53 +0000 https://code-maze.com/wp-content/uploads/2019/07/home-edit.jpg 48464 48461 0 0 cached https://code-maze.com/filters-in-asp-net-core-mvc/cached/ Mon, 01 Jul 2019 10:05:57 +0000 https://code-maze.com/wp-content/uploads/2019/07/cached.jpg 48465 48461 0 0 custom-error https://code-maze.com/filters-in-asp-net-core-mvc/custom-error/ Mon, 01 Jul 2019 11:16:15 +0000 https://code-maze.com/wp-content/uploads/2019/07/custom-error.jpg 48467 48461 0 0 model-validate-filter https://code-maze.com/filters-in-asp-net-core-mvc/model-validate-filter/ Mon, 01 Jul 2019 11:25:07 +0000 https://code-maze.com/wp-content/uploads/2019/07/model-validate-filter.jpg 48468 48461 0 0 result-filter https://code-maze.com/filters-in-asp-net-core-mvc/result-filter/ Mon, 01 Jul 2019 11:37:21 +0000 https://code-maze.com/wp-content/uploads/2019/07/result-filter.jpg 48469 48461 0 0 08 unit testing https://code-maze.com/dependency-injection-aspnetcore-mvc/08-unit-testing/ Wed, 03 Jul 2019 21:56:49 +0000 https://code-maze.com/wp-content/uploads/2019/06/08-unit-testing.png 48497 48347 0 0 07 dependency injection https://code-maze.com/dependency-injection-aspnetcore-mvc/07-dependency-injection/ Wed, 03 Jul 2019 21:57:06 +0000 https://code-maze.com/wp-content/uploads/2019/06/07-dependency-injection.png 48498 48347 0 0 09 filters https://code-maze.com/filters-in-asp-net-core-mvc/09-filters/ Wed, 03 Jul 2019 22:01:37 +0000 https://code-maze.com/wp-content/uploads/2019/07/09-filters.png 48499 48461 0 0 cached https://code-maze.com/filters-in-asp-net-core-mvc/cached-2/ Thu, 04 Jul 2019 09:19:34 +0000 https://code-maze.com/wp-content/uploads/2019/07/cached-1.jpg 48502 48461 0 0 01 - ef core series getting started https://code-maze.com/01-ef-core-series-getting-started/ Sat, 27 Jul 2019 20:16:17 +0000 https://code-maze.com/wp-content/uploads/2019/07/01-ef-core-series-getting-started.png 48533 0 0 0 ef core series https://code-maze.com/entity-framework-core-series/ef-core-series/ Sat, 27 Jul 2019 20:16:29 +0000 https://code-maze.com/wp-content/uploads/2019/07/ef-core-series.png 48534 48536 0 0 01-CreateApp https://code-maze.com/getting-started-with-efcore/01-createapp/ Sun, 28 Jul 2019 07:30:15 +0000 https://code-maze.com/wp-content/uploads/2019/07/01-CreateApp.png 48547 48543 0 0 02-Convention primary key config https://code-maze.com/configuring-nonrelational-properties/02-convention-primary-key-config/ Thu, 01 Aug 2019 08:31:21 +0000 https://code-maze.com/wp-content/uploads/2019/08/02-Convention-primary-key-config.png 48559 48558 0 0 03.1-Convention nullability config https://code-maze.com/configuring-nonrelational-properties/03-1-convention-nullability-config/ Thu, 01 Aug 2019 08:31:22 +0000 https://code-maze.com/wp-content/uploads/2019/08/03.1-Convention-nullability-config.png 48560 48558 0 0 03-Convention type config https://code-maze.com/configuring-nonrelational-properties/03-convention-type-config/ Thu, 01 Aug 2019 08:31:23 +0000 https://code-maze.com/wp-content/uploads/2019/08/03-Convention-type-config.png 48561 48558 0 0 04-Data Annotation Validation https://code-maze.com/configuring-nonrelational-properties/04-data-annotation-validation/ Thu, 01 Aug 2019 08:31:23 +0000 https://code-maze.com/wp-content/uploads/2019/08/04-Data-Annotation-Validation.png 48562 48558 0 0 05-Data Annotation Configuration https://code-maze.com/configuring-nonrelational-properties/05-data-annotation-configuration/ Thu, 01 Aug 2019 08:31:24 +0000 https://code-maze.com/wp-content/uploads/2019/08/05-Data-Annotation-Configuration.png 48563 48558 0 0 06-Fluent API same as Data Annotations attributes https://code-maze.com/configuring-nonrelational-properties/06-fluent-api-same-as-data-annotations-attributes/ Thu, 01 Aug 2019 08:31:26 +0000 https://code-maze.com/wp-content/uploads/2019/08/06-Fluent-API-same-as-Data-Annotations-attributes.png 48564 48558 0 0 07-Fluent API default constraint https://code-maze.com/configuring-nonrelational-properties/07-fluent-api-default-constraint/ Thu, 01 Aug 2019 08:31:27 +0000 https://code-maze.com/wp-content/uploads/2019/08/07-Fluent-API-default-constraint.png 48565 48558 0 0 06.1-Fluent API composite keys https://code-maze.com/configuring-nonrelational-properties/06-1-fluent-api-composite-keys/ Fri, 02 Aug 2019 07:02:26 +0000 https://code-maze.com/wp-content/uploads/2019/08/06.1-Fluent-API-composite-keys.png 48572 48558 0 0 02 - ef core series configuring nonrelational properties https://code-maze.com/configuring-nonrelational-properties/02-ef-core-series-configuring-nonrelational-properties/ Sun, 04 Aug 2019 07:19:45 +0000 https://code-maze.com/wp-content/uploads/2019/08/02-ef-core-series-configuring-nonrelational-properties.png 48579 48558 0 0 insert request postman https://code-maze.com/configure-postgresql-ef-core/insert-request-postman/ Tue, 06 Aug 2019 11:31:11 +0000 https://code-maze.com/wp-content/uploads/2019/08/insert-request-postman.png 48588 48586 0 0 pgAdmin result https://code-maze.com/configure-postgresql-ef-core/pgadmin-result/ Tue, 06 Aug 2019 11:34:01 +0000 https://code-maze.com/wp-content/uploads/2019/08/pgAdmin-result.png 48589 48586 0 0 get request postman https://code-maze.com/configure-postgresql-ef-core/get-request-postman/ Tue, 06 Aug 2019 11:38:46 +0000 https://code-maze.com/wp-content/uploads/2019/08/get-request-postman.png 48591 48586 0 0 postgresql ef core https://code-maze.com/configure-postgresql-ef-core/postgresql-ef-core/ Tue, 06 Aug 2019 12:30:53 +0000 https://code-maze.com/wp-content/uploads/2019/08/postgresql-ef-core.png 48597 48586 0 0 08-Migrations folder https://code-maze.com/migrations-and-seed-data-efcore/08-migrations-folder/ Sun, 11 Aug 2019 07:56:48 +0000 https://code-maze.com/wp-content/uploads/2019/08/08-Migrations-folder.png 48602 48600 0 0 09-Migrations Applied https://code-maze.com/migrations-and-seed-data-efcore/09-migrations-applied/ Sun, 11 Aug 2019 08:04:38 +0000 https://code-maze.com/wp-content/uploads/2019/08/09-Migrations-Applied.png 48603 48600 0 0 10-EFCoreMigrations table https://code-maze.com/migrations-and-seed-data-efcore/10-efcoremigrations-table/ Sun, 11 Aug 2019 08:05:47 +0000 https://code-maze.com/wp-content/uploads/2019/08/10-EFCoreMigrations-table.png 48604 48600 0 0 03 - ef core series migrations and seeding https://code-maze.com/03-ef-core-series-migrations-and-seeding/ Sun, 11 Aug 2019 08:06:59 +0000 https://code-maze.com/wp-content/uploads/2019/08/03-ef-core-series-migrations-and-seeding.png 48605 0 0 0 11-New project structure https://code-maze.com/migrations-and-seed-data-efcore/11-new-project-structure/ Sun, 11 Aug 2019 08:21:06 +0000 https://code-maze.com/wp-content/uploads/2019/08/11-New-project-structure.png 48606 48600 0 0 12-Test migration executed https://code-maze.com/migrations-and-seed-data-efcore/12-test-migration-executed/ Sun, 11 Aug 2019 08:25:16 +0000 https://code-maze.com/wp-content/uploads/2019/08/12-Test-migration-executed.png 48607 48600 0 0 13-Removing previous migration https://code-maze.com/migrations-and-seed-data-efcore/13-removing-previous-migration/ Sun, 11 Aug 2019 08:27:50 +0000 https://code-maze.com/wp-content/uploads/2019/08/13-Removing-previous-migration.png 48608 48600 0 0 14-Data seed https://code-maze.com/migrations-and-seed-data-efcore/14-data-seed/ Sun, 11 Aug 2019 08:34:29 +0000 https://code-maze.com/wp-content/uploads/2019/08/14-Data-seed.png 48610 48600 0 0 15-Additional row migration https://code-maze.com/migrations-and-seed-data-efcore/15-additional-row-migration/ Sun, 11 Aug 2019 08:39:25 +0000 https://code-maze.com/wp-content/uploads/2019/08/15-Additional-row-migration.png 48611 48600 0 0 16-One to one relationship https://code-maze.com/efcore-relationships/16-one-to-one-relationship/ Sat, 17 Aug 2019 08:11:50 +0000 https://code-maze.com/wp-content/uploads/2019/08/16-One-to-one-relationship.png 48624 48622 0 0 17-One to many relationship convention optional relatiion https://code-maze.com/efcore-relationships/17-one-to-many-relationship-convention-optional-relatiion/ Sat, 17 Aug 2019 08:11:51 +0000 https://code-maze.com/wp-content/uploads/2019/08/17-One-to-many-relationship-convention-optional-relatiion.png 48625 48622 0 0 17-One to many relationship convention required relatiion https://code-maze.com/efcore-relationships/17-one-to-many-relationship-convention-required-relatiion/ Sat, 17 Aug 2019 08:11:52 +0000 https://code-maze.com/wp-content/uploads/2019/08/17-One-to-many-relationship-convention-required-relatiion.png 48626 48622 0 0 18-Many to many relationship https://code-maze.com/efcore-relationships/18-many-to-many-relationship/ Sat, 17 Aug 2019 08:11:53 +0000 https://code-maze.com/wp-content/uploads/2019/08/18-Many-to-many-relationship.png 48627 48622 0 0 19-Cascade delete by default https://code-maze.com/efcore-relationships/19-cascade-delete-by-default/ Sat, 17 Aug 2019 08:11:54 +0000 https://code-maze.com/wp-content/uploads/2019/08/19-Cascade-delete-by-default.png 48628 48622 0 0 20-Restrict delete https://code-maze.com/efcore-relationships/20-restrict-delete/ Sat, 17 Aug 2019 08:11:55 +0000 https://code-maze.com/wp-content/uploads/2019/08/20-Restrict-delete.png 48629 48622 0 0 04 - ef core series relationships https://code-maze.com/efcore-relationships/04-ef-core-series-relationships/ Sun, 18 Aug 2019 12:15:16 +0000 https://code-maze.com/wp-content/uploads/2019/08/04-ef-core-series-relationships.png 48635 48622 0 0 CreatProj https://code-maze.com/differences-between-net-framework-net-core-and-net-standard/creatproj/ Thu, 22 Aug 2019 21:52:25 +0000 https://code-maze.com/wp-content/uploads/2019/08/CreatProj.png 48650 48647 0 0 Standard https://code-maze.com/differences-between-net-framework-net-core-and-net-standard/standard/ Thu, 22 Aug 2019 21:52:29 +0000 https://code-maze.com/wp-content/uploads/2019/08/Standard.png 48651 48647 0 0 StandardConfusion https://code-maze.com/differences-between-net-framework-net-core-and-net-standard/standardconfusion/ Thu, 22 Aug 2019 21:52:33 +0000 https://code-maze.com/wp-content/uploads/2019/08/StandardConfusion.png 48652 48647 0 0 21-First Query https://code-maze.com/queries-in-entity-framework-core/21-first-query/ Fri, 23 Aug 2019 09:35:08 +0000 https://code-maze.com/wp-content/uploads/2019/08/21-First-Query.png 48671 48669 0 0 22-Include in Eager Loading https://code-maze.com/queries-in-entity-framework-core/22-include-in-eager-loading/ Fri, 23 Aug 2019 09:38:28 +0000 https://code-maze.com/wp-content/uploads/2019/08/22-Include-in-Eager-Loading.png 48673 48669 0 0 23-Include in Eager Loading Translated query https://code-maze.com/queries-in-entity-framework-core/23-include-in-eager-loading-translated-query/ Fri, 23 Aug 2019 09:39:10 +0000 https://code-maze.com/wp-content/uploads/2019/08/23-Include-in-Eager-Loading-Translated-query.png 48674 48669 0 0 24-ThenInclude in Eager Loading https://code-maze.com/queries-in-entity-framework-core/24-theninclude-in-eager-loading/ Fri, 23 Aug 2019 09:43:42 +0000 https://code-maze.com/wp-content/uploads/2019/08/24-ThenInclude-in-Eager-Loading.png 48675 48669 0 0 25-Ef Core Include Warning https://code-maze.com/queries-in-entity-framework-core/25-ef-core-include-warning/ Fri, 23 Aug 2019 09:46:11 +0000 https://code-maze.com/wp-content/uploads/2019/08/25-Ef-Core-Include-Warning.png 48676 48669 0 0 26-Client vs Server validation https://code-maze.com/queries-in-entity-framework-core/26-client-vs-server-validation/ Sat, 24 Aug 2019 06:24:05 +0000 https://code-maze.com/wp-content/uploads/2019/08/26-Client-vs-Server-validation.png 48690 48669 0 0 27-Client vs Server validation result https://code-maze.com/queries-in-entity-framework-core/27-client-vs-server-validation-result/ Sat, 24 Aug 2019 06:24:43 +0000 https://code-maze.com/wp-content/uploads/2019/08/27-Client-vs-Server-validation-result.png 48691 48669 0 0 28-ExecuteSqlCommand https://code-maze.com/queries-in-entity-framework-core/28-executesqlcommand/ Sat, 24 Aug 2019 06:29:39 +0000 https://code-maze.com/wp-content/uploads/2019/08/28-ExecuteSqlCommand.png 48692 48669 0 0 29-ExecuteSqlCommand not changed local entity https://code-maze.com/queries-in-entity-framework-core/29-executesqlcommand-not-changed-local-entity/ Sat, 24 Aug 2019 06:34:00 +0000 https://code-maze.com/wp-content/uploads/2019/08/29-ExecuteSqlCommand-not-changed-local-entity.png 48693 48669 0 0 30-Reload method https://code-maze.com/queries-in-entity-framework-core/30-reload-method/ Sat, 24 Aug 2019 06:35:40 +0000 https://code-maze.com/wp-content/uploads/2019/08/30-Reload-method.png 48694 48669 0 0 05 - ef core series database queries https://code-maze.com/queries-in-entity-framework-core/05-ef-core-series-database-queries/ Sun, 25 Aug 2019 10:21:08 +0000 https://code-maze.com/wp-content/uploads/2019/08/05-ef-core-series-database-queries.png 48702 48669 0 0 31-Returned entity after adding https://code-maze.com/efcore-modifying-data/31-returned-entity-after-adding/ Thu, 29 Aug 2019 10:56:12 +0000 https://code-maze.com/wp-content/uploads/2019/08/31-Returned-entity-after-adding.png 48713 48711 0 0 32-Created entity in db https://code-maze.com/efcore-modifying-data/32-created-entity-in-db/ Thu, 29 Aug 2019 10:56:13 +0000 https://code-maze.com/wp-content/uploads/2019/08/32-Created-entity-in-db.png 48714 48711 0 0 33-States for Add method https://code-maze.com/efcore-modifying-data/33-states-for-add-method/ Thu, 29 Aug 2019 10:56:14 +0000 https://code-maze.com/wp-content/uploads/2019/08/33-States-for-Add-method.png 48715 48711 0 0 34-Add method with relationship https://code-maze.com/efcore-modifying-data/34-add-method-with-relationship/ Thu, 29 Aug 2019 10:56:15 +0000 https://code-maze.com/wp-content/uploads/2019/08/34-Add-method-with-relationship.png 48716 48711 0 0 35-Add method with relationship response https://code-maze.com/efcore-modifying-data/35-add-method-with-relationship-response/ Thu, 29 Aug 2019 10:56:16 +0000 https://code-maze.com/wp-content/uploads/2019/08/35-Add-method-with-relationship-response.png 48717 48711 0 0 36-Update actions connected https://code-maze.com/efcore-modifying-data/36-update-actions-connected/ Thu, 29 Aug 2019 10:56:17 +0000 https://code-maze.com/wp-content/uploads/2019/08/36-Update-actions-connected.png 48718 48711 0 0 37-IsModified property https://code-maze.com/efcore-modifying-data/37-ismodified-property/ Thu, 29 Aug 2019 10:56:18 +0000 https://code-maze.com/wp-content/uploads/2019/08/37-IsModified-property.png 48719 48711 0 0 38-Update relationships translated commands https://code-maze.com/efcore-modifying-data/38-update-relationships-translated-commands/ Thu, 29 Aug 2019 10:56:18 +0000 https://code-maze.com/wp-content/uploads/2019/08/38-Update-relationships-translated-commands.png 48720 48711 0 0 39-Update relationships sql result https://code-maze.com/efcore-modifying-data/39-update-relationships-sql-result/ Thu, 29 Aug 2019 10:56:20 +0000 https://code-maze.com/wp-content/uploads/2019/08/39-Update-relationships-sql-result.png 48721 48711 0 0 40-Update relationships sql result for insert https://code-maze.com/efcore-modifying-data/40-update-relationships-sql-result-for-insert/ Thu, 29 Aug 2019 10:56:21 +0000 https://code-maze.com/wp-content/uploads/2019/08/40-Update-relationships-sql-result-for-insert.png 48722 48711 0 0 41-Disconnected update command https://code-maze.com/efcore-modifying-data/41-disconnected-update-command/ Thu, 29 Aug 2019 10:56:22 +0000 https://code-maze.com/wp-content/uploads/2019/08/41-Disconnected-update-command.png 48723 48711 0 0 42-Soft delete sql result https://code-maze.com/efcore-modifying-data/42-soft-delete-sql-result/ Thu, 29 Aug 2019 10:56:23 +0000 https://code-maze.com/wp-content/uploads/2019/08/42-Soft-delete-sql-result.png 48724 48711 0 0 43-HasQueryFilterImplemented https://code-maze.com/efcore-modifying-data/43-hasqueryfilterimplemented/ Thu, 29 Aug 2019 10:56:23 +0000 https://code-maze.com/wp-content/uploads/2019/08/43-HasQueryFilterImplemented.png 48725 48711 0 0 44-IgnoreQueryFIlter Implemented https://code-maze.com/efcore-modifying-data/44-ignorequeryfilter-implemented/ Thu, 29 Aug 2019 10:56:25 +0000 https://code-maze.com/wp-content/uploads/2019/08/44-IgnoreQueryFIlter-Implemented.png 48726 48711 0 0 45-Delete Single Entity https://code-maze.com/efcore-modifying-data/45-delete-single-entity/ Thu, 29 Aug 2019 10:56:26 +0000 https://code-maze.com/wp-content/uploads/2019/08/45-Delete-Single-Entity.png 48727 48711 0 0 46-Delete Relationships https://code-maze.com/efcore-modifying-data/46-delete-relationships/ Thu, 29 Aug 2019 10:56:27 +0000 https://code-maze.com/wp-content/uploads/2019/08/46-Delete-Relationships-.png 48728 48711 0 0 efcore-series-ModifyingData https://code-maze.com/efcore-modifying-data/efcore-series-modifyingdata/ Mon, 02 Sep 2019 17:15:58 +0000 https://code-maze.com/wp-content/uploads/2019/09/efcore-series-ModifyingData.png 48742 48711 0 0 01 unit testing xunit https://code-maze.com/01-unit-testing-xunit/ Sun, 15 Sep 2019 16:21:03 +0000 https://code-maze.com/wp-content/uploads/2019/09/01-unit-testing-xunit.png 48762 0 0 0 Testing ASP.NET Core MVC Series https://code-maze.com/asp-net-core-mvc-testing/testing-asp-net-core-mvc-series/ Sun, 15 Sep 2019 16:21:08 +0000 https://code-maze.com/wp-content/uploads/2019/09/Testing-ASP.NET-Core-MVC-Series.png 48763 48760 0 0 01-Project overview https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/01-project-overview/ Sun, 15 Sep 2019 16:46:24 +0000 https://code-maze.com/wp-content/uploads/2019/09/01-Project-overview.png 48768 48759 0 0 02-xUnit Project creation https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/02-xunit-project-creation/ Sun, 15 Sep 2019 16:46:25 +0000 https://code-maze.com/wp-content/uploads/2019/09/02-xUnit-Project-creation.png 48769 48759 0 0 03-xUnit library installed https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/03-xunit-library-installed/ Sun, 15 Sep 2019 16:46:26 +0000 https://code-maze.com/wp-content/uploads/2019/09/03-xUnit-library-installed.png 48770 48759 0 0 04-xUnit project structure https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/04-xunit-project-structure/ Sun, 15 Sep 2019 16:46:28 +0000 https://code-maze.com/wp-content/uploads/2019/09/04-xUnit-project-structure.png 48771 48759 0 0 05-First test result https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/05-first-test-result/ Sun, 15 Sep 2019 16:46:29 +0000 https://code-maze.com/wp-content/uploads/2019/09/05-First-test-result.png 48772 48759 0 0 06-First test result passes in the code https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/06-first-test-result-passes-in-the-code/ Sun, 15 Sep 2019 16:46:30 +0000 https://code-maze.com/wp-content/uploads/2019/09/06-First-test-result-passes-in-the-code.png 48773 48759 0 0 07-First account part wrong test passes https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/07-first-account-part-wrong-test-passes/ Sun, 15 Sep 2019 16:46:31 +0000 https://code-maze.com/wp-content/uploads/2019/09/07-First-account-part-wrong-test-passes.png 48774 48759 0 0 08-Theory attribute in tests https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/08-theory-attribute-in-tests/ Sun, 15 Sep 2019 16:46:32 +0000 https://code-maze.com/wp-content/uploads/2019/09/08-Theory-attribute-in-tests.png 48775 48759 0 0 09-Additional tests https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/09-additional-tests/ Sun, 15 Sep 2019 16:46:34 +0000 https://code-maze.com/wp-content/uploads/2019/09/09-Additional-tests.png 48776 48759 0 0 10-Last tests fail https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/10-last-tests-fail/ Sun, 15 Sep 2019 16:46:36 +0000 https://code-maze.com/wp-content/uploads/2019/09/10-Last-tests-fail.png 48777 48759 0 0 11-Last test passes https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/11-last-test-passes/ Sun, 15 Sep 2019 16:46:38 +0000 https://code-maze.com/wp-content/uploads/2019/09/11-Last-test-passes.png 48778 48759 0 0 02 testing controllers https://code-maze.com/02-testing-controllers/ Sat, 21 Sep 2019 11:41:06 +0000 https://code-maze.com/wp-content/uploads/2019/09/02-testing-controllers.png 48795 0 0 0 12-Controllers constructor https://code-maze.com/testing-mvc-controllers-asp-net-core/12-controllers-constructor/ Sat, 21 Sep 2019 16:44:20 +0000 https://code-maze.com/wp-content/uploads/2019/09/12-Controllers-constructor.png 48797 48796 0 0 13-Installing moq https://code-maze.com/testing-mvc-controllers-asp-net-core/13-installing-moq/ Sat, 21 Sep 2019 16:44:21 +0000 https://code-maze.com/wp-content/uploads/2019/09/13-Installing-moq.png 48798 48796 0 0 14-Added new class file https://code-maze.com/testing-mvc-controllers-asp-net-core/14-added-new-class-file/ Sat, 21 Sep 2019 16:44:22 +0000 https://code-maze.com/wp-content/uploads/2019/09/14-Added-new-class-file.png 48799 48796 0 0 15-Additional libraries https://code-maze.com/testing-mvc-controllers-asp-net-core/15-additional-libraries/ Sat, 21 Sep 2019 16:44:23 +0000 https://code-maze.com/wp-content/uploads/2019/09/15-Additional-libraries.png 48800 48796 0 0 16-Index of type ViewResult test passes https://code-maze.com/testing-mvc-controllers-asp-net-core/16-index-of-type-viewresult-test-passes/ Sat, 21 Sep 2019 16:44:24 +0000 https://code-maze.com/wp-content/uploads/2019/09/16-Index-of-type-ViewResult-test-passes.png 48801 48796 0 0 17-Index returns exact number of employees https://code-maze.com/testing-mvc-controllers-asp-net-core/17-index-returns-exact-number-of-employees/ Sat, 21 Sep 2019 16:44:25 +0000 https://code-maze.com/wp-content/uploads/2019/09/17-Index-returns-exact-number-of-employees.png 48802 48796 0 0 18-Create action returns view https://code-maze.com/testing-mvc-controllers-asp-net-core/18-create-action-returns-view/ Sat, 21 Sep 2019 16:44:26 +0000 https://code-maze.com/wp-content/uploads/2019/09/18-Create-action-returns-view.png 48803 48796 0 0 19-Create action Invalid Model State https://code-maze.com/testing-mvc-controllers-asp-net-core/19-create-action-invalid-model-state/ Sat, 21 Sep 2019 16:44:28 +0000 https://code-maze.com/wp-content/uploads/2019/09/19-Create-action-Invalid-Model-State.png 48804 48796 0 0 20-Create action Invalid Model State CreateEmployee never executes https://code-maze.com/testing-mvc-controllers-asp-net-core/20-create-action-invalid-model-state-createemployee-never-executes/ Sat, 21 Sep 2019 16:44:29 +0000 https://code-maze.com/wp-content/uploads/2019/09/20-Create-action-Invalid-Model-State-CreateEmployee-never-executes.png 48805 48796 0 0 21-Create action Invalid Model State CreateEmployee never executes test fail https://code-maze.com/testing-mvc-controllers-asp-net-core/21-create-action-invalid-model-state-createemployee-never-executes-test-fail/ Sat, 21 Sep 2019 16:44:31 +0000 https://code-maze.com/wp-content/uploads/2019/09/21-Create-action-Invalid-Model-State-CreateEmployee-never-executes-test-fail.png 48806 48796 0 0 22-Create action Valid Model State CreateEmployee called once https://code-maze.com/testing-mvc-controllers-asp-net-core/22-create-action-valid-model-state-createemployee-called-once/ Sat, 21 Sep 2019 16:44:33 +0000 https://code-maze.com/wp-content/uploads/2019/09/22-Create-action-Valid-Model-State-CreateEmployee-called-once.png 48807 48796 0 0 run the app https://code-maze.com/automapper-net-core/run-the-app/ Sun, 22 Sep 2019 14:20:51 +0000 https://code-maze.com/wp-content/uploads/2019/09/run-the-app.jpg 48818 48816 0 0 run the app https://code-maze.com/automapper-net-core/run-the-app-2/ Sun, 22 Sep 2019 15:20:52 +0000 https://code-maze.com/wp-content/uploads/2019/09/run-the-app-1.jpg 48821 48816 0 0 run the app https://code-maze.com/automapper-net-core/run-the-app-3/ Sun, 22 Sep 2019 17:32:32 +0000 https://code-maze.com/wp-content/uploads/2019/09/run-the-app-2.jpg 48824 48816 0 0 run the app with complex type mapping https://code-maze.com/automapper-net-core/rrun-the-app-with-complex-type-mapping/ Sun, 22 Sep 2019 17:33:20 +0000 https://code-maze.com/wp-content/uploads/2019/09/Rrun-the-app-with-complex-type-mapping.jpg 48825 48816 0 0 profile_image-125x125.png https://code-maze.com/profile_image-125x125-png/ Mon, 23 Sep 2019 21:33:12 +0000 https://code-maze.com/wp-content/uploads/2019/09/profile_image-125x125.png 48842 0 0 0 marinko profile.jpg https://code-maze.com/marinko-profile-jpg/ Mon, 23 Sep 2019 21:49:38 +0000 https://code-maze.com/wp-content/uploads/2019/09/marinko-profile.jpg 48844 0 0 0 DSC_9468-2.jpg https://code-maze.com/testimonial/you-are-at-the-right-place/dsc_9468-2-jpg/ Tue, 24 Sep 2019 13:25:01 +0000 https://code-maze.com/wp-content/uploads/2019/09/DSC_9468-2.jpg 48862 48861 0 0 darran jones https://code-maze.com/testimonial/great-resource-for-net-developers/darran-jpg/ Fri, 27 Sep 2019 09:35:14 +0000 https://code-maze.com/wp-content/uploads/2019/09/darran.jpg 48883 48882 0 0 Marie Jacksman Profile https://code-maze.com/testimonial/source-for-golden-information/dsc_4759/ Fri, 27 Sep 2019 16:33:34 +0000 https://code-maze.com/wp-content/uploads/2019/09/DSC_4759.jpg 48888 48864 0 0 red hair hat male https://code-maze.com/testimonial/great-series/red-hair-hat-male/ Fri, 27 Sep 2019 16:47:11 +0000 https://code-maze.com/wp-content/uploads/2019/09/red-hair-hat-male.png 48889 48884 0 0 headphones green shirt male https://code-maze.com/testimonial/ef-core-feedback/headphones-green-shirt-male/ Fri, 27 Sep 2019 16:48:10 +0000 https://code-maze.com/wp-content/uploads/2019/09/headphones-green-shirt-male.png 48890 48885 0 0 deepak singh https://code-maze.com/testimonial/loved-your-work/deepak-singh/ Fri, 27 Sep 2019 16:51:51 +0000 https://code-maze.com/wp-content/uploads/2019/09/deepak-singh.jpg 48892 48887 0 0 24560296.jpg https://code-maze.com/testimonial/hidden-gem-in-the-net-world/24560296-jpg/ Fri, 27 Sep 2019 18:20:57 +0000 https://code-maze.com/wp-content/uploads/2019/09/24560296.jpg 48896 48895 0 0 moustache guy male https://code-maze.com/testimonial/the-most-awesome-code-blog-in-c-world-perhaps-i-dare-say-in-programming/moustache-guy-male/ Fri, 27 Sep 2019 19:50:11 +0000 https://code-maze.com/wp-content/uploads/2019/09/moustache-guy-male.png 48899 48897 0 0 simple male https://code-maze.com/testimonial/best-c-resource/simple-male/ Sat, 28 Sep 2019 08:59:30 +0000 https://code-maze.com/wp-content/uploads/2019/09/simple-male.png 48901 48900 0 0 03 integration testing https://code-maze.com/03-integration-testing/ Sun, 29 Sep 2019 13:12:20 +0000 https://code-maze.com/wp-content/uploads/2019/09/03-integration-testing.png 48902 0 0 0 23-Integration tests project https://code-maze.com/integration-testing-asp-net-core-mvc/23-integration-tests-project/ Sun, 29 Sep 2019 14:09:23 +0000 https://code-maze.com/wp-content/uploads/2019/09/23-Integration-tests-project.png 48904 48903 0 0 24-Additional references and packages https://code-maze.com/integration-testing-asp-net-core-mvc/24-additional-references-and-packages/ Sun, 29 Sep 2019 14:09:24 +0000 https://code-maze.com/wp-content/uploads/2019/09/24-Additional-references-and-packages.png 48905 48903 0 0 25-IsSuccessStatusCode https://code-maze.com/integration-testing-asp-net-core-mvc/25-issuccessstatuscode/ Sun, 29 Sep 2019 14:09:25 +0000 https://code-maze.com/wp-content/uploads/2019/09/25-IsSuccessStatusCode.png 48906 48903 0 0 26-Index integration test pass https://code-maze.com/integration-testing-asp-net-core-mvc/26-index-integration-test-pass/ Sun, 29 Sep 2019 14:09:26 +0000 https://code-maze.com/wp-content/uploads/2019/09/26-Index-integration-test-pass.png 48907 48903 0 0 27-Create integration test pass https://code-maze.com/integration-testing-asp-net-core-mvc/27-create-integration-test-pass/ Sun, 29 Sep 2019 14:09:27 +0000 https://code-maze.com/wp-content/uploads/2019/09/27-Create-integration-test-pass.png 48908 48903 0 0 28-Post integration test fail due to Antiforgery https://code-maze.com/integration-testing-asp-net-core-mvc/28-post-integration-test-fail-due-to-antiforgery/ Sun, 29 Sep 2019 14:09:29 +0000 https://code-maze.com/wp-content/uploads/2019/09/28-Post-integration-test-fail-due-to-Antiforgery.png 48909 48903 0 0 29-Commenting anti-forgery token validation attribute https://code-maze.com/integration-testing-asp-net-core-mvc/29-commenting-anti-forgery-token-validation-attribute/ Sun, 29 Sep 2019 14:09:31 +0000 https://code-maze.com/wp-content/uploads/2019/09/29-Commenting-anti-forgery-token-validation-attribute.png 48910 48903 0 0 30-Post integration test passes https://code-maze.com/integration-testing-asp-net-core-mvc/30-post-integration-test-passes/ Sun, 29 Sep 2019 14:09:32 +0000 https://code-maze.com/wp-content/uploads/2019/09/30-Post-integration-test-passes.png 48911 48903 0 0 30-The Last Post integration test passes https://code-maze.com/integration-testing-asp-net-core-mvc/30-the-last-post-integration-test-passes/ Sun, 29 Sep 2019 14:28:31 +0000 https://code-maze.com/wp-content/uploads/2019/09/30-The-Last-Post-integration-test-passes.png 48912 48903 0 0 postman response headers paging https://code-maze.com/paging-aspnet-core-webapi/postman-response-headers-paging/ Tue, 01 Oct 2019 11:00:29 +0000 https://code-maze.com/wp-content/uploads/2019/09/postman-response-headers-paging.png 48926 48879 0 0 Goran-Todorovic.png https://code-maze.com/testimonial/excellent-and-up-to-date-blog/goran-todorovic-png/ Tue, 01 Oct 2019 13:19:23 +0000 https://code-maze.com/wp-content/uploads/2019/10/Goran-Todorovic.png 48931 48930 0 0 01 paging asp net core web api https://code-maze.com/paging-aspnet-core-webapi/01-paging/ Tue, 01 Oct 2019 13:34:00 +0000 https://code-maze.com/wp-content/uploads/2019/09/01-paging.png 48933 48879 0 0 goatie male https://code-maze.com/testimonial/kindly-start-video-tutorials/goatie-male/ Wed, 02 Oct 2019 20:29:26 +0000 https://code-maze.com/wp-content/uploads/2019/10/goatie-male.png 48947 48945 0 0 grey hair male https://code-maze.com/testimonial/perfect-stop/grey-hair-male/ Fri, 04 Oct 2019 06:28:00 +0000 https://code-maze.com/wp-content/uploads/2019/10/grey-hair-male.png 48951 48950 0 0 database owners https://code-maze.com/filtering-aspnet-core-webapi/database-owners/ Fri, 04 Oct 2019 09:26:33 +0000 https://code-maze.com/wp-content/uploads/2019/10/database-owners.png 48954 48937 0 0 filtering asp net core web api https://code-maze.com/filtering-aspnet-core-webapi/02-filtering/ Fri, 04 Oct 2019 09:49:18 +0000 https://code-maze.com/wp-content/uploads/2019/10/02-filtering.png 48957 48937 0 0 search result https://code-maze.com/searching-aspnet-core-webapi/search-result/ Fri, 04 Oct 2019 19:31:56 +0000 https://code-maze.com/wp-content/uploads/2019/10/search-result.png 48966 48963 0 0 searching asp net core web api https://code-maze.com/searching-aspnet-core-webapi/03-searching/ Fri, 04 Oct 2019 19:43:53 +0000 https://code-maze.com/wp-content/uploads/2019/10/03-searching.png 48967 48963 0 0 04 integration testing with antiforgerytoken https://code-maze.com/04-integration-testing-with-antiforgerytoken/ Sun, 06 Oct 2019 08:51:25 +0000 https://code-maze.com/wp-content/uploads/2019/10/04-integration-testing-with-antiforgerytoken.png 48974 0 0 0 31-Cookie value in the response https://code-maze.com/testing-anti-forgery-token-asp-net-core-mvc/31-cookie-value-in-the-response/ Sun, 06 Oct 2019 15:59:53 +0000 https://code-maze.com/wp-content/uploads/2019/10/31-Cookie-value-in-the-response.png 48979 48976 0 0 32-Field value in the response https://code-maze.com/testing-anti-forgery-token-asp-net-core-mvc/32-field-value-in-the-response/ Sun, 06 Oct 2019 15:59:54 +0000 https://code-maze.com/wp-content/uploads/2019/10/32-Field-value-in-the-response.png 48980 48976 0 0 33-Both Integration POST tests pass https://code-maze.com/testing-anti-forgery-token-asp-net-core-mvc/33-both-integration-post-tests-pass/ Sun, 06 Oct 2019 15:59:56 +0000 https://code-maze.com/wp-content/uploads/2019/10/33-Both-Integration-POST-tests-pass.png 48981 48976 0 0 long moustache male https://code-maze.com/testimonial/best-explaination/long-moustache-male/ Wed, 09 Oct 2019 19:42:33 +0000 https://code-maze.com/wp-content/uploads/2019/10/long-moustache-male.png 48996 48994 0 0 database owners extended https://code-maze.com/sorting-aspnet-core-webapi/database-owners-extended/ Fri, 11 Oct 2019 16:59:53 +0000 https://code-maze.com/wp-content/uploads/2019/10/database-owners-extended.png 49008 48970 0 0 sorting asp net core web api https://code-maze.com/sorting-aspnet-core-webapi/04-sorting/ Fri, 11 Oct 2019 21:28:15 +0000 https://code-maze.com/wp-content/uploads/2019/10/04-sorting.png 49015 48970 0 0 profile.jpg https://code-maze.com/testimonial/extraordinary-net-and-c-source-of-code-and-knowledge/profile-jpg/ Sat, 12 Oct 2019 18:25:49 +0000 https://code-maze.com/wp-content/uploads/2019/10/profile.jpg 49027 49026 0 0 05 ui testing with selenium https://code-maze.com/05-ui-testing-with-selenium/ Sun, 13 Oct 2019 09:50:42 +0000 https://code-maze.com/wp-content/uploads/2019/10/05-ui-testing-with-selenium.png 49031 0 0 0 34-Automated UI tests project created https://code-maze.com/automatic-ui-testing-selenium-asp-net-core-mvc/34-automated-ui-tests-project-created/ Sun, 13 Oct 2019 10:09:59 +0000 https://code-maze.com/wp-content/uploads/2019/10/34-Automated-UI-tests-project-created.png 49033 49032 0 0 35-Selenium installed https://code-maze.com/automatic-ui-testing-selenium-asp-net-core-mvc/35-selenium-installed/ Sun, 13 Oct 2019 10:10:00 +0000 https://code-maze.com/wp-content/uploads/2019/10/35-Selenium-installed.png 49034 49032 0 0 36-First UI test pass https://code-maze.com/automatic-ui-testing-selenium-asp-net-core-mvc/36-first-ui-test-pass/ Sun, 13 Oct 2019 10:10:01 +0000 https://code-maze.com/wp-content/uploads/2019/10/36-First-UI-test-pass.png 49035 49032 0 0 37-Second UI test fail https://code-maze.com/automatic-ui-testing-selenium-asp-net-core-mvc/37-second-ui-test-fail/ Sun, 13 Oct 2019 10:10:02 +0000 https://code-maze.com/wp-content/uploads/2019/10/37-Second-UI-test-fail.png 49036 49032 0 0 38-Second UI test passes https://code-maze.com/automatic-ui-testing-selenium-asp-net-core-mvc/38-second-ui-test-passes/ Sun, 13 Oct 2019 10:10:03 +0000 https://code-maze.com/wp-content/uploads/2019/10/38-Second-UI-test-passes.png 49037 49032 0 0 39-The last UI test passes https://code-maze.com/automatic-ui-testing-selenium-asp-net-core-mvc/39-the-last-ui-test-passes/ Sun, 13 Oct 2019 10:10:04 +0000 https://code-maze.com/wp-content/uploads/2019/10/39-The-last-UI-test-passes.png 49038 49032 0 0 data shaping asp net core web api https://code-maze.com/data-shaping-aspnet-core-webapi/05-data-shaping/ Mon, 14 Oct 2019 14:43:48 +0000 https://code-maze.com/wp-content/uploads/2019/10/05-data-shaping.png 49055 49017 0 0 man red glasses https://code-maze.com/testimonial/the-articles-are-really-nice-and-helpfull/man-red-glasses/ Sun, 20 Oct 2019 08:32:09 +0000 https://code-maze.com/wp-content/uploads/2019/10/man-red-glasses.png 49071 49067 0 0 netcore netframework netstandard https://code-maze.com/differences-between-net-framework-net-core-and-net-standard/netcore-netframework-netstandard/ Sun, 20 Oct 2019 18:10:22 +0000 https://code-maze.com/wp-content/uploads/2019/08/netcore-netframework-netstandard.png 49080 48647 0 0 man hippie hair https://code-maze.com/testimonial/code-maze-is-an-amazing-site/man-hippie-hair/ Sun, 20 Oct 2019 18:17:39 +0000 https://code-maze.com/wp-content/uploads/2019/10/man-hippie-hair.png 49082 49076 0 0 ef core migration add migration https://code-maze.com/net-core-web-api-ef-core-code-first/ef-core-migration-add-migration/ Thu, 24 Oct 2019 08:07:00 +0000 https://code-maze.com/wp-content/uploads/2018/10/ef-core-migration-add-migration.png 49085 4467 0 0 automapper-aspnet-core https://code-maze.com/automapper-net-core/automapper-aspnet-core/ Sun, 27 Oct 2019 11:53:06 +0000 https://code-maze.com/wp-content/uploads/2019/09/automapper-aspnet-core.png 49095 48816 0 0 swagger-json https://code-maze.com/swagger-ui-asp-net-core-web-api/swagger-json/ Mon, 28 Oct 2019 18:09:16 +0000 https://code-maze.com/wp-content/uploads/2019/10/swagger-json.png 49283 49098 0 0 swagger-ui https://code-maze.com/swagger-ui-asp-net-core-web-api/swagger-ui-2/ Mon, 28 Oct 2019 18:10:05 +0000 https://code-maze.com/wp-content/uploads/2019/10/swagger-ui.png 49284 49098 0 0 swagger-ui-action-method https://code-maze.com/swagger-ui-asp-net-core-web-api/swagger-ui-action-method/ Mon, 28 Oct 2019 18:10:56 +0000 https://code-maze.com/wp-content/uploads/2019/10/swagger-ui-action-method.png 49285 49098 0 0 swagger-ui-try-it-out https://code-maze.com/swagger-ui-asp-net-core-web-api/swagger-ui-try-it-out/ Mon, 28 Oct 2019 18:12:16 +0000 https://code-maze.com/wp-content/uploads/2019/10/swagger-ui-try-it-out.png 49286 49098 0 0 swagger-ui-api-info https://code-maze.com/swagger-ui-asp-net-core-web-api/swagger-ui-api-info/ Mon, 28 Oct 2019 18:13:27 +0000 https://code-maze.com/wp-content/uploads/2019/10/swagger-ui-api-info.png 49287 49098 0 0 action-method-with-summary https://code-maze.com/swagger-ui-asp-net-core-web-api/action-method-with-summary/ Mon, 28 Oct 2019 18:15:27 +0000 https://code-maze.com/wp-content/uploads/2019/10/action-method-with-summary.png 49288 49098 0 0 action-method-with-summary-and-remarks https://code-maze.com/swagger-ui-asp-net-core-web-api/action-method-with-summary-and-remarks/ Mon, 28 Oct 2019 18:16:43 +0000 https://code-maze.com/wp-content/uploads/2019/10/action-method-with-summary-and-remarks.png 49289 49098 0 0 required-attribute-in-swagger-ui https://code-maze.com/swagger-ui-asp-net-core-web-api/required-attribute-in-swagger-ui/ Mon, 28 Oct 2019 18:19:28 +0000 https://code-maze.com/wp-content/uploads/2019/10/required-attribute-in-swagger-ui.png 49290 49098 0 0 repsonse-types https://code-maze.com/swagger-ui-asp-net-core-web-api/repsonse-types/ Mon, 28 Oct 2019 18:21:28 +0000 https://code-maze.com/wp-content/uploads/2019/10/repsonse-types.png 49291 49098 0 0 generate-xml-documentation-setting https://code-maze.com/swagger-ui-asp-net-core-web-api/generate-xml-documentation-setting/ Mon, 28 Oct 2019 18:23:40 +0000 https://code-maze.com/wp-content/uploads/2019/10/generate-xml-documentation-setting.png 49292 49098 0 0 customized-ui https://code-maze.com/swagger-ui-asp-net-core-web-api/customized-ui/ Mon, 28 Oct 2019 18:25:17 +0000 https://code-maze.com/wp-content/uploads/2019/10/customized-ui.png 49294 49098 0 0 01- Configure project https://code-maze.com/01-configure-project/ Wed, 30 Oct 2019 07:55:03 +0000 https://code-maze.com/wp-content/uploads/2019/10/01-Configure-project.png 49315 0 0 0 02- Project type https://code-maze.com/02-project-type/ Wed, 30 Oct 2019 07:56:36 +0000 https://code-maze.com/wp-content/uploads/2019/10/02-Project-type.png 49316 0 0 0 03- New project creation https://code-maze.com/03-new-project-creation/ Wed, 30 Oct 2019 08:13:33 +0000 https://code-maze.com/wp-content/uploads/2019/10/03-New-project-creation.png 49320 0 0 0 04- Installing NLog library https://code-maze.com/04-installing-nlog-library/ Wed, 30 Oct 2019 08:20:13 +0000 https://code-maze.com/wp-content/uploads/2019/10/04-Installing-NLog-library.png 49321 0 0 0 05- Log messages https://code-maze.com/05-log-messages/ Wed, 30 Oct 2019 08:27:30 +0000 https://code-maze.com/wp-content/uploads/2019/10/05-Log-messages.png 49322 0 0 0 06- AddingOwnerController https://code-maze.com/06-addingownercontroller/ Wed, 30 Oct 2019 11:42:43 +0000 https://code-maze.com/wp-content/uploads/2019/10/06-AddingOwnerController.png 49330 0 0 0 07- Convention based routing https://code-maze.com/07-convention-based-routing/ Wed, 30 Oct 2019 11:48:19 +0000 https://code-maze.com/wp-content/uploads/2019/10/07-Convention-based-routing.png 49331 0 0 0 08- GetById Valid https://code-maze.com/08-getbyid-valid/ Wed, 30 Oct 2019 13:32:24 +0000 https://code-maze.com/wp-content/uploads/2019/10/08-GetById-Valid.png 49334 0 0 0 08- GetById Valid https://code-maze.com/08-getbyid-valid-2/ Wed, 30 Oct 2019 13:33:56 +0000 https://code-maze.com/wp-content/uploads/2019/10/08-GetById-Valid-1.png 49335 0 0 0 09- GetById InValid https://code-maze.com/09-getbyid-invalid/ Wed, 30 Oct 2019 13:35:45 +0000 https://code-maze.com/wp-content/uploads/2019/10/09-GetById-InValid.png 49336 0 0 0 7.1- GetAllOwners https://code-maze.com/7-1-getallowners/ Wed, 30 Oct 2019 14:01:11 +0000 https://code-maze.com/wp-content/uploads/2019/10/7.1-GetAllOwners.png 49339 0 0 0 7.1- GetAllOwners https://code-maze.com/7-1-getallowners-2/ Wed, 30 Oct 2019 14:05:31 +0000 https://code-maze.com/wp-content/uploads/2019/10/7.1-GetAllOwners-1.png 49340 0 0 0 10- GetWithDetails https://code-maze.com/10-getwithdetails/ Wed, 30 Oct 2019 14:46:48 +0000 https://code-maze.com/wp-content/uploads/2019/10/10-GetWithDetails.png 49341 0 0 0 11- Create action https://code-maze.com/11-create-action/ Thu, 31 Oct 2019 08:16:36 +0000 https://code-maze.com/wp-content/uploads/2019/10/11-Create-action.png 49351 0 0 0 12- Header of Create action https://code-maze.com/12-header-of-create-action/ Thu, 31 Oct 2019 08:28:18 +0000 https://code-maze.com/wp-content/uploads/2019/10/12-Header-of-Create-action.png 49352 0 0 0 stanoje-ivanovic-goolge.jpg https://code-maze.com/testimonial/code-maze-is-a-true-discovery/stanoje-ivanovic-goolge-jpg/ Thu, 31 Oct 2019 09:33:51 +0000 https://code-maze.com/wp-content/uploads/2019/10/stanoje-ivanovic-goolge.jpg 49355 49354 0 0 13- Update https://code-maze.com/13-update/ Thu, 31 Oct 2019 10:18:38 +0000 https://code-maze.com/wp-content/uploads/2019/10/13-Update.png 49358 0 0 0 13- Update https://code-maze.com/13-update-2/ Thu, 31 Oct 2019 10:19:29 +0000 https://code-maze.com/wp-content/uploads/2019/10/13-Update-1.png 49359 0 0 0 14- publish screen https://code-maze.com/14-publish-screen/ Thu, 31 Oct 2019 13:21:25 +0000 https://code-maze.com/wp-content/uploads/2019/10/14-publish-screen.png 49366 0 0 0 15- Add website https://code-maze.com/15-add-website/ Thu, 31 Oct 2019 13:50:06 +0000 https://code-maze.com/wp-content/uploads/2019/10/15-Add-website.png 49367 0 0 0 16- app pool https://code-maze.com/16-app-pool/ Thu, 31 Oct 2019 13:52:12 +0000 https://code-maze.com/wp-content/uploads/2019/10/16-app-pool.png 49368 0 0 0 17- Edit app pool https://code-maze.com/17-edit-app-pool/ Thu, 31 Oct 2019 13:54:14 +0000 https://code-maze.com/wp-content/uploads/2019/10/17-Edit-app-pool.png 49369 0 0 0 creating view with template https://code-maze.com/getting-started-with-asp-net-core-mvc/creating-view-with-template/ Thu, 31 Oct 2019 19:43:36 +0000 https://code-maze.com/wp-content/uploads/2019/05/creating-view-with-template.png 49381 5513 0 0 creating view with template https://code-maze.com/getting-started-with-asp-net-core-mvc/creating-view-with-template-2/ Thu, 31 Oct 2019 20:00:56 +0000 https://code-maze.com/wp-content/uploads/2019/05/creating-view-with-template-1.png 49382 5513 0 0 01- Postam Jwt Request https://code-maze.com/01-postam-jwt-request/ Fri, 01 Nov 2019 12:36:39 +0000 https://code-maze.com/wp-content/uploads/2019/11/01-Postam-Jwt-Request.png 49396 0 0 0 02- Postam Jwt Response https://code-maze.com/02-postam-jwt-response/ Fri, 01 Nov 2019 12:38:06 +0000 https://code-maze.com/wp-content/uploads/2019/11/02-Postam-Jwt-Response.png 49397 0 0 0 03- authorization header https://code-maze.com/03-authorization-header/ Fri, 01 Nov 2019 14:32:18 +0000 https://code-maze.com/wp-content/uploads/2019/11/03-authorization-header.png 49405 0 0 0 swagger ui asp net core https://code-maze.com/swagger-ui-asp-net-core-web-api/swagger-ui-asp-net-core/ Sat, 02 Nov 2019 11:05:32 +0000 https://code-maze.com/wp-content/uploads/2019/11/swagger-ui-asp-net-core.png 49413 49098 0 0 dalsoft restclient system testing https://code-maze.com/system-testing-rest-api-dalsoft-restclient/dalsoft-restclient-system-testing/ Mon, 04 Nov 2019 22:18:26 +0000 https://code-maze.com/wp-content/uploads/2019/11/dalsoft-restclient-system-testing.png 49438 49432 0 0 sweater man https://code-maze.com/testimonial/asp-net-core-web-api-logging-with-nlog/sweater-man/ Tue, 05 Nov 2019 10:39:05 +0000 https://code-maze.com/wp-content/uploads/2019/11/sweater-man.png 49441 49439 0 0 man 1 https://code-maze.com/testimonial/awesome/man-1/ Tue, 05 Nov 2019 21:26:02 +0000 https://code-maze.com/wp-content/uploads/2019/11/man-1.png 49449 49447 0 0 man 2 https://code-maze.com/testimonial/intermediate-to-professionalism/man-2/ Tue, 05 Nov 2019 21:27:37 +0000 https://code-maze.com/wp-content/uploads/2019/11/man-2.png 49450 49448 0 0 06 hateoas https://code-maze.com/hateoas-aspnet-core-web-api/06-hateoas/ Fri, 08 Nov 2019 23:33:25 +0000 https://code-maze.com/wp-content/uploads/2019/11/06-hateoas.png 49456 49442 0 0 darran profile https://code-maze.com/darran-profile/ Sun, 10 Nov 2019 17:13:21 +0000 https://code-maze.com/wp-content/uploads/2019/11/darran-profile.png 49463 0 0 0 man 3 https://code-maze.com/testimonial/fantastic/man-3/ Thu, 14 Nov 2019 12:17:04 +0000 https://code-maze.com/wp-content/uploads/2019/11/man-3.png 49531 49530 0 0 code maze weekly 1 https://code-maze.com/code-maze-weekly-1/code-maze-weekly-1/ Thu, 14 Nov 2019 18:26:22 +0000 https://code-maze.com/wp-content/uploads/2019/11/code-maze-weekly-1.png 49536 49466 0 0 Creating Web Api For PDF Generator https://code-maze.com/creating-web-api-for-pdf-generator/ Sun, 17 Nov 2019 14:49:50 +0000 https://code-maze.com/wp-content/uploads/2019/11/Creating-Web-Api-For-PDF-Generator.png 49571 0 0 0 Successfully created PDF https://code-maze.com/create-pdf-dotnetcore/successfully-created-pdf/ Sun, 17 Nov 2019 16:09:36 +0000 https://code-maze.com/wp-content/uploads/2018/06/Successfully-created-PDF.png 49572 3498 0 0 PDF in a browser https://code-maze.com/create-pdf-dotnetcore/pdf-in-a-browser/ Sun, 17 Nov 2019 16:11:55 +0000 https://code-maze.com/wp-content/uploads/2018/06/PDF-in-a-browser.png 49573 3498 0 0 New web page in pdf https://code-maze.com/create-pdf-dotnetcore/new-web-page-in-pdf/ Sun, 17 Nov 2019 16:14:10 +0000 https://code-maze.com/wp-content/uploads/2018/06/New-web-page-in-pdf.png 49574 3498 0 0 code maze weekly 2 https://code-maze.com/code-maze-weekly-2/code-maze-weekly-2/ Mon, 18 Nov 2019 21:27:47 +0000 https://code-maze.com/wp-content/uploads/2019/11/code-maze-weekly-2.png 49587 49578 0 0 01-DetailsPage https://code-maze.com/data-protection-aspnet-core/01-detailspage/ Sat, 23 Nov 2019 09:21:19 +0000 https://code-maze.com/wp-content/uploads/2019/11/01-DetailsPage.png 49600 49599 0 0 02-DetailsPage protected id https://code-maze.com/data-protection-aspnet-core/02-detailspage-protected-id/ Sat, 23 Nov 2019 09:21:20 +0000 https://code-maze.com/wp-content/uploads/2019/11/02-DetailsPage-protected-id.png 49601 49599 0 0 03-Invalid payload https://code-maze.com/data-protection-aspnet-core/03-invalid-payload/ Sat, 23 Nov 2019 09:21:21 +0000 https://code-maze.com/wp-content/uploads/2019/11/03-Invalid-payload.png 49602 49599 0 0 04-Protected payload time expired https://code-maze.com/data-protection-aspnet-core/04-protected-payload-time-expired/ Sat, 23 Nov 2019 09:21:22 +0000 https://code-maze.com/wp-content/uploads/2019/11/04-Protected-payload-time-expired.png 49603 49599 0 0 05-Local configuration file https://code-maze.com/data-protection-aspnet-core/05-local-configuration-file/ Sat, 23 Nov 2019 09:21:23 +0000 https://code-maze.com/wp-content/uploads/2019/11/05-Local-configuration-file.png 49604 49599 0 0 06-Encrypted key https://code-maze.com/data-protection-aspnet-core/06-encrypted-key/ Sat, 23 Nov 2019 09:21:24 +0000 https://code-maze.com/wp-content/uploads/2019/11/06-Encrypted-key.png 49605 49599 0 0 07-Different methods for key encryption https://code-maze.com/data-protection-aspnet-core/07-different-methods-for-key-encryption/ Sat, 23 Nov 2019 09:21:25 +0000 https://code-maze.com/wp-content/uploads/2019/11/07-Different-methods-for-key-encryption.png 49606 49599 0 0 08-Expiration date changed for the key https://code-maze.com/data-protection-aspnet-core/08-expiration-date-changed-for-the-key/ Sat, 23 Nov 2019 09:21:25 +0000 https://code-maze.com/wp-content/uploads/2019/11/08-Expiration-date-changed-for-the-key.png 49607 49599 0 0 data protection https://code-maze.com/data-protection-aspnet-core/data-protection/ Sun, 24 Nov 2019 20:28:51 +0000 https://code-maze.com/wp-content/uploads/2019/11/data-protection.png 49624 49599 0 0 rider discount code 1 year cropped https://code-maze.com/rider-discount-code-1-year-cropped/ Mon, 25 Nov 2019 00:48:51 +0000 https://code-maze.com/wp-content/uploads/2019/11/rider-discount-code-1-year-cropped.png 49634 0 0 0 rider discount code 1 year cropped https://code-maze.com/rider-discount-code-1-year-cropped-2/ Mon, 25 Nov 2019 01:18:08 +0000 https://code-maze.com/wp-content/uploads/2019/11/rider-discount-code-1-year-cropped-1.png 49635 0 0 0 code maze weekly 3 https://code-maze.com/code-maze-weekly-3/code-maze-weekly-3/ Tue, 26 Nov 2019 10:16:07 +0000 https://code-maze.com/wp-content/uploads/2019/11/code-maze-weekly-3.png 49643 49642 0 0 new project https://code-maze.com/unit-testing-aspnetcore-web-api/new-project-5/ Wed, 27 Nov 2019 11:55:45 +0000 https://code-maze.com/wp-content/uploads/2018/07/new-project-1.png 49652 3847 0 0 web api project https://code-maze.com/unit-testing-aspnetcore-web-api/web-api-project/ Wed, 27 Nov 2019 11:58:41 +0000 https://code-maze.com/wp-content/uploads/2018/07/web-api-project.png 49653 3847 0 0 new xunit project https://code-maze.com/unit-testing-aspnetcore-web-api/new-xunit-project/ Wed, 27 Nov 2019 12:00:36 +0000 https://code-maze.com/wp-content/uploads/2018/07/new-xunit-project.png 49654 3847 0 0 referencing web-api https://code-maze.com/unit-testing-aspnetcore-web-api/referencing-web-api/ Wed, 27 Nov 2019 12:02:35 +0000 https://code-maze.com/wp-content/uploads/2018/07/referencing-web-api.png 49655 3847 0 0 code maze weekly 4 https://code-maze.com/code-maze-weekly-4/code-maze-weekly-4/ Wed, 04 Dec 2019 18:15:02 +0000 https://code-maze.com/wp-content/uploads/2019/12/code-maze-weekly-4.png 49683 49682 0 0 code maze weekly 5 https://code-maze.com/code-maze-weekly-5/code-maze-weekly-5/ Sun, 08 Dec 2019 18:43:18 +0000 https://code-maze.com/wp-content/uploads/2019/12/code-maze-weekly-5.png 49720 49719 0 0 woocommerce-placeholder https://code-maze.com/woocommerce-placeholder/ Tue, 10 Dec 2019 11:32:21 +0000 https://code-maze.com/wp-content/uploads/2019/12/woocommerce-placeholder.png 49725 0 0 0 aleksa profile https://code-maze.com/75379861_652325005172219_6837996472521195520_n/ Fri, 13 Dec 2019 22:20:16 +0000 https://code-maze.com/wp-content/uploads/2019/12/75379861_652325005172219_6837996472521195520_n.jpg 49816 0 0 0 man 4 https://code-maze.com/testimonial/awesome-and-very-useful-article/man-4/ Mon, 16 Dec 2019 09:14:18 +0000 https://code-maze.com/wp-content/uploads/2019/12/man-4.png 49838 49836 0 0 Swagger UI https://code-maze.com/swagger-ui-3/ Tue, 17 Dec 2019 14:29:37 +0000 https://code-maze.com/wp-content/uploads/2019/12/Swagger-UI.png 49843 0 0 0 DockerHub https://code-maze.com/dockerhub/ Tue, 17 Dec 2019 21:26:51 +0000 https://code-maze.com/wp-content/uploads/2019/12/DockerHub.png 49851 0 0 0 DockerHub create repository https://code-maze.com/dockerhub-create-repository/ Wed, 18 Dec 2019 11:33:33 +0000 https://code-maze.com/wp-content/uploads/2019/12/DockerHub-create-repository.png 49856 0 0 0 Powershell docker images https://code-maze.com/powershell-docker-images/ Wed, 18 Dec 2019 12:03:02 +0000 https://code-maze.com/wp-content/uploads/2019/12/Powershell-docker-images.png 49857 0 0 0 Powershell docker images comparison https://code-maze.com/powershell-docker-images-comparison/ Wed, 18 Dec 2019 15:36:31 +0000 https://code-maze.com/wp-content/uploads/2019/12/Powershell-docker-images-comparison.png 49858 0 0 0 docker build step https://code-maze.com/docker-build-step-2/ Thu, 19 Dec 2019 18:05:24 +0000 https://code-maze.com/wp-content/uploads/2019/12/docker-build-step.png 49878 0 0 0 docker push step https://code-maze.com/docker-push-step-2/ Thu, 19 Dec 2019 18:07:29 +0000 https://code-maze.com/wp-content/uploads/2019/12/docker-push-step.png 49879 0 0 0 code maze weekly 6 https://code-maze.com/code-maze-weekly-6/code-maze-weekly-6-2/ Fri, 20 Dec 2019 11:02:07 +0000 https://code-maze.com/wp-content/uploads/2019/12/code-maze-weekly-6.png 49904 49872 0 0 Powershell build image https://code-maze.com/aspnetcore-app-dockerfiles/powershell-build-image/ Mon, 23 Dec 2019 09:43:56 +0000 https://code-maze.com/wp-content/uploads/2018/05/Powershell-build-image.png 49935 1990 0 0 table https://code-maze.com/?attachment_id=49979 Thu, 26 Dec 2019 07:40:54 +0000 https://code-maze.com/wp-content/uploads/2019/12/table.png 49979 49977 0 0 data https://code-maze.com/?attachment_id=49980 Thu, 26 Dec 2019 07:41:58 +0000 https://code-maze.com/wp-content/uploads/2019/12/data.png 49980 49977 0 0 login page https://code-maze.com/?attachment_id=49981 Thu, 26 Dec 2019 07:48:44 +0000 https://code-maze.com/wp-content/uploads/2019/12/login-page.png 49981 49977 0 0 login success https://code-maze.com/?attachment_id=49982 Thu, 26 Dec 2019 07:49:42 +0000 https://code-maze.com/wp-content/uploads/2019/12/login-success.png 49982 49977 0 0 invalid login https://code-maze.com/?attachment_id=49984 Thu, 26 Dec 2019 08:01:17 +0000 https://code-maze.com/wp-content/uploads/2019/12/invalid-login.png 49984 49977 0 0 invalid login with exception https://code-maze.com/?attachment_id=49985 Thu, 26 Dec 2019 08:02:24 +0000 https://code-maze.com/wp-content/uploads/2019/12/invalid-login-with-exception.png 49985 49977 0 0 invalid login with exception https://code-maze.com/?attachment_id=49986 Thu, 26 Dec 2019 08:05:59 +0000 https://code-maze.com/wp-content/uploads/2019/12/invalid-login-with-exception-1.png 49986 49977 0 0 validation https://code-maze.com/?attachment_id=49992 Thu, 26 Dec 2019 09:31:33 +0000 https://code-maze.com/wp-content/uploads/2019/12/validation.png 49992 49977 0 0 invalid login https://code-maze.com/?attachment_id=49993 Thu, 26 Dec 2019 09:37:30 +0000 https://code-maze.com/wp-content/uploads/2019/12/invalid-login-1.png 49993 49977 0 0 code maze weekly 7 https://code-maze.com/code-maze-weekly-7/code-maze-weekly-7-2/ Fri, 27 Dec 2019 11:00:48 +0000 https://code-maze.com/wp-content/uploads/2019/12/code-maze-weekly-7.png 50008 49987 0 0 01-Project structure https://code-maze.com/?attachment_id=50049 Mon, 30 Dec 2019 08:37:31 +0000 https://code-maze.com/wp-content/uploads/2019/12/01-Project-structure.png 50049 50048 0 0 02-MailKit https://code-maze.com/?attachment_id=50050 Mon, 30 Dec 2019 08:37:32 +0000 https://code-maze.com/wp-content/uploads/2019/12/02-MailKit.png 50050 50048 0 0 03-Postman request sync mail https://code-maze.com/?attachment_id=50051 Mon, 30 Dec 2019 08:37:33 +0000 https://code-maze.com/wp-content/uploads/2019/12/03-Postman-request-sync-mail.png 50051 50048 0 0 04-Email server source https://code-maze.com/?attachment_id=50052 Mon, 30 Dec 2019 08:37:34 +0000 https://code-maze.com/wp-content/uploads/2019/12/04-Email-server-source.png 50052 50048 0 0 05-Email server destination https://code-maze.com/?attachment_id=50053 Mon, 30 Dec 2019 08:37:35 +0000 https://code-maze.com/wp-content/uploads/2019/12/05-Email-server-destination.png 50053 50048 0 0 06-Html body for the email message https://code-maze.com/?attachment_id=50054 Mon, 30 Dec 2019 08:37:36 +0000 https://code-maze.com/wp-content/uploads/2019/12/06-Html-body-for-the-email-message.png 50054 50048 0 0 07-Async email message https://code-maze.com/?attachment_id=50055 Mon, 30 Dec 2019 08:37:37 +0000 https://code-maze.com/wp-content/uploads/2019/12/07-Async-email-message.png 50055 50048 0 0 08-Postman attachment https://code-maze.com/?attachment_id=50056 Mon, 30 Dec 2019 08:37:37 +0000 https://code-maze.com/wp-content/uploads/2019/12/08-Postman-attachment.png 50056 50048 0 0 09-Attachments email https://code-maze.com/?attachment_id=50057 Mon, 30 Dec 2019 08:37:39 +0000 https://code-maze.com/wp-content/uploads/2019/12/09-Attachments-email.png 50057 50048 0 0 _Group_ https://code-maze.com/_group_/ Tue, 31 Dec 2019 23:54:18 +0000 https://code-maze.com/wp-content/uploads/2020/01/Group_.png 50074 0 0 0 cover https://code-maze.com/cover/ Tue, 31 Dec 2019 23:59:34 +0000 https://code-maze.com/wp-content/uploads/2020/01/cover.png 50075 0 0 0 Creating ASP.NET Core App - EF Core https://code-maze.com/creating-asp-net-core-app-ef-core/ Wed, 01 Jan 2020 11:10:29 +0000 https://code-maze.com/wp-content/uploads/2020/01/Creating-ASP.NET-Core-App-EF-Core.png 50088 0 0 0 New Project Structure https://code-maze.com/new-project-structure/ Wed, 01 Jan 2020 12:04:45 +0000 https://code-maze.com/wp-content/uploads/2020/01/New-Project-Structure.png 50091 0 0 0 Translated query https://code-maze.com/translated-query/ Wed, 01 Jan 2020 14:04:51 +0000 https://code-maze.com/wp-content/uploads/2020/01/Translated-query.png 50094 0 0 0 xUnit library installed https://code-maze.com/xunit-library-installed/ Thu, 02 Jan 2020 12:33:45 +0000 https://code-maze.com/wp-content/uploads/2020/01/xUnit-library-installed.png 50117 0 0 0 moq https://code-maze.com/moq/ Thu, 02 Jan 2020 12:49:59 +0000 https://code-maze.com/wp-content/uploads/2020/01/moq.png 50119 0 0 0 Selenium libraries https://code-maze.com/selenium-libraries/ Thu, 02 Jan 2020 14:36:48 +0000 https://code-maze.com/wp-content/uploads/2020/01/Selenium-libraries.png 50123 0 0 0 Divi Builder Demo https://code-maze.com/et_pb_layout/divi-builder-demo/ Thu, 23 Nov 2017 19:30:45 +0000 https://code-maze.com/et_pb_layout/divi-builder-demo/ Lorem ipsum dolor sit amet consectetur adipiscing elit el vivamus eu vehicula.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_4"][et_pb_blurb title="Drag & Drop" use_icon="on" font_icon="%%372%%" icon_color="rgba(46,48,55,0.38)" animation="off" text_orientation="center" use_icon_font_size="on" icon_font_size="32px" header_font="Raleway|on||on|" header_font_size="20" header_text_color="#3a3e59" body_font_size="16px" body_text_color="rgba(46,48,55,0.5)" body_line_height="1.8em" use_background_color_gradient="on" background_color_gradient_start="#e4e4e4" background_color_gradient_end="rgba(228,228,228,0)" background_color_gradient_start_position="30%" background_color_gradient_end_position="90%" custom_padding="40px|24px|140%|24px" custom_css_blurb_title="margin-bottom: 10px;" saved_tabs="all" background_position="top_left" background_repeat="repeat" background_size="initial"]

Lorem ipsum dolor sit amet consectetur adipiscing elit el vivamus eu vehicula.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_4"][et_pb_blurb title="Fully Customize" use_icon="on" font_icon="%%70%%" icon_color="rgba(46,48,55,0.38)" animation="off" text_orientation="center" use_icon_font_size="on" icon_font_size="32px" header_font="Raleway|on||on|" header_font_size="20" header_text_color="#3a3e59" body_font_size="16px" body_text_color="rgba(46,48,55,0.5)" body_line_height="1.8em" use_background_color_gradient="on" background_color_gradient_start="#efefef" background_color_gradient_end="rgba(239,239,239,0)" background_color_gradient_start_position="30%" background_color_gradient_end_position="90%" custom_padding="40px|24px|140%|24px" custom_css_blurb_title="margin-bottom: 10px;" saved_tabs="all" background_position="top_left" background_repeat="repeat" background_size="initial"]

Lorem ipsum dolor sit amet consectetur adipiscing elit el vivamus eu vehicula.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_4"][et_pb_blurb title="Clone & Delete" use_icon="on" font_icon="%%100%%" icon_color="rgba(46,48,55,0.38)" animation="off" text_orientation="center" use_icon_font_size="on" icon_font_size="32px" header_font="Raleway|on||on|" header_font_size="20" header_text_color="#3a3e59" body_font_size="16px" body_text_color="rgba(46,48,55,0.5)" body_line_height="1.8em" use_background_color_gradient="on" background_color_gradient_start="#d7d9d9" background_color_gradient_end="rgba(215,217,217,0)" background_color_gradient_start_position="30%" background_color_gradient_end_position="90%" custom_padding="40px|24px|140%|24px" custom_css_blurb_title="margin-bottom: 10px;" saved_tabs="all" background_position="top_left" background_repeat="repeat" background_size="initial"]

Lorem ipsum dolor sit amet consectetur adipiscing elit el vivamus eu vehicula.

[/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fullwidth="off" specialty="off" custom_padding="0px|||" background_color="#efefe8"][et_pb_row custom_margin="|||" padding_top_1="10%" background_position_1="top_left" background_position_2="top_left" background_repeat_1="no-repeat" background_repeat_2="no-repeat" background_position="top_left" background_repeat="repeat" background_size="initial"][et_pb_column type="1_2"][et_pb_blurb title="So simple!" animation="off" text_orientation="right" header_font="Raleway|on||on|" header_font_size="36px" header_text_color="#3a3e59" custom_css_main_element="max-width: none;" background_position="top_left" background_repeat="repeat" background_size="initial"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tempus sed odio non blandit. Ut varius purus eget erat ornare facilisis. Mauris pharetra lacus nec sapien iaculis, sit amet ullamcorper sapien finibus.[/et_pb_blurb][et_pb_button button_text="Learn more" button_alignment="right" background_layout="dark" disabled_on="on|on|" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#e85929" button_border_width="0px" button_border_radius="3px" button_letter_spacing="3px" button_font="Raleway|on||on|" button_use_icon="off" button_bg_color_hover="#e84712" button_letter_spacing_hover="2px" background_color="#7EBEC5" /][/et_pb_column][et_pb_column type="1_2"][et_pb_image src="https://cdn.elegantthemes.com/images/tour/jar.jpg" align="center" max_width="510px" /][/et_pb_column][/et_pb_row][et_pb_row custom_margin="|||" make_equal="on" padding_top_2="14%" background_position_1="top_left" background_position_2="top_left" background_repeat_1="no-repeat" background_repeat_2="no-repeat" background_position="top_left" background_repeat="repeat" background_size="initial"][et_pb_column type="1_2"][et_pb_image src="https://cdn.elegantthemes.com/images/tour/cake.jpg" animation="right" align="center" max_width="510px" /][/et_pb_column][et_pb_column type="1_2"][et_pb_blurb title="So sweet!" animation="off" header_font="Raleway|on||on|" header_font_size="36px" header_text_color="#3a3e59" custom_css_main_element="max-width: none;" background_position="top_left" background_repeat="repeat" background_size="initial"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tempus sed odio non blandit. Ut varius purus eget erat ornare facilisis. Mauris pharetra lacus nec sapien iaculis, sit amet ullamcorper sapien finibus.[/et_pb_blurb][et_pb_button button_text="Learn more" button_alignment="left" background_layout="dark" disabled_on="on|on|" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#e85929" button_border_width="0px" button_border_radius="2px" button_letter_spacing="3px" button_font="Raleway|on||on|" button_use_icon="off" button_bg_color_hover="#e84712" button_letter_spacing_hover="2px" background_color="#7EBEC5" /][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section background_color="#212127" custom_padding="|||" fullwidth="on"][et_pb_fullwidth_header title="Perfectly Diverse" background_layout="dark" text_orientation="center" scroll_down_icon_color="#ffffff" scroll_down_icon_size="36px" content_font_color="rgba(255,255,255,0.72)" button_one_text="Download" background_overlay_color="rgba(255,255,255,0)" title_font="Old Standard TT|on||on|" title_font_size="62px" title_font_size_tablet="62px" title_font_size_phone="48px" title_font_size_last_edited="on|phone" content_font="Raleway||||" content_font_size="20px" content_text_color="#ffffff" custom_button_one="on" button_one_text_size="16px" button_one_text_color="#ffffff" button_one_bg_color="#e85929" button_one_border_width="0px" button_one_border_radius="2px" button_one_letter_spacing="3px" button_one_font="Raleway|on||on|" button_one_use_icon="off" button_one_bg_color_hover="rgba(232,89,41,0.85)" button_one_letter_spacing_hover="2px" custom_css_main_element="padding: 6% 0;" custom_css_header_container="width: 100%;" custom_css_button_1="margin-top: 40px;"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vel mollis eros. Vestibulum in blandit massa. Nunc lorem lacus, lacinia ut lobortis id, tempor a sem. Vivamus rhoncus imperdiet quam quis vestibulum. Sed at dui orci.

[/et_pb_fullwidth_header][/et_pb_section][et_pb_section fullwidth="on"][et_pb_fullwidth_image src="https://cdn.elegantthemes.com/images/tour/cheese.jpg" animation="off" /][/et_pb_section]]]>
739 0 0 0
Extra https://code-maze.com/extra/ Thu, 23 Nov 2017 20:00:13 +0000 https://code-maze.com/extra/ 783 0 0 0 CodeMaze https://code-maze.com/et_pb_layout/codemaze/ Thu, 23 Nov 2017 23:03:08 +0000 https://code-maze.com/et_pb_layout/codemaze/ [et_pb_section bb_built="1" admin_label="section"][et_pb_row admin_label="row" background_position="top_left" background_repeat="repeat" background_size="initial" _builder_version="3.0.89" disabled="on" disabled_on="on|on|on"][et_pb_column type="4_4"][et_pb_featured_posts_slider category_id="0" display_featured_posts_only="on" show_author="on" show_categories="off" show_comments="on" show_rating="off" show_date="on" remove_drop_shadow="off" _builder_version="3.0.89" ignore_displayed_posts="off" border_radii="on|3px|3px|3px|3px" module_class="carousel-item-fix" custom_css_nav="height: 445.647px;" /][/et_pb_column][/et_pb_row][et_pb_row][et_pb_column type="1_2"][et_pb_posts category_id="13" posts_per_page="4" display_featured_posts_only="off" show_thumbnails="off" show_author="on" show_categories="off" show_comments="on" show_rating="off" show_date="on" use_border_color="off" border_color="#ffffff" border_style="solid" remove_drop_shadow="off" _builder_version="3.0.89" ignore_displayed_posts="off" header_text_color="#7ac8cc" main_title_text_color="#7ac8cc" main_meta_text_color="#000000" border_color_all="#7ac8cc" header_font_size="20px" hover_overlay_icon="%%71%%" orderby="comment_count" /][/et_pb_column][et_pb_column type="1_2"][et_pb_posts category_id="4" posts_per_page="4" display_featured_posts_only="off" show_thumbnails="off" show_author="on" show_categories="off" show_comments="on" show_rating="off" show_date="on" use_border_color="off" border_color="#ffffff" border_style="solid" remove_drop_shadow="off" _builder_version="3.0.89" ignore_displayed_posts="off" border_color_all="#e6543d" header_text_color="#e6543d" main_title_text_color="#e6543d" main_meta_text_color="#000000" main_meta_text_align="left" list_meta_text_align="left" header_font_size="20px" hover_overlay_icon="%%71%%" orderby="comment_count" /][/et_pb_column][/et_pb_row][/et_pb_section]

]]>
845 0 0 0
CodeMaze https://code-maze.com/et_pb_layout/codemaze-2/ Thu, 11 Jan 2018 09:09:41 +0000 https://code-maze.com/et_pb_layout/codemaze-2/ [et_pb_section bb_built="1" admin_label="section" _builder_version="3.0.89"][et_pb_row][et_pb_column type="4_4"][et_pb_posts_blog_feed_standard _builder_version="3.0.89" category_id="0" posts_per_page="3" display_featured_posts_only="off" ignore_displayed_posts="off" content_length="excerpt" show_pagination="on" show_author="on" show_categories="on" show_featured_image="on" show_date="on" show_comments="on" show_rating="off" show_more="on" remove_drop_shadow="off" custom_read_more="off" read_more_icon_placement="right" hover_overlay_icon="%%65%%" /][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.89" custom_margin="||70px|"][et_pb_column type="1_2"][et_pb_posts category_id="13" posts_per_page="4" display_featured_posts_only="off" show_thumbnails="off" show_author="on" show_categories="off" show_comments="on" show_rating="off" show_date="on" use_border_color="off" border_color="#ffffff" border_style="solid" remove_drop_shadow="off" _builder_version="3.0.89" ignore_displayed_posts="off" header_text_color="#7ac8cc" main_title_text_color="#7ac8cc" main_meta_text_color="#000000" border_color_all="#7ac8cc" header_font_size="20px" hover_overlay_icon="%%71%%" orderby="comment_count" list_title_text_color="#7ac8cc" /][/et_pb_column][et_pb_column type="1_2"][et_pb_posts category_id="4" posts_per_page="4" display_featured_posts_only="off" show_thumbnails="off" show_author="on" show_categories="off" show_comments="on" show_rating="off" show_date="on" use_border_color="off" border_color="#ffffff" border_style="solid" remove_drop_shadow="off" _builder_version="3.0.89" ignore_displayed_posts="off" border_color_all="#e6543d" header_text_color="#e6543d" main_title_text_color="#e6543d" main_meta_text_color="#000000" main_meta_text_align="left" list_meta_text_align="left" header_font_size="20px" hover_overlay_icon="%%71%%" orderby="comment_count" list_title_text_color="#e6543d" /][/et_pb_column][/et_pb_row][/et_pb_section]

]]>
1097 0 0 0
The Complete Guide To HTTP Book https://code-maze.com/?post_type=dlm_download&p=2890 Fri, 04 May 2018 11:37:53 +0000 https://code-maze.com/?post_type=dlm_download&p=2890 2890 0 0 0 Download #2890 File Version https://code-maze.com/?post_type=dlm_download_version&p=2893 Fri, 04 May 2018 11:45:13 +0000 https://code-maze.com/?post_type=dlm_download_version&p=2893 2893 2890 0 0 Code Maze 2 https://code-maze.com/et_pb_layout/code-maze-2/ Fri, 05 Oct 2018 11:27:13 +0000 https://code-maze.com/et_pb_layout/code-maze-2/ 4821 0 0 0 3 rows https://code-maze.com/et_pb_layout/3-rows/ Tue, 16 Oct 2018 14:29:44 +0000 https://code-maze.com/et_pb_layout/3-rows/ 4955 0 0 0 Coding About https://code-maze.com/et_pb_layout/coding-about/ Mon, 16 Oct 2017 09:16:12 +0000 https://code-maze.com/et_pb_layout/coding-about/ We are Passionate About Building Tools for the Classroom [/et_pb_text][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font_size="16px" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][et_pb_button button_url="#" button_text="Start Free Trial" button_alignment="left" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_use_icon="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_video src="https://www.youtube.com/watch?v=FkQuawiGWUw" image_src="https://code-maze.com/wp-content/uploads/2019/02/video-overlay.jpg" play_icon_color="#7272ff" _builder_version="3.0.82" custom_margin="60px|||" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%"][/et_pb_video][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Features Section" _builder_version="3.0.82" custom_padding="100px|0px|100px|0px"][et_pb_row custom_padding="90px|0px|90px|0px" admin_label="Online Learning" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_19.jpg" _builder_version="3.0.82" max_width="64px" animation_style="zoom"][/et_pb_image][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Individual Online Learning

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos

himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque.

[/et_pb_text][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-06.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][/et_pb_row][et_pb_row custom_padding="90px|0px|90px|0px" padding_top_2="10%" admin_label="Classroom Lessons" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-07.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="right" animation_delay="100ms" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" padding_top="10%" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_15.jpg" _builder_version="3.0.82" max_width="64px" animation_style="zoom"][/et_pb_image][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Classrooom Lessons

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos

himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. In pellentesque

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row custom_padding="90px|0px|90px|0px" custom_margin="|||" padding_top_1="10%" admin_label="Coding Camps" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" padding_top="10%" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_16.jpg" _builder_version="3.0.82" max_width="64px" animation_style="zoom"][/et_pb_image][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Coding Camps

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos

himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. In pellentesque

[/et_pb_text][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-08.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="20%" locked="off"][/et_pb_image][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Education is in Our Blood" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay" custom_padding="100px|0px|100px|0px"][et_pb_row padding_top_2="60px" _builder_version="3.0.82" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-09.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="right" animation_delay="100ms" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" padding_top="60px" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.3em" custom_margin="||20px|" animation_style="zoom" animation_direction="left" locked="off"]

Education is in Our Blood

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" animation_style="zoom" animation_direction="left" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque.

[/et_pb_text][et_pb_button button_url="#" button_text="View All Courses" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" specialty="on" admin_label="Instructors" _builder_version="3.0.82" custom_padding="101px|0px|100px|0px"][et_pb_column type="1_3" _builder_version="3.0.47"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Our Instructors

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra

himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. In pellentesque

[/et_pb_text][/et_pb_column][et_pb_column type="2_3" specialty_columns="2" _builder_version="3.0.47"][et_pb_row_inner custom_padding="0px|0px|0px|0px" use_custom_gutter="on" gutter_width="1" module_class_1="et_pb_column_1_2" module_class_2="et_pb_column_1_2" _builder_version="3.0.82" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)"][et_pb_column_inner type="1_2" saved_specialty_column_type="2_3" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on" module_class="et_pb_column_1_2"][et_pb_blurb title="Casey Andrews" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_2.png" icon_placement="left" image_max_width="65px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" module_alignment="left" custom_margin="|||" custom_padding="60px|40px|30px|40px" animation_style="slide" animation_direction="top" animation_intensity_slide="5%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="#f0f0fa" locked="off"]Javascript[/et_pb_blurb][et_pb_blurb title="Timothy Heiden" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_6.png" icon_placement="left" image_max_width="65px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" module_alignment="left" custom_margin="|||" custom_padding="60px|40px|30px|40px" animation_style="slide" animation_direction="top" animation_intensity_slide="5%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="#f0f0fa" locked="off"]Python[/et_pb_blurb][et_pb_blurb title="Celine Marie" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_1.png" icon_placement="left" image_max_width="65px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" module_alignment="left" custom_margin="|||" custom_padding="60px|40px|30px|40px" animation_style="slide" animation_direction="top" animation_intensity_slide="5%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="#f0f0fa" locked="off"]Web Apps[/et_pb_blurb][/et_pb_column_inner][et_pb_column_inner type="1_2" saved_specialty_column_type="2_3" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on" module_class="et_pb_column_1_2"][et_pb_blurb title="Jennifer Holand" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_5.png" icon_placement="left" image_max_width="65px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" module_alignment="left" custom_margin="|||" custom_padding="60px|40px|30px|40px" animation_style="slide" animation_direction="top" animation_intensity_slide="5%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="#f0f0fa" locked="off"]Html / CSS[/et_pb_blurb][et_pb_blurb title="Cathy Waller" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_3.png" icon_placement="left" image_max_width="65px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" module_alignment="left" custom_margin="|||" custom_padding="60px|40px|30px|40px" animation_style="slide" animation_direction="top" animation_intensity_slide="5%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="#f0f0fa" locked="off"]Apps & Games[/et_pb_blurb][et_pb_blurb title="Paul Smith" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_4.png" icon_placement="left" image_max_width="65px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" module_alignment="left" custom_margin="|||" custom_padding="60px|40px|30px|40px" animation_style="slide" animation_direction="top" animation_intensity_slide="5%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="#f0f0fa" locked="off"]PHP[/et_pb_blurb][/et_pb_column_inner][/et_pb_row_inner][/et_pb_column][/et_pb_section][et_pb_section fb_built="1" specialty="on" padding_top_2="60px" admin_label="Contact Information" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay"][et_pb_column type="1_2" specialty_columns="2" _builder_version="3.0.47"][et_pb_row_inner _builder_version="3.0.82"][et_pb_column_inner type="4_4" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.3em" custom_margin="||20px|" animation_style="zoom" animation_direction="left" locked="off"]

Get In Touch with Us

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][/et_pb_column_inner][/et_pb_row_inner][et_pb_row_inner custom_padding="0px|0px|29.6875px|0px" admin_label="Row" _builder_version="3.0.82"][et_pb_column_inner type="1_2" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font="|on||on|" text_text_color="rgba(255,255,255,0.5)"]

New York

[/et_pb_text][/et_pb_column_inner][et_pb_column_inner type="1_2" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font_size="16px" locked="off"]

2923 Extra Ave.

New York, NY 39456

[/et_pb_text][/et_pb_column_inner][/et_pb_row_inner][et_pb_row_inner custom_padding="0px|0px|29.6875px|0px" _builder_version="3.0.82"][et_pb_column_inner type="1_2" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font="|on||on|" text_text_color="rgba(255,255,255,0.5)"]

Headquarters

[/et_pb_text][/et_pb_column_inner][et_pb_column_inner type="1_2" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font_size="16px" locked="off"]

1235 Divi Street, Floor 3

San Francisco, CA 92021

[/et_pb_text][/et_pb_column_inner][/et_pb_row_inner][et_pb_row_inner _builder_version="3.0.82"][et_pb_column_inner type="4_4" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_divider color="rgba(255,255,255,0.15)" show_divider="on" divider_weight="3px" _builder_version="3.0.82"][/et_pb_divider][/et_pb_column_inner][/et_pb_row_inner][et_pb_row_inner custom_padding="0px|0px|29.6875px|0px" _builder_version="3.0.82"][et_pb_column_inner type="1_2" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font="|on||on|" text_text_color="rgba(255,255,255,0.5)"]

Phone Number

[/et_pb_text][/et_pb_column_inner][et_pb_column_inner type="1_2" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font_size="16px" locked="off"]sales@codingwithdivi.com[/et_pb_text][/et_pb_column_inner][/et_pb_row_inner][et_pb_row_inner custom_padding="0px|0px|29.6875px|0px" _builder_version="3.0.82"][et_pb_column_inner type="1_2" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font="|on||on|" text_text_color="rgba(255,255,255,0.5)"]

Sales

[/et_pb_text][/et_pb_column_inner][et_pb_column_inner type="1_2" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font_size="16px" locked="off"]

sales@codingwithdivi.com

[/et_pb_text][/et_pb_column_inner][/et_pb_row_inner][et_pb_row_inner custom_padding="0px|0px|29.6875px|0px" _builder_version="3.0.82"][et_pb_column_inner type="1_2" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font="|on||on|" text_text_color="rgba(255,255,255,0.5)"]

Support

[/et_pb_text][/et_pb_column_inner][et_pb_column_inner type="1_2" saved_specialty_column_type="1_2" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font_size="16px" locked="off"]

support@codingwithdivi.com

[/et_pb_text][/et_pb_column_inner][/et_pb_row_inner][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" padding_top="60px"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-10.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="20%" locked="off"][/et_pb_image][/et_pb_column][/et_pb_section][et_pb_section fb_built="1" admin_label="Companies that use our product" _builder_version="3.0.82" custom_padding="100px|0px|180px|0px"][et_pb_row _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="4_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_text_align="center" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" max_width="540px" module_alignment="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Join the Revolution

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" text_orientation="center" max_width="600px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis.

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/logo_3.jpg" _builder_version="3.0.82"][/et_pb_image][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/logo_2.jpg" _builder_version="3.0.82" locked="off"][/et_pb_image][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/logo_4.jpg" _builder_version="3.0.82" locked="off"][/et_pb_image][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/logo_1.jpg" _builder_version="3.0.82" locked="off"][/et_pb_image][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" background_color="#f7f8fc" admin_label="Footer" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" saved_tabs="all" global_module="46771"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

Ready to get started?

[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"]

Get in touch, or create an account

[/et_pb_text][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section]]]>
47100 0 0 0
Coding Blog https://code-maze.com/et_pb_layout/coding-blog/ Mon, 16 Oct 2017 09:16:18 +0000 https://code-maze.com/et_pb_layout/coding-blog/ Blog [/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#474ab6" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" max_width="540px" module_alignment="center" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Blog" _builder_version="3.0.82" custom_margin="|||" custom_padding="0px|0px|100px|0px"][et_pb_row use_custom_width="on" custom_width_px="960px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="4_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blog posts_number="10" include_categories="30" offset_number="0" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" meta_font_size="16px" meta_text_color="rgba(133,133,189,0.5)" meta_line_height="1.9em" background_color="#ffffff" text_orientation="center" custom_padding="40px|40px|40px|40px" animation_style="zoom" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)"][/et_pb_blog][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Subscribe" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_blend="overlay" custom_padding="100px|0px|200px|0px"][et_pb_row custom_margin="|||" padding_top_2="60px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" locked="off"][et_pb_column type="4_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="42px" header_font_size_last_edited="on|desktop" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" max_width="530px" module_alignment="center" custom_padding="|||" locked="off"]

Follow Along

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row custom_padding="0px|0px|27px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_signup feedburner_uri="Elegant Themes Blog" mailchimp_list="MailChimp|03dac884f0" name_field="on" use_background_color="off" _builder_version="3.0.82" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_use_icon="off" button_letter_spacing_hover="0" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(9,225,192,0.3)"][/et_pb_signup][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" background_color="#f7f8fc" admin_label="Footer" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" global_module="46771" saved_tabs="all"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

Ready to get started?

[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"]

Get in touch, or create an account

[/et_pb_text][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section]]]>
47101 0 0 0
Coding Contact https://code-maze.com/et_pb_layout/coding-contact/ Mon, 16 Oct 2017 09:16:24 +0000 https://code-maze.com/et_pb_layout/coding-contact/ Contact Us[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="1" custom_padding="0px|0px|0px|0px" make_equal="on" background_color="#ffffff" background_color_2="#09e1c0" padding_top_1="60px" padding_right_1="40px" padding_bottom_1="60px" padding_left_1="40px" padding_top_2="60px" padding_right_2="40px" padding_bottom_2="60px" padding_left_2="40px" admin_label="Contact Info" _builder_version="3.0.82" animation_style="zoom" animation_intensity_zoom="10%" animation_starting_opacity="100%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)"][et_pb_column type="2_3" _builder_version="3.0.82" padding_bottom="60px" padding_left="40px" padding_right="40px" padding_top="60px" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_text_color="#9271f6" header_line_height="1.3em" custom_margin="||40px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Send us a Message

[/et_pb_text][et_pb_contact_form captcha="off" submit_button_text="Send Message" form_background_color="rgba(0,0,0,0)" module_id="et_pb_contact_form_0" _builder_version="3.0.82" form_field_font_size="16px" form_field_text_color="rgba(114,114,255,0.61)" use_border_color="on" border_color="rgba(71,74,182,0.12)" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_use_icon="off" button_letter_spacing_hover="2px" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="30px" box_shadow_color_button="rgba(146,113,246,0.3)"][et_pb_contact_field field_id="First" field_title="First Name" _builder_version="3.0.82"][/et_pb_contact_field][et_pb_contact_field field_id="Last" field_title="Last Name" _builder_version="3.0.82"][/et_pb_contact_field][et_pb_contact_field field_id="Email" field_title="Email Address" field_type="email" _builder_version="3.0.47"][/et_pb_contact_field][et_pb_contact_field field_id="Phone" field_title="Phone Number" field_type="email" _builder_version="3.0.82"][/et_pb_contact_field][et_pb_contact_field field_id="Message" field_title="Message" field_type="text" fullwidth_field="on" _builder_version="3.0.47"][/et_pb_contact_field][/et_pb_contact_form][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" padding_bottom="60px" padding_left="40px" padding_right="40px" padding_top="60px" background_color="#09e1c0" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.3em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Info

[/et_pb_text][et_pb_text background_layout="dark" _builder_version="3.0.82" text_font_size="16px" text_line_height="3em" custom_margin="||40px|"]267 Banbury Road Summertown, Oxford. 42 Upper Berkeley Street, London. +44 777888 989 hello@massimpressions.com[/et_pb_text][et_pb_social_media_follow background_layout="dark" _builder_version="3.0.82" text_orientation="left" custom_margin="|||"][et_pb_social_media_follow_network social_network="facebook" _builder_version="3.0.82" custom_margin="|6px||" link_shape="rounded_rectangle" follow_button="off" url_new_window="on"]facebook[/et_pb_social_media_follow_network][et_pb_social_media_follow_network social_network="twitter" _builder_version="3.0.82" custom_margin="|6px||" link_shape="rounded_rectangle" follow_button="off" url_new_window="on"]twitter[/et_pb_social_media_follow_network][et_pb_social_media_follow_network social_network="google-plus" _builder_version="3.0.82" custom_margin="|6px||" link_shape="rounded_rectangle" follow_button="off" url_new_window="on"]google-plus[/et_pb_social_media_follow_network][et_pb_social_media_follow_network social_network="instagram" _builder_version="3.0.82" custom_margin="|6px||" link_shape="rounded_rectangle" follow_button="off" url_new_window="on"]instagram[/et_pb_social_media_follow_network][et_pb_social_media_follow_network social_network="linkedin" _builder_version="3.0.82" custom_margin="|6px||" custom_padding="|||" link_shape="rounded_rectangle" follow_button="off" url_new_window="on"]linkedin[/et_pb_social_media_follow_network][/et_pb_social_media_follow][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="FAQ" _builder_version="3.0.82" custom_margin="|||" custom_padding="60px|0px|100px|0px"][et_pb_row _builder_version="3.0.82"][et_pb_column type="4_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" text_orientation="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Frequently Asked Questions

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Praesent non massa egestas? " use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" body_text_color="#8585bd" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Praesent non massa egestas? " use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" body_text_color="#8585bd" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Praesent non massa egestas? " use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" body_text_color="#8585bd" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="200ms" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Praesent non massa egestas? " use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" body_text_color="#8585bd" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Praesent non massa egestas? " use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" body_text_color="#8585bd" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Praesent non massa egestas? " use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" body_text_color="#8585bd" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="200ms" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Subscribe" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_blend="overlay" custom_padding="100px|0px|180px|0px"][et_pb_row custom_margin="|||" padding_top_2="60px" _builder_version="3.0.82" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" locked="off"][et_pb_column type="4_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="42px" header_font_size_last_edited="on|desktop" header_line_height="1.3em" text_orientation="center" max_width="530px" module_alignment="center" custom_padding="|||" locked="off"]

Contact Us

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_signup mailchimp_list="MailChimp|03dac884f0" name_field="on" use_background_color="off" _builder_version="3.0.82" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_use_icon="off" button_letter_spacing_hover="0" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(9,225,192,0.3)"][/et_pb_signup][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" background_color="#f7f8fc" admin_label="Footer" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" global_module="46771" saved_tabs="all"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

Ready to get started?

[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"]

Get in touch, or create an account

[/et_pb_text][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section]]]>
47102 0 0 0
Coding Course https://code-maze.com/et_pb_layout/coding-course/ Mon, 16 Oct 2017 09:16:33 +0000 https://code-maze.com/et_pb_layout/coding-course/ UX Design 8hours[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][et_pb_button button_url="#" button_text="Watch the Preview" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-11.png" _builder_version="3.0.82" animation_style="zoom" animation_direction="left" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" specialty="on" padding_top_2="0px" admin_label="Course Chapters" _builder_version="3.0.82" custom_margin="|||" custom_padding="0px||120px|"][et_pb_column type="2_3" specialty_columns="2" _builder_version="3.0.47"][et_pb_row_inner custom_padding="0px|||" use_custom_gutter="on" gutter_width="4" _builder_version="3.0.82" custom_margin="-80px|||"][et_pb_column_inner type="4_4" saved_specialty_column_type="2_3" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on"][et_pb_blurb title="Chapter 1 : An Introduction to the Basics" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_4.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" admin_label="Chapter" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus sapien.

45min.

[/et_pb_blurb][et_pb_blurb title="Chapter 2 : Information Architecture" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_12.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" admin_label="Chapter" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus sapien.

1hr. 10min.

[/et_pb_blurb][et_pb_blurb title="Chapter 3 : Responsive Design" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-9.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" admin_label="Chapter" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus sapien.

2hrs. 27min.

[/et_pb_blurb][et_pb_blurb title="Chapter 4 : Interaction and Motion" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_16.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" admin_label="Chapter" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus sapien.

2hrs 1min.

[/et_pb_blurb][et_pb_blurb title="Chapter 5 : Designing for Machine Learning" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" admin_label="Chapter" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus sapien.

1hr. 35min.

[/et_pb_blurb][et_pb_button button_url="#" button_text="Back to Course List" button_alignment="left" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%35%%" button_icon_placement="left" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column_inner][/et_pb_row_inner][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" padding_top="0px"][et_pb_blurb title="Jordan Reynolds" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/portraits-circle-small_2.png" image_max_width="100px" admin_label="Instructor" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" text_orientation="center" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset1" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)"]Instructor

Duis egestas aliquet maecenas erat eros, fringilla et leo eget, viverpretium. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus sapien. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

[/et_pb_blurb][et_pb_button button_url="#" button_text="Full Profile" button_alignment="center" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" saved_tabs="all" locked="off"][/et_pb_button][et_pb_button _builder_version="3.0.82"][/et_pb_button][/et_pb_column][/et_pb_section][et_pb_section fb_built="1" background_color="#f7f8fc" admin_label="Footer" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" global_module="46771" saved_tabs="all"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

Ready to get started?

[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"]

Get in touch, or create an account

[/et_pb_text][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section]]]>
47103 0 0 0
Coding Courses https://code-maze.com/et_pb_layout/coding-courses/ Mon, 16 Oct 2017 09:16:42 +0000 https://code-maze.com/et_pb_layout/coding-courses/ Our Courses [/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" max_width="540px" module_alignment="center" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Courses" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px"][et_pb_row custom_padding="0px|0px|27px|0px" custom_margin="|||" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Web Development" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="-80px||10%|" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus.

17 Courses

[/et_pb_blurb][et_pb_blurb title="Python" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_1.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||10%|" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus.

8 Courses

[/et_pb_blurb][et_pb_blurb title="UX Design" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_3.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||10%|" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus.

16 Courses

[/et_pb_blurb][et_pb_blurb title="Database Design" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_5.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||10%|" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus.

7 Courses

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Javascript" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_7.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="-80px||10%|" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" custom_margin_last_edited="on|phone" custom_margin_tablet="0px||10%|"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus.

22 Courses

[/et_pb_blurb][et_pb_blurb title="HTML & CSS" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||10%|" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus.

36 Courses

[/et_pb_blurb][et_pb_blurb title="Intro to Coding" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_4.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||10%|" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus.

2 Courses

[/et_pb_blurb][et_pb_blurb title="Apps & Games" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_6.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_font_size="16px" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||10%|" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor, dapibus nisi. Donec vel lectus.

19 Courses

[/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" background_color="#f7f8fc" admin_label="Footer" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" global_module="46771" saved_tabs="all"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

Ready to get started?

[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"]

Get in touch, or create an account

[/et_pb_text][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section]]]>
47104 0 0 0
Coding Home https://code-maze.com/et_pb_layout/coding-home/ Mon, 16 Oct 2017 09:16:48 +0000 https://code-maze.com/et_pb_layout/coding-home/ A better way to learn to code websites, apps, and games [/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][et_pb_button button_url="#" button_text="View All Courses" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-01.png" _builder_version="3.0.82" custom_margin="||0px|" custom_padding="|||" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" specialty="on" admin_label="All Courses" _builder_version="3.0.82" custom_padding="100px|0px|100px|0px"][et_pb_column type="1_3" _builder_version="3.0.47"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

100's of Courses

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra

himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. In pellentesque

[/et_pb_text][et_pb_button button_url="#" button_text="View All Courses" button_alignment="left" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="2_3" specialty_columns="2" _builder_version="3.0.47"][et_pb_row_inner custom_padding="0px|0px|0px|0px" use_custom_gutter="on" gutter_width="1" module_class_1="et_pb_column_1_2" module_class_2="et_pb_column_1_2" _builder_version="3.0.82" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)"][et_pb_column_inner type="1_2" saved_specialty_column_type="2_3" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on" module_class="et_pb_column_1_2"][et_pb_blurb title="Web Development" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="Python" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_1.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="UX Design" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_3.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="Database Design" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_5.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][/et_pb_column_inner][et_pb_column_inner type="1_2" saved_specialty_column_type="2_3" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on" module_class="et_pb_column_1_2"][et_pb_blurb title="Javascript" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_7.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="HTML & CSS" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="Intro to Coding" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_4.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="Apps & Games" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_6.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][/et_pb_column_inner][/et_pb_row_inner][/et_pb_column][/et_pb_section][et_pb_section fb_built="1" admin_label="Section" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay" custom_padding="100px|0px|180px|0px"][et_pb_row padding_top_1="60px" _builder_version="3.0.82" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%"][et_pb_column type="1_2" _builder_version="3.0.47" padding_top="60px" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-03.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="right" animation_delay="100ms" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Easy As 1...2...3...

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque.

[/et_pb_text][et_pb_button button_url="#" button_text="Start a Free Trial" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" background_color="#f7f8fc" admin_label="Footer" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" global_module="46771" saved_tabs="all"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

Ready to get started?

[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"]

Get in touch, or create an account

[/et_pb_text][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section]]]>
47105 0 0 0
Coding Landing https://code-maze.com/et_pb_layout/coding-landing/ Mon, 16 Oct 2017 09:16:54 +0000 https://code-maze.com/et_pb_layout/coding-landing/ Learn to Code Websites, Apps & Games[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][et_pb_button button_url="#" button_text="View Courses" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-01.png" _builder_version="3.0.82" custom_margin="|||" custom_padding="|||" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" specialty="on" admin_label="All Courses" _builder_version="3.0.82" custom_padding="100px|0px|100px|0px"][et_pb_column type="1_3" _builder_version="3.0.47"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

100's of Courses

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra

himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. In pellentesque

[/et_pb_text][et_pb_button button_url="#" button_text="View All Courses" button_alignment="left" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="2_3" specialty_columns="2" _builder_version="3.0.47"][et_pb_row_inner custom_padding="0px|0px|0px|0px" use_custom_gutter="on" gutter_width="1" module_class_1="et_pb_column_1_2" module_class_2="et_pb_column_1_2" _builder_version="3.0.82" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)"][et_pb_column_inner type="1_2" saved_specialty_column_type="2_3" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on" module_class="et_pb_column_1_2"][et_pb_blurb title="Web Development" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="Python" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_1.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="UX Design" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_3.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="Database Design" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_5.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][/et_pb_column_inner][et_pb_column_inner type="1_2" saved_specialty_column_type="2_3" _builder_version="3.0.47" box_shadow_horizontal="0px" box_shadow_vertical="2px" box_shadow_blur="18px" box_shadow_spread="0px" box_shadow_position="outer" parallax="off" parallax_method="on" module_class="et_pb_column_1_2"][et_pb_blurb title="Javascript" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_7.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="HTML & CSS" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="Intro to Coding" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_4.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][et_pb_blurb title="Apps & Games" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_6.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][/et_pb_column_inner][/et_pb_row_inner][/et_pb_column][/et_pb_section][et_pb_section fb_built="1" admin_label="Features" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay" custom_padding="100px|0px|100px|0px"][et_pb_row padding_top_2="60px" admin_label="Become an Expert" _builder_version="3.0.82" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%"][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-02.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="right" animation_delay="100ms" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.82" padding_top="60px" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Become an expert

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque.

[/et_pb_text][et_pb_button button_url="#" button_text="View All Courses" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Learn by Doing" _builder_version="3.0.82" custom_padding="100px|0px|100px|0px"][et_pb_row custom_padding="90px|0px|90px|0px" _builder_version="3.0.82"][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-8.jpg" _builder_version="3.0.82" max_width="64px" animation_style="zoom" locked="off"][/et_pb_image][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Learn by Doing

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos

himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque.

[/et_pb_text][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-03.png" _builder_version="3.0.82" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][/et_pb_row][et_pb_row custom_padding="90px|0px|90px|0px" admin_label="Build Your Portfolio" _builder_version="3.0.82"][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-04.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="right" animation_delay="100ms" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-9.jpg" _builder_version="3.0.82" max_width="64px" animation_style="zoom" locked="off"][/et_pb_image][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Build your portfolio

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos

himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. In pellentesque

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row custom_padding="90px|0px|90px|0px" custom_margin="|||" admin_label="Achieve Your Goals" _builder_version="3.0.82"][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-10.jpg" _builder_version="3.0.82" max_width="64px" animation_style="zoom"][/et_pb_image][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Achieve your goals

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos

himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. In pellentesque

[/et_pb_text][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-05.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="20%" locked="off"][/et_pb_image][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Testimonial" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay" custom_margin="|||" custom_padding="0px||0px|" box_shadow_style="preset7" box_shadow_horizontal="0px" box_shadow_vertical="80px" box_shadow_color="#ffffff" locked="off"][et_pb_row use_custom_width="on" custom_width_px="1280px" custom_padding="0px|0px|0px|0px" custom_padding_tablet="|10%||10%" custom_padding_phone="|||" make_equal="on" bg_img_2="https://code-maze.com/wp-content/uploads/2019/02/coding-dot-bg.png" padding_top_1="150px" padding_bottom_1="80px" parallax_2="on" padding_1_tablet="100px|||" padding_1_last_edited="on|phone" custom_padding_last_edited="on|tablet" _builder_version="3.0.82"][et_pb_column type="1_2" _builder_version="3.0.82" padding_tablet="100px|||" padding_last_edited="on|phone" padding_bottom="80px" padding_top="150px" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

"
My entire team was prototyping by the end of the first day!

[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="18px" text_text_color="#d4ccff" text_line_height="1.9em" animation_style="slide" animation_direction="top" animation_intensity_slide="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][et_pb_blurb image="https://code-maze.com/wp-content/uploads/2019/02/testimonial-avatar.png" icon_placement="left" background_layout="dark" content_max_width="800px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" body_font_size="16px" body_text_color="#d4ccff" custom_margin="|||" custom_padding="|||" animation_style="fade" locked="off"]

John Smith, Divi Design Initiative

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.82" bg_img="https://code-maze.com/wp-content/uploads/2019/02/coding-dot-bg.png" parallax="on" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-dots.png" _builder_version="3.0.82" custom_margin="|||" animation_style="slide" animation_direction="top" animation_intensity_slide="3%" animation_speed_curve="ease-out"][/et_pb_image][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="FAQ" _builder_version="3.0.82" custom_padding="100px|0px|180px|0px"][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

FAQ

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra

himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. In pellentesque

[/et_pb_text][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Praesent non massa egestas? " use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][et_pb_blurb title="Quisque ante ante lobortis?" use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][et_pb_blurb title="Lorem ipsum dolor sit?" use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Tis at dapibus et congue?" use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][et_pb_blurb title="At dapibus et congue?" use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][et_pb_blurb title=" Sit Etiam porttitor ligula?" use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" background_color="#f7f8fc" admin_label="Footer" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" global_module="46771" saved_tabs="all"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

Ready to get started?

[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"]

Get in touch, or create an account

[/et_pb_text][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section]]]>
47106 0 0 0
Coding Marketing https://code-maze.com/et_pb_layout/coding-marketing/ Mon, 16 Oct 2017 09:17:00 +0000 https://code-maze.com/et_pb_layout/coding-marketing/ Supercharge Your Classroom with a Free Trial of Divi Coding Academy [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="off|off|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque.

[/et_pb_text][et_pb_button button_url="#" button_text="View All Courses" button_alignment="left" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-12.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="slide" animation_direction="left" animation_duration="500ms" animation_delay="100ms" animation_intensity_slide="10%"][/et_pb_image][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Features" _builder_version="3.0.82" custom_margin="|||" custom_padding="100px|0px|100px|0px"][et_pb_row custom_padding="27px|0px|24px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="4_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" module_alignment="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

What We Offer

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="off|off|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Beginner Courses" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_12.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Gamified Learning" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_13.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Realworld Projects" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_14.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Progress Tracking" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_17.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Lessons Plans" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_4.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Teacher Training" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_18.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla.

[/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Our Courses" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay" custom_padding="100px|0px|0px|0px" box_shadow_style="preset7" box_shadow_horizontal="0px" box_shadow_vertical="-80px" box_shadow_color="#ffffff"][et_pb_row padding_top_2="60px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%"][et_pb_column type="4_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" max_width="540px" module_alignment="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Our Most Popular Courses

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="off|off|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="600px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor.

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_video src="https://www.youtube.com/watch?v=FkQuawiGWUw&t=6s" image_src="https://code-maze.com/wp-content/uploads/2019/02/video-overlay.jpg" play_icon_color="#7272ff" _builder_version="3.0.82" box_shadow_style="preset1" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)"][/et_pb_video][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_video src="https://www.youtube.com/watch?v=FkQuawiGWUw&t=6s" image_src="https://code-maze.com/wp-content/uploads/2019/02/video-overlay.jpg" play_icon_color="#7272ff" _builder_version="3.0.82" box_shadow_style="preset1" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"][/et_pb_video][/et_pb_column][/et_pb_row][et_pb_row custom_padding="27px|0px|0px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_video src="https://www.youtube.com/watch?v=FkQuawiGWUw&t=6s" image_src="https://code-maze.com/wp-content/uploads/2019/02/video-overlay.jpg" play_icon_color="#7272ff" _builder_version="3.0.82" box_shadow_style="preset1" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"][/et_pb_video][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_video src="https://www.youtube.com/watch?v=FkQuawiGWUw&t=6s" image_src="https://code-maze.com/wp-content/uploads/2019/02/video-overlay.jpg" play_icon_color="#7272ff" _builder_version="3.0.82" box_shadow_style="preset1" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"][/et_pb_video][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Testimonials" _builder_version="3.0.82" custom_margin="40px|||" custom_padding="100px|0px|100px|0px"][et_pb_row _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" locked="off"][et_pb_column type="4_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" module_alignment="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

What People Are Saying

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="off|off|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][/et_pb_column][/et_pb_row][et_pb_row custom_padding="0px|0px|0px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="4_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_slider admin_label="Testimomial Slider" _builder_version="3.0.82" header_font="||||" header_line_height="1.5em" body_text_color="#8585bd" custom_padding="|||%22" custom_padding_tablet="|||" custom_padding_phone="|||" show_inner_shadow="off"][et_pb_slide background_layout="light" _builder_version="3.0.82" body_line_height="1.9em" text_orientation="center" background_color="#ffffff"]

"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque."

David Cole, Monarch

[/et_pb_slide][et_pb_slide background_layout="light" _builder_version="3.0.82" body_line_height="1.9em" text_orientation="center" background_color="#ffffff"]

"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque."

David Cole, Monarch

[/et_pb_slide][et_pb_slide background_layout="light" _builder_version="3.0.82" body_line_height="1.9em" text_orientation="center" background_color="#ffffff"]

"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque."

David Cole, Monarch

[/et_pb_slide][et_pb_slide background_layout="light" _builder_version="3.0.82" body_line_height="1.9em" text_orientation="center" background_color="#ffffff"]

"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque."

David Cole, Monarch

[/et_pb_slide][/et_pb_slider][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Join" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay" custom_padding="100px|0px|180px|0px" locked="off"][et_pb_row padding_top_2="60px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%"][et_pb_column type="4_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" max_width="540px" module_alignment="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Elevate Your Classroom

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="off|off|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="600px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor.

[/et_pb_text][et_pb_button button_url="#" button_text="Start a Free Trial" button_alignment="center" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" background_color="#f7f8fc" admin_label="Footer" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" global_module="46771" saved_tabs="all"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

Ready to get started?

[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"]

Get in touch, or create an account

[/et_pb_text][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section]]]>
47107 0 0 0
Coding Pricing https://code-maze.com/et_pb_layout/coding-pricing/ Mon, 16 Oct 2017 09:17:06 +0000 https://code-maze.com/et_pb_layout/coding-pricing/ Choose a Plan that Works for You[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row custom_padding="27px|0px|0px|0px" admin_label="Row" _builder_version="3.0.82"][et_pb_column type="4_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_pricing_tables header_background_color="rgba(0,0,0,0)" show_bullet="off" _builder_version="3.0.82" header_font="|on|||" header_font_size="20px" header_text_color="#2e2545" header_line_height="2em" body_font_size="16px" body_text_color="#8585bd" body_line_height="19px" subheader_font="|on|||" subheader_font_size="14px" subheader_text_color="#8585bd" currency_frequency_font="|on|||" currency_frequency_font_size="26px" currency_frequency_text_color="#9271f6" price_font="|on|||" price_text_color="#9271f6" price_line_height="54px" use_border_color="on" border_width="0px" text_orientation="center" custom_margin="|||" custom_padding="30px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_use_icon="off" button_letter_spacing_hover="2px" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(9,225,192,0.36)" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="10%" animation_starting_opacity="100%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" custom_css_pricing_top="border: none;"][et_pb_pricing_table title="7 Day Trial" subtitle="Limited Access" currency="$" per="mo" sum="0" button_url="#" button_text="Try For Free" pricing_item_excluded_color="rgba(133,133,189,0.34)" _builder_version="3.0.82" currency_frequency_text_color="#7272ff" price_text_color="#7272ff" custom_button="on" button_text_size="16px" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(114,114,255,0.3)"]Try 5 lessons in each course No credit card required You can upgrade anytime - Projects with our Cloud IDE - Course completion certificates - Exclusive Slack channel[/et_pb_pricing_table][et_pb_pricing_table title="Premium Account" subtitle="Unlimited Access" currency="$" per="mo" sum="29" button_url="#" button_text="Signup Now" _builder_version="3.0.82" currency_frequency_text_color="#09e1c0" price_text_color="#09e1c0" box_shadow_style="preset4" box_shadow_horizontal="-1px" box_shadow_vertical="0px" box_shadow_color="rgba(71,74,182,0.12)"]Get unlimited access Test your knowledge 100+ guided projects Projects with our Cloud IDE Course completion certificates Exclusive Slack channel[/et_pb_pricing_table][et_pb_pricing_table title="Premium Account" subtitle="Unlimited Access" currency="$" per="mo" sum="49" button_url="#" button_text="Signup Now" _builder_version="3.0.82" custom_button="on" button_text_size="16px" button_bg_color="#9271f6" button_border_width="10px" button_border_color="#9271f6" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(146,113,246,0.3)" box_shadow_style="preset4" box_shadow_horizontal="-1px" box_shadow_vertical="0px" box_shadow_color="rgba(71,74,182,0.12)"]Get unlimited access Test your knowledge 100+ guided projects Projects with our Cloud IDE Course completion certificates Exclusive Slack channel[/et_pb_pricing_table][/et_pb_pricing_tables][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="What You Get" _builder_version="3.0.82" custom_margin="|||" custom_padding="60px|0px|100px|0px"][et_pb_row _builder_version="3.0.82"][et_pb_column type="4_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" text_orientation="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

What you Get by Enrolling

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="100+ Interactive Courses" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_15.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Video Tutorials" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_20.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Coding Challenges" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-10.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="200ms" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="New Monthly Content" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_4.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Mobile App" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_21.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Coding Community" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_16.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="200ms" animation_intensity_zoom="10%" locked="off"]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci.[/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="Become an Expert" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay" custom_padding="100px|0px|100px|0px"][et_pb_row padding_top_2="60px" _builder_version="3.0.82" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%"][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-11.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="right" animation_delay="100ms" animation_intensity_zoom="20%"][/et_pb_image][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.82" padding_top="60px" parallax="off" parallax_method="on"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.3em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

Become an expert

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque.

[/et_pb_text][et_pb_button button_url="#" button_text="View All Courses" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" admin_label="FAQ" _builder_version="3.0.82" custom_padding="100px|0px|180px|0px"][et_pb_row _builder_version="3.0.82" locked="off"][et_pb_column type="4_4" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" text_orientation="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

FAQ

[/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off"][/et_pb_divider][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"]

Himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu.

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="How does the free trial work?" icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][et_pb_blurb title="How often do you release new content?" icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][et_pb_blurb title="Do you offer Virtual Reality Development?" icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.82" parallax="off" parallax_method="on"][et_pb_blurb title="Can I cancel at any time?" icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][et_pb_blurb title="What age groups do you support? " icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][et_pb_blurb title=" What if I get stuck on a lesson?" icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet

[/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" background_color="#f7f8fc" admin_label="Footer" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" global_module="46771" saved_tabs="all"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"]

Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

Ready to get started?

[/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"]

Get in touch, or create an account

[/et_pb_text][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off"][/et_pb_button][/et_pb_column][et_pb_column type="1_4" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off"][/et_pb_button][/et_pb_column][/et_pb_row][/et_pb_section]]]>
47108 0 0 0
New web application template https://code-maze.com/__trashed-2/new-web-application-template-2/ Fri, 03 Jan 2020 09:33:58 +0000 https://code-maze.com/wp-content/uploads/2020/01/New-web-application-template.png 50132 50131 0 0 creating view with template https://code-maze.com/__trashed-2/creating-view-with-template-3/ Fri, 03 Jan 2020 09:48:08 +0000 https://code-maze.com/wp-content/uploads/2020/01/creating-view-with-template.png 50133 50131 0 0 Project type - .net core configuration https://code-maze.com/net-core-web-development-part2/project-type-net-core-configuration/ Fri, 03 Jan 2020 12:43:58 +0000 https://code-maze.com/wp-content/uploads/2018/01/Project-type-.net-core-configuration.png 50139 1053 0 0 a https://code-maze.com/et_pb_layout/a/ Sun, 03 Feb 2019 09:53:06 +0000 https://code-maze.com/et_pb_layout/a/ Configuration? Creating a database? Deployment to production? It's all there, and much more!

ASP.NET Core has many moving parts. We've compiled them in one place and demonstrated how to use them on the real life examples.

[/et_pb_text]]]>
47162 0 0 0
Button Orange https://code-maze.com/et_pb_layout/button-orange/ Tue, 05 Feb 2019 02:00:38 +0000 https://code-maze.com/et_pb_layout/button-orange/ 47185 0 0 0 Rounded image with border https://code-maze.com/et_pb_layout/rounded-image-with-border/ Sun, 10 Feb 2019 18:10:26 +0000 https://code-maze.com/et_pb_layout/rounded-image-with-border/ Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor.

[/et_pb_blurb]]]>
47259 0 0 0
Profile Blurb https://code-maze.com/et_pb_layout/profile-blurb/ Sun, 10 Feb 2019 18:15:26 +0000 https://code-maze.com/et_pb_layout/profile-blurb/ 47260 0 0 0 Purple Header Font https://code-maze.com/et_pb_layout/purple-header-font/ Sun, 10 Feb 2019 18:34:54 +0000 https://code-maze.com/et_pb_layout/purple-header-font/ FAQ [/et_pb_text]]]> 47267 0 0 0 Start Here Page https://code-maze.com/et_pb_layout/start-here-page/ Tue, 12 Feb 2019 17:46:20 +0000 https://code-maze.com/et_pb_layout/start-here-page/ [et_pb_section bb_built="1" fullwidth="off" specialty="off" _builder_version="3.0.89" custom_css_main_element=" max-width: 1180px;|| margin: 0 auto;|| background: #ecf0f5;||" disabled="off" disabled_on="|on|"][et_pb_row _builder_version="3.0.89" background_color_1="#405061" background_color_2="#405061" padding_top_1="20px" padding_right_1="20px" padding_bottom_1="20px" padding_left_1="20px" padding_top_2="20px" padding_right_2="20px" padding_bottom_2="20px" padding_left_2="20px" make_fullwidth="on"][et_pb_column type="1_2"][et_pb_blurb _builder_version="3.0.89" url_new_window="off" use_icon="on" font_icon="%%101%%" use_circle="off" use_circle_border="off" icon_placement="left" use_icon_font_size="on" background_layout="light" icon_color="#ffffff" title="Java dev" url="#" icon_font_size="120px" body_text_color="#ffffff"]

Lorem ipsum dolor sit amet, id ius appareat contentiones, quo cetero aperiri numquam id. Ei incorrupte liberavisse duo, eum malis repudiandae cu

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2"][et_pb_blurb _builder_version="3.0.89" url_new_window="off" use_icon="on" font_icon="%%98%%" use_circle="off" use_circle_border="off" icon_placement="left" use_icon_font_size="on" background_layout="light" icon_color="#ffffff" title="Java dev" url="#" icon_font_size="120px" body_text_color="#ffffff"]

Lorem ipsum dolor sit amet, id ius appareat contentiones, quo cetero aperiri numquam id. Ei incorrupte liberavisse duo, eum malis repudiandae cu

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About Angular

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="168" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About C#

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-latest_articles.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="12" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About Web Dev

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-rest_with_spring.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="13" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][/et_pb_row][et_pb_row][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About HTTP

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="171" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Latest Posts

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-latest_articles.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="168,12,4,3,66,171,86,169,1,170,13" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About DevOps

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-rest_with_spring.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="66" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][/et_pb_row][et_pb_row][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About Continuous Delivery

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="4" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About Continuous Integration

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-latest_articles.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="3" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About Vue.js

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-rest_with_spring.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" include_categories="170" /][/et_pb_column][/et_pb_row][et_pb_row][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Download Book

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Download</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Complete series

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Full guide</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Custom posts

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" fullwidth="off" specialty="off" _builder_version="3.0.89" disabled="off" disabled_on="on||on" custom_css_main_element="background:#ecf0f5"][et_pb_row _builder_version="3.0.89" background_color_1="#405061" background_color_2="#405061" padding_top_1="20px" padding_right_1="20px" padding_bottom_1="20px" padding_left_1="20px" padding_top_2="20px" padding_right_2="20px" padding_bottom_2="20px" padding_left_2="20px" make_fullwidth="on" module_class="tablet-row"][et_pb_column type="1_2"][et_pb_blurb _builder_version="3.0.89" url_new_window="off" use_icon="on" font_icon="%%101%%" use_circle="off" use_circle_border="off" icon_placement="top" use_icon_font_size="on" background_layout="light" icon_color="#ffffff" title="Java dev" url="#" icon_font_size="120px" body_text_color="#ffffff"]

Lorem ipsum dolor sit amet, id ius appareat contentiones, quo cetero aperiri numquam id. Ei incorrupte liberavisse duo, eum malis repudiandae cu

[/et_pb_blurb][/et_pb_column][et_pb_column type="1_2"][et_pb_blurb _builder_version="3.0.89" url_new_window="off" use_icon="on" font_icon="%%98%%" use_circle="off" use_circle_border="off" icon_placement="top" use_icon_font_size="on" background_layout="light" icon_color="#ffffff" title="Java dev" url="#" icon_font_size="120px" body_text_color="#ffffff"]

Lorem ipsum dolor sit amet, id ius appareat contentiones, quo cetero aperiri numquam id. Ei incorrupte liberavisse duo, eum malis repudiandae cu

[/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.89" module_class="tablet-row"][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About Angular

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="168" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About C#

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-latest_articles.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="12" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.89" module_class="tablet-row"][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About Web Dev

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-rest_with_spring.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="13" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About HTTP

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="171" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.89" module_class="tablet-row"][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Latest Posts

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-latest_articles.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="168,12,4,3,66,171,86,169,1,170,13" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About DevOps

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-rest_with_spring.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="66" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.89" module_class="tablet-row"][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About Continuous Delivery

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="4" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About Continuous Integration

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-latest_articles.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" include_categories="3" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" /][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.89" module_class="tablet-row"][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Post About Vue.js

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/02/homepage-rest_with_spring.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_blog _builder_version="3.0.89" posts_number="3" show_content="off" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_comments="off" show_pagination="off" fullwidth="on" use_dropshadow="off" use_overlay="off" background_layout="light" pagination_font_size_tablet="51" pagination_line_height_tablet="2" module_class="home-page-content" header_font="|300||on|||||" include_categories="170" /][/et_pb_column][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Download Book

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Download</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.89" module_class="tablet-row"][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Complete series

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Full guide</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.89" background_layout="light" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

Custom posts

[/et_pb_text][et_pb_image _builder_version="3.0.89" src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][/et_pb_row][/et_pb_section]

]]>
47280 0 0 0
Pricing Tables https://code-maze.com/et_pb_layout/pricing-tables/ Mon, 18 Feb 2019 14:39:40 +0000 https://code-maze.com/et_pb_layout/pricing-tables/ GET THE REAL WORLD ASP.NET CORE BOOK [/et_pb_text][et_pb_text _builder_version="3.0.89" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" locked="off" background_layout="dark"]

Pre-order until March 28 for 50% Off!
Order now and you'll get the updates delivered to you whenever the new version of the REAL WORLD ASP.NET CORE comes out!

[/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row custom_padding="27px|0px|0px|0px" _builder_version="3.0.82"][et_pb_column type="4_4"][et_pb_pricing_tables header_background_color="rgba(0,0,0,0)" show_bullet="on" _builder_version="3.0.89" header_font="|on|||" header_font_size="20px" header_text_color="#2e2545" header_line_height="2em" body_font_size="16px" body_text_color="#8585bd" body_line_height="19px" subheader_font="|on|||" subheader_font_size="14px" subheader_text_color="#8585bd" currency_frequency_font="|on|||" currency_frequency_font_size="26px" currency_frequency_text_color="#9271f6" price_font="|on|||" price_text_color="#9271f6" price_line_height="54px" use_border_color="on" border_width="0px" text_orientation="center" custom_margin="|||" custom_padding="30px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_use_icon="off" button_letter_spacing_hover="2px" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(9,225,192,0.36)" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="10%" animation_starting_opacity="100%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" custom_css_pricing_top="border: none;" center_list_items="off" show_featured_drop_shadow="on" button_icon_placement="right"] [et_pb_pricing_table title="Essentials" currency="$" sum="29.99" button_url="#" button_text="PREORDER (50% OFF)" pricing_item_excluded_color="rgba(0,0,0,0.49)" _builder_version="3.0.89" currency_frequency_text_color="#e09900" price_text_color="#e09900" button_bg_color="#e09900" button_border_color="#d1a470" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(114,114,255,0.3)" use_background_color_gradient="off" background_color_gradient_start="#2b87da" background_color_gradient_end="#29c4a9" background_color_gradient_type="linear" background_color_gradient_direction="180deg" background_color_gradient_direction_radial="center" background_color_gradient_start_position="0%" background_color_gradient_end_position="100%" background_color_gradient_overlays_image="off" parallax="off" parallax_method="on" background_size="cover" background_position="center" background_repeat="no-repeat" background_blend="normal" allow_player_pause="off" featured="off" header_font="||||||||" subheader_font="||||||||" currency_frequency_font="||||||||" price_font="||||||||" button_font="||||||||" button_use_icon="on" box_shadow_horizontal_button="0px" box_shadow_style="none" box_shadow_color="rgba(0,0,0,0.3)" price_font_size="60" price_font_size_tablet="60" price_font_size_phone="60" box_shadow_style_button="none" text_orientation="left" custom_button="off" currency_frequency_font_size="40px" currency_frequency_font_size_tablet="40px" currency_frequency_font_size_phone="40px" price_text_align="center" header_text_align="center" button_alignment="center" body_text_color="#000000" header_font_size="30px" header_font_size_tablet="30px" header_font_size_phone="30px"]

Get the Full Book
Build, test and deploy one full application
-No support
-No bonus content

[/et_pb_pricing_table][et_pb_pricing_table title="Premium" currency="$" sum="49.99" button_url="#" button_text="PREORDER (50% OFF)" _builder_version="3.0.89" currency_frequency_text_color="#e09900" price_text_color="#e09900" box_shadow_style="preset4" box_shadow_horizontal="-1px" box_shadow_vertical="0px" use_background_color_gradient="off" background_color_gradient_start="#2b87da" background_color_gradient_end="#29c4a9" background_color_gradient_type="linear" background_color_gradient_direction="180deg" background_color_gradient_direction_radial="center" background_color_gradient_start_position="0%" background_color_gradient_end_position="100%" background_color_gradient_overlays_image="off" parallax="off" parallax_method="on" background_size="cover" background_position="center" background_repeat="no-repeat" background_blend="normal" allow_player_pause="off" featured="off" header_font="||||||||" subheader_font="||||||||" currency_frequency_font="||||||||" price_font="||||||||" button_font="||||||||" button_use_icon="on" box_shadow_style_button="preset4" box_shadow_color_button="rgba(224,153,0,0.3)" box_shadow_blur="0px" box_shadow_spread="0px" box_shadow_color="rgba(0,0,0,0.3)" text_orientation="left" header_text_align="center" currency_frequency_font_size="40px" currency_frequency_font_size_tablet="40px" currency_frequency_font_size_phone="40px" price_text_align="center" price_font_size="60" price_font_size_tablet="60" price_font_size_phone="60" button_alignment="center" body_text_color="#000000" header_font_size="30px" header_font_size_tablet="30px" header_font_size_phone="30px" button_text_color="#e09900" button_icon="%%36%%" box_shadow_horizontal_button="10px" box_shadow_vertical_button="10px" box_shadow_blur_button="0px" box_shadow_spread_button="0px" button_border_radius="0" custom_button="off"]

All the benefits of Essentials Package
How to Dockerize your Application mini book
HTTP reference tables
Exclusive Slack channel

[/et_pb_pricing_table] [/et_pb_pricing_tables][/et_pb_column][/et_pb_row][/et_pb_section]]]>
47376 0 0 0
Home Page https://code-maze.com/et_pb_layout/home-page/ Fri, 05 Apr 2019 14:28:32 +0000 https://code-maze.com/et_pb_layout/home-page/ [et_pb_section bb_built="1" admin_label="section" _builder_version="3.0.89"][et_pb_row][et_pb_column type="4_4"][et_pb_posts_blog_feed_standard _builder_version="3.0.89" category_id="0" posts_per_page="5" display_featured_posts_only="off" ignore_displayed_posts="off" content_length="excerpt" show_pagination="on" show_author="on" show_categories="off" show_featured_image="on" show_date="on" show_comments="on" show_rating="off" show_more="on" remove_drop_shadow="off" custom_read_more="off" read_more_icon_placement="right" hover_overlay_icon="%%65%%" title_text_color="#3e5062" /][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.89" custom_margin="||70px|"][et_pb_column type="1_2"][et_pb_posts category_id="13" posts_per_page="4" display_featured_posts_only="off" show_thumbnails="on" show_author="on" show_categories="off" show_comments="on" show_rating="off" show_date="on" use_border_color="off" border_color="#ffffff" border_style="solid" remove_drop_shadow="off" _builder_version="3.0.89" ignore_displayed_posts="off" header_text_color="#7ac8cc" main_title_text_color="#7ac8cc" main_meta_text_color="#000000" border_color_all="#7ac8cc" header_font_size="20px" hover_overlay_icon="%%71%%" orderby="comment_count" list_title_text_color="#7ac8cc" /][/et_pb_column][et_pb_column type="1_2"][et_pb_posts category_id="4" posts_per_page="4" display_featured_posts_only="off" show_thumbnails="on" show_author="on" show_categories="off" show_comments="on" show_rating="off" show_date="on" use_border_color="off" border_color="#ffffff" border_style="solid" remove_drop_shadow="off" _builder_version="3.0.89" ignore_displayed_posts="off" border_color_all="#e6543d" header_text_color="#e6543d" main_title_text_color="#e6543d" main_meta_text_color="#000000" main_meta_text_align="left" list_meta_text_align="left" header_font_size="20px" hover_overlay_icon="%%71%%" orderby="comment_count" list_title_text_color="#e6543d" /][/et_pb_column][/et_pb_row][/et_pb_section]

]]>
47872 0 0 0
Blog Feed Standard https://code-maze.com/et_pb_layout/blog-feed-standard/ Fri, 05 Apr 2019 14:30:45 +0000 https://code-maze.com/et_pb_layout/blog-feed-standard/ 47875 0 0 0 Blog Feed https://code-maze.com/et_pb_layout/blog-feed/ Fri, 05 Apr 2019 14:36:43 +0000 https://code-maze.com/et_pb_layout/blog-feed/ 47878 0 0 0 Blog Feed https://code-maze.com/et_pb_layout/blog-feed-2/ Fri, 05 Apr 2019 14:37:31 +0000 https://code-maze.com/et_pb_layout/blog-feed-2/ 47879 0 0 0 Home Page https://code-maze.com/et_pb_layout/home-page-2/ Fri, 05 Apr 2019 14:41:23 +0000 https://code-maze.com/et_pb_layout/home-page-2/ 47882 0 0 0 Homepage Basic https://code-maze.com/et_pb_layout/homepage-basic/ Fri, 03 Jan 2020 22:49:18 +0000 https://code-maze.com/et_pb_layout/homepage-basic/ 50153 0 0 0 Standard Blog Homepage https://code-maze.com/et_pb_layout/standard-blog-homepage/ Fri, 03 Jan 2020 22:49:18 +0000 https://code-maze.com/et_pb_layout/standard-blog-homepage/ 50154 0 0 0 Masonry Blog Homepage https://code-maze.com/et_pb_layout/masonry-blog-homepage/ Fri, 03 Jan 2020 22:49:18 +0000 https://code-maze.com/et_pb_layout/masonry-blog-homepage/ 50155 0 0 0 Magazine Homepage https://code-maze.com/et_pb_layout/magazine-homepage/ Fri, 03 Jan 2020 22:49:18 +0000 https://code-maze.com/et_pb_layout/magazine-homepage/ 50156 0 0 0 Standard Blog Category https://code-maze.com/et_pb_layout/standard-blog-category/ Fri, 03 Jan 2020 22:49:18 +0000 https://code-maze.com/et_pb_layout/standard-blog-category/ 50157 0 0 0 Masonry Blog Category https://code-maze.com/et_pb_layout/masonry-blog-category/ Fri, 03 Jan 2020 22:49:18 +0000 https://code-maze.com/et_pb_layout/masonry-blog-category/ 50158 0 0 0 Standard Blog Category With Featured Posts https://code-maze.com/et_pb_layout/standard-blog-category-with-featured-posts/ Fri, 03 Jan 2020 22:49:18 +0000 https://code-maze.com/et_pb_layout/standard-blog-category-with-featured-posts/ 50159 0 0 0 Masonry Blog Category With Featured Posts https://code-maze.com/et_pb_layout/masonry-blog-category-with-featured-posts/ Fri, 03 Jan 2020 22:49:18 +0000 https://code-maze.com/et_pb_layout/masonry-blog-category-with-featured-posts/ 50160 0 0 0 https://code-maze.com/621/ Wed, 12 Jul 2017 06:01:37 +0000 https://code-maze.com/?p=621 621 0 24 0 Start Here https://code-maze.com/home/ Wed, 12 Jul 2017 06:01:55 +0000 https://code-maze.com/?p=622 622 0 1 0 Homepage https://code-maze.com/layout/homepage/ Thu, 23 Nov 2017 19:30:45 +0000 https://code-maze.com/layout/homepage/ 737 0 0 0 Default Category https://code-maze.com/layout/default-category/ Thu, 23 Nov 2017 19:30:45 +0000 https://code-maze.com/layout/default-category/ 738 0 0 0 https://code-maze.com/1026/ Mon, 08 Jan 2018 06:39:11 +0000 https://code-maze.com/?p=1026 1026 0 5 0 https://code-maze.com/1757/ Sun, 04 Mar 2018 18:48:13 +0000 https://code-maze.com/?p=1757 1757 0 28 0 https://code-maze.com/1758/ Sun, 04 Mar 2018 18:51:00 +0000 https://code-maze.com/?p=1758 1758 0 27 0 https://code-maze.com/1759/ Sun, 04 Mar 2018 18:51:22 +0000 https://code-maze.com/?p=1759 1759 0 31 0 About https://code-maze.com/about/ Sun, 04 Mar 2018 18:54:21 +0000 https://code-maze.com/?p=1760 1760 0 26 0 Series https://code-maze.com/series/ Wed, 11 Apr 2018 15:49:39 +0000 https://code-maze.com/?p=2439 2439 0 3 0 https://code-maze.com/2754/ Sun, 29 Apr 2018 12:59:52 +0000 https://code-maze.com/?p=2754 2754 0 20 0 https://code-maze.com/2859/ Thu, 03 May 2018 07:42:32 +0000 https://code-maze.com/?p=2859 2859 0 18 0 https://code-maze.com/2970/ Tue, 08 May 2018 06:18:15 +0000 https://code-maze.com/?p=2970 2970 0 22 0 https://code-maze.com/4376/ Mon, 13 Aug 2018 09:37:49 +0000 https://code-maze.com/?p=4376 4376 0 23 0 https://code-maze.com/5355/ Sun, 02 Dec 2018 18:42:54 +0000 https://code-maze.com/?p=5355 5355 0 21 0 https://code-maze.com/5356/ Sun, 02 Dec 2018 18:42:54 +0000 https://code-maze.com/?p=5356 5356 0 17 0 https://code-maze.com/5357/ Sun, 02 Dec 2018 18:42:54 +0000 https://code-maze.com/?p=5357 5357 0 16 0 C# Series https://code-maze.com/c-series/ Sun, 02 Dec 2018 18:42:54 +0000 https://code-maze.com/?p=5358 5358 0 15 0 Angular https://code-maze.com/angular/ Sun, 02 Dec 2018 18:42:54 +0000 https://code-maze.com/?p=5359 5359 0 19 0 Best Practices https://code-maze.com/best-practices/ Sun, 17 Feb 2019 09:57:26 +0000 https://code-maze.com/?p=47318 47318 0 9 0 Kubernetes Series https://code-maze.com/?page_id=2221 https://code-maze.com/?page_id=2221
  • Windows 10 or any newer Linux OS (We're going to use Win10 with Linux containers through the series)
  • Docker for Windows 10
  • Docker compose
  • Kubernetes
  • Helm
  • Continuous Integration Server
  • Visual Studio 2017 Community 15.5.2 or VS Code (you might go for another IDE if you like, that's the part of the beauty)
  • Kubernetes series
    • Introducing Kubernetes and Minikube
    • Orchestrating ASP.NET Core App and MySQL with Minkube
    • Configuring Kubernetes With Helm
    • Creating a Build Configuration with Codefresh (Kubernetes and Helm)
    • Introducing GKE and Configuring GKE Cluster
    • Kubernetes Logging
    • Recap: Going Entire Continuous Delivery Lifecycle
     ]]>
    2221 0 0 0
    No Access https://code-maze.com/?page_id=2889 Fri, 04 May 2018 11:35:31 +0000 https://code-maze.com/no-access/ 2889 0 0 0 Syntax highlight https://code-maze.com/?page_id=3304 https://code-maze.com/?page_id=3304 using System; namespace TestApplication { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); Console.ReadLine(); } } }  
    using System.IO.Compression;
    
    #pragma warning disable 414, 3021
    
    namespace MyApplication
    {
        [Obsolete("...")]
        class Program : IInterface
        {
            public static List<int> JustDoIt(int count)
            {
                Console.WriteLine($"Hello {Name}!");
                return new List<int>(new int[] { 1, 2, 3 })
            }
        }
    }
    
    ]]>
    3304 0 0 0
    Shop https://code-maze.com/?page_id=5553 Sun, 20 Jan 2019 19:00:58 +0000 https://code-maze.com/shop/ 5553 0 0 0 Cart https://code-maze.com/?page_id=5554 Sun, 20 Jan 2019 19:00:58 +0000 https://code-maze.com/cart/ 5554 0 0 0 Checkout https://code-maze.com/?page_id=5555 Sun, 20 Jan 2019 19:00:58 +0000 https://code-maze.com/checkout/ 5555 0 0 0 My account https://code-maze.com/?page_id=5556 Sun, 20 Jan 2019 19:00:58 +0000 https://code-maze.com/my-account/ 5556 0 0 0 https://code-maze.com/47319/ Sun, 17 Feb 2019 09:57:26 +0000 https://code-maze.com/?p=47319 47319 0 10 0 https://code-maze.com/47320/ Sun, 17 Feb 2019 09:57:26 +0000 https://code-maze.com/?p=47320 47320 0 13 0 https://code-maze.com/47321/ Sun, 17 Feb 2019 09:57:26 +0000 https://code-maze.com/?p=47321 47321 0 11 0 https://code-maze.com/47322/ Sun, 17 Feb 2019 09:57:26 +0000 https://code-maze.com/?p=47322 47322 0 12 0 https://code-maze.com/47547/ Thu, 14 Mar 2019 07:08:13 +0000 https://code-maze.com/?p=47547 47547 0 25 0 https://code-maze.com/47674/ Sat, 23 Mar 2019 08:18:10 +0000 https://code-maze.com/?p=47674 47674 0 14 0 Latest Articles https://code-maze.com/latest-articles/ Fri, 05 Apr 2019 14:25:39 +0000 https://code-maze.com/?p=47870 47870 0 2 0 ASP.NET Core https://code-maze.com/asp-net-core/ Mon, 27 May 2019 06:10:27 +0000 https://code-maze.com/?p=48338 48338 0 4 0 GraphQL ASP.NET Core https://code-maze.com/graphql-asp-net-core/ Mon, 27 May 2019 06:10:29 +0000 https://code-maze.com/?p=48339 48339 0 6 0 https://code-maze.com/48433/ Sat, 22 Jun 2019 21:24:07 +0000 https://code-maze.com/?p=48433 48433 0 7 0 https://code-maze.com/48860/ Tue, 24 Sep 2019 09:31:18 +0000 https://code-maze.com/?p=48860 48860 0 29 0 https://code-maze.com/48894/ Fri, 27 Sep 2019 16:58:22 +0000 https://code-maze.com/?p=48894 48894 0 30 0 https://code-maze.com/49000/ Thu, 10 Oct 2019 19:18:44 +0000 https://code-maze.com/?p=49000 49000 0 8 0 About Us https://code-maze.com/about/ Mon, 01 Feb 2016 20:13:35 +0000 http://www.code-maze.com/blog/?page_id=2 profile imageVladimir Pecanac Hi, my name is Vladimir Pecanac, and I am full time .NET developer and DevOps enthusiast. I founded this blog so I can share the things I learn in the hope of both helping others and deepening my own knowledge of the topics I write about. The best way to learn is to teach. When I first entered the software development world, I had quite a bit of trouble understanding a lot of the technical articles I read. Since I didn't know much at that time or had any practical experience, I thought I was to blame. A few years ahead, I read some of those articles again but didn't find them any easier to digest. That's when I realized it was not entirely my fault. I feel that many technical articles are written in an unnecessarily complicated way to sound more authoritative and serious. My goal is to change that trend and write down-to-earth, simple articles that are easy to read and understand. Having said all that, I hope you will enjoy reading my articles and learn something new in the process. See you in the comments section.

    Marinko Spasojevic

    marinkoHi, my name is Marinko Spasojevic. Currently, I work as a full time .NET developer and my passion is web application development. Just getting something to work is not enough for me. To make it just how I like it, it must be readable, reusable and easy to maintain. Prior to being an author on the CodeMaze blog, I had been working as a professor of Computer Science for several years. So, sharing knowledge while working as a full-time developer comes naturally to me. My hope is that the experience and knowledge I share will help you improve your own and that you will learn something new by reading my articles. Thanks for coming to the Code Maze, I look forward to reading your comments.  ]]>
    2 0 0 0
    HTTP Series https://code-maze.com/http-series/ Tue, 11 Jul 2017 18:09:21 +0000 https://code-maze.com/?page_id=607 Knowing the basics is important. A lot of information exchanged on the Internet is carried over HTTP. So, if you want to build great web apps, isn't it obvious that you need to understand what happens behind the scenes? In this series, you will learn anything from the most basic stuff like how HTTP messages look, to the more complicated concepts like Digest authentication and HTTPS/SSL. This will give you a deeper insight into the world of internet communication and help you build more robust web applications. This series contains a lot of information, but every bit is as important as the other. Before I've written this series of articles, I realized how much fuzzy my knowledge of some concepts is, and it helped me understand them better, and I hope it will do that for you too. And there is even more! For your convenience (and mine too) we added an extra chapter called "The HTTP reference" which contains a cheat sheet of every HTTP concept that is hard to remember by heart such as status codes, HTTP headers, MIME types and HTTP request methods. The series is comprised of following parts: ]]> 607 0 -1 0 ASP.NET Core Series https://code-maze.com/net-core-series/ Mon, 08 Jan 2018 06:37:37 +0000 https://code-maze.com/?page_id=1008 .NET Core Series In the ASP.NET Core series, we are going to go through a detailed example of how to use .NET Core, Angular and MySQL for ASP.NET Core web application development. If someone asks: “Why this combination of technologies”, without getting in too much detail, the answer would be:
    • The technologies are free of charge
    • Applications can be deployed on both Windows and Linux OS
    • Production-grade performance
    • … And because we can :)

    What are we going to do in this guide?

    We are going to use MySQL as our database. First, we are going to install the MySQL server, create tables and populate them with some data. Then, we are going to step into the world of ASP.NET Core Web API development. It is going to be our server-side part of the application. As we progress through the ASP.NET Core series, we are going to use repository pattern, generics, LINQ, entity framework core, create more projects and services to demonstrate some good practices. Overall we will try to write the application as we would in the real-time environment. Furthermore, you will learn about ASP.NET Core architecture and code organization, so you can make it more readable and maintainable. There are three approaches to using Entity Framework: Database First, Code First and Model First. In this tutorial, we are going to use the Database First approach, because we want to create our database prior to typing .NET code. This approach is good when you know the structure of your database beforehand, and we get to use the visual editor which makes the creation of relationships between tables much easier. After we finish the ASP.NET Core series, we are going to introduce three of the most popular client frameworks (Angular, React or Vue.js) to consume our Web API. This will result in creating a full-stack web application. In the end, we are going to publish our app on both Windows and Linux OS, and finish strong by completing the entire development cycle.

    Prerequisites

    For this series you will need:

    Installation

    To install MySQL on your machine, follow this guide. You can install Visual Studio 2019 by following these instructions (In the section where you select workloads, please select ASP.NET and web development section).

    Background

    This tutorial will be separated into several parts:

    To learn how to consume ASP.NET Core Web API, check out:

    Hosting ASP.NET Core Web API on different platforms:

    To improve your API with some advanced techniques you might want to check out:

    ]]>
    1008 0 0 0
    Privacy Policy https://code-maze.com/privacy-policy/ Fri, 12 Jan 2018 19:42:56 +0000 https://code-maze.com/?page_id=1102
    This privacy policy has been compiled to better serve those who are concerned with how their 'Personally Identifiable Information' (PII) is being used online. PII, as described in US privacy law and information security, is information that can be used on its own or with other information to identify, contact, or locate a single person, or to identify an individual in context. Please read our privacy policy carefully to get a clear understanding of how we collect, use, protect or otherwise handle your Personally Identifiable Information in accordance with our website.
     
    What personal information do we collect from the people that visit our blog, website or app?
    When ordering or registering on our site, as appropriate, you may be asked to enter your name, email address or other details to help you with your experience.
    When do we collect information?
    We collect information from you when you place an order, subscribe to a newsletter or enter information on our site.
     
    How do we use your information?
    We may use the information we collect from you when you register, make a purchase, sign up for our newsletter, respond to a survey or marketing communication, surf the website, or use certain other site features in the following ways:
           To improve our website in order to better serve you.
           To allow us to better service you in responding to your customer service requests.
           To ask for ratings and reviews of services or products
     
    How do we protect your information?
    We do not use vulnerability scanning and/or scanning to PCI standards.
    We only provide articles and information. We never ask for credit card numbers.
    We do not use Malware Scanning.
    Your personal information is contained behind secured networks and is only accessible by a limited number of persons who have special access rights to such systems, and are required to keep the information confidential. In addition, all sensitive/credit information you supply is encrypted via Secure Socket Layer (SSL) technology.
    We implement a variety of security measures when a user places an order enters, submits, or accesses their information to maintain the safety of your personal information.
    All transactions are processed through a gateway provider and are not stored or processed on our servers.
     
    Do we use 'cookies'?
    Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow) that enables the site's or service provider's systems to recognize your browser and capture and remember certain information. For instance, we use cookies to help us remember and process the items in your shopping cart. They are also used to help us understand your preferences based on previous or current site activity, which enables us to provide you with improved services. We also use cookies to help us compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future.
    We use cookies to:
           Understand and save user's preferences for future visits.
           Keep track of advertisements.
    You can choose to have your computer warn you each time a cookie is being sent, or you can choose to turn off all cookies. You do this through your browser settings. Since browser is a little different, look at your browser's Help Menu to learn the correct way to modify your cookies.
    If you turn cookies off, Some of the features that make your site experience more efficient may not function properly.It won't affect the user's experience that make your site experience more efficient and may not function properly.
     
    Third-party disclosure
    We do not sell, trade, or otherwise transfer to outside parties your Personally Identifiable Information unless we provide users with advance notice. This does not include website hosting partners and other parties who assist us in operating our website, conducting our business, or serving our users, so long as those parties agree to keep this information confidential. We may also release information when it's release is appropriate to comply with the law, enforce our site policies, or protect ours or others' rights, property or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.
     
    Third-party links
    Occasionally, at our discretion, we may include or offer third-party products or services on our website. These third-party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.
     
    Google
    Google's advertising requirements can be summed up by Google's Advertising Principles. They are put in place to provide a positive experience for users. https://support.google.com/adwordspolicy/answer/1316548?hl=en
    We use Google AdSense Advertising on our website.
    Google, as a third-party vendor, uses cookies to serve ads on our site. Google's use of the DART cookie enables it to serve ads to our users based on previous visits to our site and other sites on the Internet. Users may opt-out of the use of the DART cookie by visiting the Google Ad and Content Network privacy policy.
    We have implemented the following:
           Remarketing with Google AdSense
           DoubleClick Platform Integration
    We, along with third-party vendors such as Google use first-party cookies (such as the Google Analytics cookies) and third-party cookies (such as the DoubleClick cookie) or other third-party identifiers together to compile data regarding user interactions with ad impressions and other ad service functions as they relate to our website.
    Opting out: Users can set preferences for how Google advertises to you using the Google Ad Settings page. Alternatively, you can opt out by visiting the Network Advertising Initiative Opt Out page or by using the Google Analytics Opt Out Browser add on.
     
    California Online Privacy Protection Act
    CalOPPA is the first state law in the nation to require commercial websites and online services to post a privacy policy. The law's reach stretches well beyond California to require any person or company in the United States (and conceivably the world) that operates websites collecting Personally Identifiable Information from California consumers to post a conspicuous privacy policy on its website stating exactly the information being collected and those individuals or companies with whom it is being shared. - See more at: http://consumercal.org/california-online-privacy-protection-act-caloppa/#sthash.0FdRbT51.dpuf
    According to CalOPPA, we agree to the following:
    Users can visit our site anonymously.
    Once this privacy policy is created, we will add a link to it on our home page or as a minimum, on the first significant page after entering our website.
    Our Privacy Policy link includes the word 'Privacy' and can easily be found on the page specified above.
    You will be notified of any Privacy Policy changes:
           On our Privacy Policy Page
    Can change your personal information:
           By emailing us
    How does our site handle Do Not Track signals?
    We honor Do Not Track signals and Do Not Track, plant cookies, or use advertising when a Do Not Track (DNT) browser mechanism is in place.
    Does our site allow third-party behavioral tracking?
    It's also important to note that we allow third-party behavioral tracking
     
    COPPA (Children Online Privacy Protection Act)
    When it comes to the collection of personal information from children under the age of 13 years old, the Children's Online Privacy Protection Act (COPPA) puts parents in control. The Federal Trade Commission, United States' consumer protection agency, enforces the COPPA Rule, which spells out what operators of websites and online services must do to protect children's privacy and safety online.
    We do not specifically market to children under the age of 13 years old.
    Do we let third-parties, including ad networks or plug-ins collect PII from children under 13?
     
    Fair Information Practices
    The Fair Information Practices Principles form the backbone of privacy law in the United States and the concepts they include have played a significant role in the development of data protection laws around the globe. Understanding the Fair Information Practice Principles and how they should be implemented is critical to comply with the various privacy laws that protect personal information.
    In order to be in line with Fair Information Practices we will take the following responsive action, should a data breach occur:
    We will notify you via email
           Within 7 business days
    We will notify the users via in-site notification
           Within 7 business days
    We also agree to the Individual Redress Principle which requires that individuals have the right to legally pursue enforceable rights against data collectors and processors who fail to adhere to the law. This principle requires not only that individuals have enforceable rights against data users, but also that individuals have recourse to courts or government agencies to investigate and/or prosecute non-compliance by data processors.
     
    CAN SPAM Act
    The CAN-SPAM Act is a law that sets the rules for commercial email, establishes requirements for commercial messages, gives recipients the right to have emails stopped from being sent to them, and spells out tough penalties for violations.
    We collect your email address in order to:
           Send information, respond to inquiries, and/or other requests or questions
           Market to our mailing list or continue to send emails to our clients after the original transaction has occurred.
    To be in accordance with CANSPAM, we agree to the following:
           Not use false or misleading subjects or email addresses.
           Identify the message as an advertisement in some reasonable way.
           Include the physical address of our business or site headquarters.
           Monitor third-party email marketing services for compliance, if one is used.
           Honor opt-out/unsubscribe requests quickly.
           Allow users to unsubscribe by using the link at the bottom of each email.
    If at any time you would like to unsubscribe from receiving future emails, you can email us at
           Follow the instructions at the bottom of each email.
    and we will promptly remove you from ALL correspondence.
     
    Contacting Us
    If there are any questions regarding this privacy policy, you may contact us using the information below.
    Karlovacki Drum 16
    Novi Sad, Serbia 21000
    Serbia
    ]]>
    1102 0 0 0
    Disclosure Policy https://code-maze.com/disclosure-policy/ Fri, 12 Jan 2018 19:56:08 +0000 https://code-maze.com/?page_id=1113 vladimir.pecanac@gmail.com. This blog accepts forms of cash advertising, sponsorship, paid insertions or other forms of compensation. The compensation received will never influence the content, topics or posts made on this blog. All advertising is in the form of advertisements generated by a third party ad network. Those advertisements will be identified as paid advertisements. The owner(s) of this blog is not compensated to provide opinion on products, services, websites and various other topics. The views and opinions expressed on this blog are purely the blog owners. If we claim or appear to be experts on a certain topic or product or service area, we will only endorse products or services that we believe, based on our expertise, are worthy of such endorsement. Any product claim, statistic, quote or other representation about a product or service should be verified with the manufacturer or provider. This blog does not contain any content which might present a conflict of interest.]]> 1113 0 0 0 Terms of Service https://code-maze.com/terms-of-service/ Fri, 12 Jan 2018 20:17:21 +0000 https://code-maze.com/?page_id=1118 Links To Other Web Sites Our Service may contain links to third-party web sites or services that are not owned or controlled by CodeMaze. CodeMaze has no control over, and assumes no responsibility for, the content, privacy policies, or practices of any third party web sites or services. You further acknowledge and agree that CodeMaze shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such content, goods or services available on or through any such web sites or services. We strongly advise you to read the terms and conditions and privacy policies of any third-party web sites or services that you visit.

    Governing Law

    These Terms shall be governed and construed in accordance with the laws of Serbia, without regard to its conflict of law provisions. Our failure to enforce any right or provision of these Terms will not be considered a waiver of those rights. If any provision of these Terms is held to be invalid or unenforceable by a court, the remaining provisions of these Terms will remain in effect. These Terms constitute the entire agreement between us regarding our Service, and supersede and replace any prior agreements we might have between us regarding the Service.

    Changes

    We reserve the right, at our sole discretion, to modify or replace these Terms at any time. If a revision is material we will try to provide at least 30 days notice prior to any new terms taking effect. What constitutes a material change will be determined at our sole discretion. By continuing to access or use our Service after those revisions become effective, you agree to be bound by the revised terms. If you do not agree to the new terms, please stop using the Service.

    Contact Us

    If you have any questions about these Terms, please contact us.]]>
    1118 0 0 0
    Disclaimer https://code-maze.com/disclaimer/ Fri, 12 Jan 2018 20:22:55 +0000 https://code-maze.com/?page_id=1123 Disclaimer Template for CodeMaze.]]> 1123 0 0 0 The Complete Guide to HTTP Book https://code-maze.com/the-complete-guide-to-http/ Tue, 13 Feb 2018 23:24:29 +0000 https://code-maze.com/?page_id=1514 The Complete Guide to HTTP ebook. This eBook has originated from the HTTP series we've written some time ago and it contains all the information from the series, neatly bundled and organized in one place. Plus the bonus content. You'll learn anything from the most basic stuff like how HTTP messages are formed, to the more complicated concepts like Digest authentication, HTTPS/SSL, and content negotiation. This will give you a deeper insight into the world of internet communication and help you build more robust web applications. There is a ton of information, but every bit is as important as the other so take your time while reading it. Before we’ve written this ebook, we realized how much fuzzy our knowledge of some concepts was. Writing it helped us understand them better. We hope it will do that for you too. We've written all the content in an easily digestible way, so you don't need to go through the crazy RFCs and a parse through a lot of highly technical pages to learn HTTP. And there is even more! The book contains an extra chapter on advanced features of HTTP. Be sure to check it out. You can get it completely free by [thrive_2step id='2792']subscribing to our blog[/thrive_2step].  

    [thrive_2step id='2792']Download your Free Book! [/thrive_2step]

    ]]>
    1514 0 0 0
    Write for Code Maze https://code-maze.com/write-for-codemaze/ Sun, 04 Mar 2018 18:41:35 +0000 https://code-maze.com/?page_id=1723 We are looking for good writers out there.

    About the Website

    CodeMaze is the place where we share our knowledge about Microsoft technologies, focusing on web development. We are trying to help people understand how to make web applications using the best practices and newest technologies. That also includes utilizing modern CI tooling and Containers. When we write articles, we are trying to convey the topic clearly without too much-complicated terminology. We believe that everyone should be able to understand and apply the knowledge gained by reading our content. The content can be both hands-on or theory-oriented. Top lists and tool comparisons are welcome too. Our reach is 300k page views monthly and rising.

    Who Should Apply

    We expect our authors to have a decent mastery of the English language, as well as technical understanding of the article topic. While previous writing experience is a plus, it's certainly not necessary to be able to apply.

    Publishing Process Schedule

    Once you submit an article, we are going to review it and give you technical and language feedback. We will help you tidy up and wrap up the article in the shortest time manageable. We are trying to publish quality articles first and foremost, so the length of the article should be at least 1000 words. If you feel your article should be shorter, we can review it and discuss it further. Some exceptions to the rule are allowed. Currently, our publishing schedule is one or two articles weekly but it's flexible. Depending on how much articles we have pending, your article should be published in a week or two.

    Payment

    Now the fun part. We don't expect you to write for us for fame and glory, but you can write using your own name. Otherwise, we will pay you for the work you invest in producing content. The price can vary depending on how long the article is and how much work we had to do to get it to the finish line. The price will go up as the blog grows. Of course, the writers who stay with us longer will get paid more and maybe even earn their place among the regular CodeMaze authors. We typically handle payments just via PayPal for now, with more options coming later.

    Attribution

    There are three ways to get your article published on CodeMaze.
    • Under generic CodeMaze account (you get paid)
    • Using your name and profile (non-paid variant, your typical guest blog post)
    • Published using your own name and getting paid (this is reserved for the writers who stick with us longer and help us grow the blog considerably)
    While almost everyone likes getting paid, there are some benefits of guest posting using your own name and linking to your website. So we are keeping that option available too.

    Content Topics

    There are a variety of topics available to write about, but we are trying to limit our content to these major topics:
    • .NET Core C# or F# application development
    • ASP.NET Core application development
    • Angular 2+, React.js, or some other technology combined with .NET Core backend
    • General web knowledge, like HTTP and REST
    • Best practices in web development
    • Security, authentication, and authorization
    • Continuous Integration and Delivery (tools, processes, deployment)
    • Useful .NET Core libraries
    • ...
    You can always suggest your own idea and we will consider it. The important thing to note is that we have zero tolerance for plagiarism of any sort. The articles should be original, expressed in your own opinion and words.

    How to Get Started?

    You can start by introducing yourself to us by using the contact form. After we get to know you, we are going to make you a Contributor account. That way you'll be able to write an article, but not to publish it. Before you start writing we are going to share some guidelines to make your job easier (and ours too). Be sure to go through our article formatting guidelines, good writing guidelines, and code formatting guidelines. Once you finish your article, you can contact us again and we're going to review the article. After that, we'll go together through the feedback and give suggestions on how to improve it. Upon integration of the feedback, we hit Publish and congrats, you are online!   Cheers, Vladimir Pecanac]]>
    1723 0 0 0
    Contact https://code-maze.com/contact/ Sun, 04 Mar 2018 18:30:23 +0000 https://code-maze.com/?page_id=1748 1748 0 0 0 Testing forms and links https://code-maze.com/?page_id=2795 https://code-maze.com/?page_id=2795
  • Creating a Database for Our Project
  • Creating a Database for Our Project (Current article)
  • Creating a Database for Our Project2
  • Creating a Database for Our Project3
  • [sc name="part_of_series" headline="This article is part of the series"] [sc name="subscribe" formNumber="2792" contentType="Web Development"] [sc name="subscribe" formNumber="2797" contentType=".NET Core"] [sc name="subscribe" formNumber="3785" contentType="Angular"] [sc name="subscribe" formNumber="3820" contentType="CI/CD"] [sc name="subscribe" formNumber="47515" contentType="Angular Material"]   Buy my product]]>
    2795 0 0 0 Book-download]]>
    Sales Page https://code-maze.com/?page_id=5828 Sat, 02 Feb 2019 21:08:05 +0000 https://code-maze.com/?page_id=5828 [/et_pb_text][et_pb_divider _builder_version="3.0.89" show_divider="on" background_color="#e02b20" color="#ff0000" divider_weight="40" height="40px" /][et_pb_testimonial _builder_version="3.0.89" author="Petar Petrovic" job_title="Software Developer" company_name="Microsoft" portrait_url="https://code-maze.com/wp-content/uploads/2016/02/profile_image.png" url="Code Maze" url_new_window="on" quote_icon="on" use_background_color="on" quote_icon_background_color="#f5f5f5" background_layout="light"] “There’s no way we could have hired this many people and gotten so much business had we not had all of those back-office systems figured out. It’s been easier growing our company with a system that is so easy and scalable.” [/et_pb_testimonial][/et_pb_column][/et_pb_row][/et_pb_section]]]> 5828 0 0 0 Book Sales Page https://code-maze.com/?page_id=47109 https://code-maze.com/?page_id=47109 Learn Real World ASP.NET Core [/et_pb_text][et_pb_text _builder_version="3.0.89" text_font_size="16px" text_text_color="#ffffff" text_line_height="1.9em" locked="off" background_layout="light"] A down to earth approach to learning how to create ASP.NET Core Web API, according to REST best practices and with real-world examples that you can understand. [/et_pb_text][et_pb_button button_url="#" button_text="PREORDER (50% OFF)" _builder_version="3.0.89" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#e09900" button_border_width="10px" button_border_color="#e09900" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset2" box_shadow_blur="0px" box_shadow_color="rgba(147,88,0,0.6)" saved_tabs="all" locked="off" url_new_window="off" background_layout="light" button_icon_placement="right" box_shadow_horizontal="3px" global_module="47185" box_shadow_vertical="3px" /][/et_pb_column][et_pb_column type="1_2"][et_pb_image src="https://code-maze.com/wp-content/uploads/2018/07/NetCoreBestPractices7.png" _builder_version="3.0.89" custom_margin="|||" custom_padding="|||" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="20%" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="All Courses" specialty="on" _builder_version="3.0.89" custom_padding="100px|0px|100px|0px"][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" text_text_color="#160909" header_font="|on|||" header_font_size="36px" header_text_color="#160a0a" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off" background_layout="light" text_text_shadow_style="preset5" text_text_shadow_horizontal_length="0.2em" text_text_shadow_vertical_length="0.2em" text_text_shadow_color="rgba(0,0,0,0.12)"]

    Straight to the point approach!

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.89" text_font_size="16px" text_text_color="#bcbcbc" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off" background_layout="light" saved_tabs="all"] Configuration? Creating a database? Deployment to production? It's all there, and much more! ASP.NET Core has many moving parts. We've compiled them in one place and demonstrated how to use them on the real life examples. [/et_pb_text][et_pb_button button_url="#" button_text="PREORDER (50% OFF)" _builder_version="3.0.89" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#e09900" button_border_width="10px" button_border_color="#e09900" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset2" box_shadow_blur="0px" box_shadow_color="rgba(147,88,0,0.6)" saved_tabs="all" locked="off" url_new_window="off" background_layout="light" button_icon_placement="right" box_shadow_horizontal="5px" global_module="47185" /][/et_pb_column][et_pb_column type="2_3" specialty_columns="2"][et_pb_row_inner custom_padding="0px|0px|0px|0px" use_custom_gutter="on" gutter_width="1" module_class_1="et_pb_column_1_2" module_class_2="et_pb_column_1_2" _builder_version="3.0.82" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)"][et_pb_column_inner type="1_2" saved_specialty_column_type="2_3"][et_pb_blurb title="Project Architecture" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" url_new_window="off" use_icon="off" use_circle="off" use_circle_border="off" use_icon_font_size="off" background_layout="light"] We'll show you how to extend your configuration and set up the project structure in ASP.NET Core projects. You'll learn to write clean and modular apps. [/et_pb_blurb][et_pb_blurb title="Creating REST API" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_1.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" url_new_window="off" use_icon="off" use_circle="off" use_circle_border="off" use_icon_font_size="off" background_layout="light"] Creating an API is not that hard. Creating a GOOD API, on the other hand, is much harder. We'll introduce concepts one by one and implement best practices while we do that. [/et_pb_blurb][et_pb_blurb title="User Experience" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_3.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" url_new_window="off" use_icon="off" use_circle="off" use_circle_border="off" use_icon_font_size="off" background_layout="light"] User experience makes a big part of web application development. If the user is happy, you did your job well. Let's learn how to get most out of SignalR to achieve better UX. [/et_pb_blurb][et_pb_blurb title="Deployment" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_5.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" url_new_window="off" use_icon="off" use_circle="off" use_circle_border="off" use_icon_font_size="off" background_layout="light"] There's no point in making a web application no one can see. We'll show you how to deploy your application on IIS and make sure everyone gets to see the application you've worked hard on. [/et_pb_blurb][/et_pb_column_inner][et_pb_column_inner type="1_2" saved_specialty_column_type="2_3"][et_pb_blurb title="Database" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_5.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" url_new_window="off" use_icon="off" use_circle="off" use_circle_border="off" use_icon_font_size="off" background_layout="light"] Whether you prefer Code First or Database First approach when modeling your databases, we won't leave you hanging. We'll show you how to do both! [/et_pb_blurb][et_pb_blurb title="Security" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" url_new_window="off" use_icon="off" use_circle="off" use_circle_border="off" use_icon_font_size="off" background_layout="light"] Many developers agree that securing your application is the hardest part. You'll learn how to secure your APIs using JSON Web Tokens (JWT) and protect your resources properly. [/et_pb_blurb][et_pb_blurb title="Data Handling" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_4.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" url_new_window="off" use_icon="off" use_circle="off" use_circle_border="off" use_icon_font_size="off" background_layout="light"] The crucial part of every application is data handling. How you handle data can determine your application longevity and stability. That's where the Repository pattern comes in. [/et_pb_blurb][et_pb_blurb title="Unit Testing" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_6.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="|||" custom_padding="30px|30px|30px|30px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset7" box_shadow_horizontal="-1px" box_shadow_vertical="-1px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" url_new_window="off" use_icon="off" use_circle="off" use_circle_border="off" use_icon_font_size="off" background_layout="light"] Testing is often a forgotten aspect of application development. But, we didn't forget it! We make sure that our applications are properly covered with tests, and so should you! [/et_pb_blurb][/et_pb_column_inner][/et_pb_row_inner][/et_pb_column][/et_pb_section][et_pb_section bb_built="1" fullwidth="off" specialty="off" _builder_version="3.0.89" use_background_color_gradient="on" background_color_gradient_start="rgba(0,0,0,0.81)" background_color_gradient_end="rgba(0,0,0,0.81)" custom_css_main_element="width:100%;||height:50px;"][et_pb_row][et_pb_column type="4_4"][et_pb_divider _builder_version="3.0.89" /][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Learn by Doing" _builder_version="3.0.89" custom_padding="100px|0px|100px|0px" background_color_gradient_start="rgba(0,0,0,0.43)" background_color_gradient_end="rgba(0,0,0,0.4)"][et_pb_row admin_label="Achieve Your Goals" custom_padding="90px|0px|90px|0px" custom_margin="|||" _builder_version="3.0.82"][et_pb_column type="1_2"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-10.jpg" _builder_version="3.0.89" max_width="64px" animation_style="zoom" /][et_pb_text _builder_version="3.0.89" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#e09900" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off" background_layout="light"]

    Achieve your goals

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.89" text_font_size="16px" text_text_color="#353535" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off" background_layout="light"]

    Success is the progressive realization of a worthy goal or ideal. Earl Nightingale

    There is no better way to achieve a goal than to have one in mind or rather on paper. Our goal with this book is to teach you how to be the best at your work. How to make the best API possible. Without any Fluff whatsoever. A simple and down-to-earth approach that gets you to your goal in no time. [/et_pb_text][/et_pb_column][et_pb_column type="1_2"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/goals-orange-1.png" _builder_version="3.0.89" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="20%" locked="off" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="on" force_fullwidth="off" show_bottom_space="on" /][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="FAQ" _builder_version="3.0.89" custom_padding="100px|0px|180px|0px" custom_css_main_element="display:none;"][et_pb_row _builder_version="3.0.89" custom_css_main_element="padding: 10px !important;"][et_pb_column type="1_3"][et_pb_text _builder_version="3.0.89" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off" saved_tabs="all" background_layout="light" global_module="47267"]

    FAQ

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"] Class aptent taciti sociosqu ad litora torquent per conubia nostra himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. In pellentesque [/et_pb_text][et_pb_button button_url="#" button_text="CONTACT US" _builder_version="3.0.89" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#e09900" button_border_width="10px" button_border_color="#e09900" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset2" box_shadow_blur="0px" box_shadow_color="rgba(147,88,0,0.6)" saved_tabs="all" locked="off" url_new_window="off" background_layout="light" button_icon_placement="right" box_shadow_horizontal="3px" global_module="47185" box_shadow_vertical="3px" /][/et_pb_column][et_pb_column type="1_3"][et_pb_blurb title="Praesent non massa egestas? " use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][et_pb_blurb title="Quisque ante ante lobortis?" use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][et_pb_blurb title="Lorem ipsum dolor sit?" use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_3"][et_pb_blurb title="Tis at dapibus et congue?" use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][et_pb_blurb title="At dapibus et congue?" use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][et_pb_blurb title=" Sit Etiam porttitor ligula?" use_icon="on" font_icon="%%220%%" icon_color="#09e1c0" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Footer" background_color="#f7f8fc" _builder_version="3.0.89" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" saved_tabs="all"][et_pb_row _builder_version="3.0.89"][et_pb_column type="4_4"][et_pb_text _builder_version="3.0.89" background_layout="light" text_text_shadow_horizontal_length="0.2em" text_text_shadow_vertical_length="0.2em" text_text_color="#160000" text_text_shadow_color="rgba(0,0,0,0.3)" custom_margin="||20px|" text_font_size="30px" header_font_size="35px" header_text_color="#000000" header_letter_spacing="2px"]

    Meet the Authors

    [/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.89" background_size="initial" background_position="top_left" background_repeat="repeat" custom_css_main_element="padding-top: 40px !important;"][et_pb_column type="1_2"][et_pb_blurb title="Vladimir Pecanac" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/profile_image_sqare.png" icon_placement="top" content_max_width="1100px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#000000" header_line_height="1.5em" body_text_color="#aaaaaa" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" url_new_window="off" use_icon="off" use_circle="off" use_circle_border="off" use_icon_font_size="off" background_layout="light" saved_tabs="all" border_radii_image="on|135px|135px|135px|135px" border_width_all_image="5px" border_color_all_image="#ef9700" header_level="h2" global_module="47260" image_max_width="50%" module_alignment="center" module_id="profile-blurb"] Vladimir is the founder of the Code Maze blog and is very passionate about teaching the newest technologies in .NET world, REST and DevOps. He understands the complexities of these topics and make the examples both fun and useful. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_2"][et_pb_blurb title="Marinko Spasojevic" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/marinko-profile.jpg" icon_placement="top" image_max_width="50%" content_max_width="1100px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#000000" header_line_height="1.5em" body_text_color="#333333" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" url_new_window="off" use_icon="off" use_circle="off" use_circle_border="off" use_icon_font_size="off" background_layout="light" saved_tabs="all" border_radii_image="on|135px|135px|135px|135px" border_width_all_image="5px" border_color_all_image="#ef9700" header_level="h2" global_module="47260" locked="off" module_alignment="center" module_id="profile-blurb"] Marinko is a lifetime learner and teacher. There is no problem left unsolved and no comment left unanswered! He teaches about .NET Core, C#, JS frameworks, and best coding practices. He has built production ready enterprise systems and has many happy students in his portfolio. [/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Pricing Hero" _builder_version="3.0.89" use_background_color_gradient="on" background_color_gradient_start="rgba(12,3,3,0.23)" background_color_gradient_end="rgba(12,5,5,0.51)" background_image="https://code-maze.com/wp-content/uploads/2019/02/geometric-1732847_1920.jpg" background_blend="overlay" custom_padding="100px|0px|60px|0px" animation_style="slide" animation_direction="top" animation_intensity_slide="2%" box_shadow_style="preset7" box_shadow_horizontal="0px" box_shadow_vertical="-80px" box_shadow_color="#ffffff" locked="off" global_module="47376"][et_pb_row global_parent="47376" custom_width_px="1280px" custom_padding="27px|0px|60px|0px" custom_margin="|||" _builder_version="3.0.89"][et_pb_column type="4_4"][et_pb_text global_parent="47376" background_layout="dark" _builder_version="3.0.89" header_font="|on|||" header_font_size="42px" header_font_size_last_edited="on|desktop" header_line_height="1.3em" text_orientation="center" max_width="530px" module_alignment="center" custom_padding="|||" locked="off" text_text_shadow_style="preset5" text_text_shadow_horizontal_length="0.2em" text_text_shadow_vertical_length="0.2em"]

    GET THE REAL WORLD ASP.NET CORE BOOK

    [/et_pb_text][et_pb_text global_parent="47376" _builder_version="3.0.89" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" locked="off" background_layout="dark"]

    Pre-order until March 28 for 50% Off! Order now and you'll get the updates delivered to you whenever the new version of the REAL WORLD ASP.NET CORE comes out!

    [/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row global_parent="47376" custom_padding="27px|0px|0px|0px" _builder_version="3.0.82"][et_pb_column type="4_4"][et_pb_pricing_tables global_parent="47376" header_background_color="rgba(0,0,0,0)" show_bullet="on" _builder_version="3.0.89" header_font="|on|||" header_font_size="20px" header_text_color="#2e2545" header_line_height="2em" body_font_size="16px" body_text_color="#8585bd" body_line_height="19px" subheader_font="|on|||" subheader_font_size="14px" subheader_text_color="#8585bd" currency_frequency_font="|on|||" currency_frequency_font_size="26px" currency_frequency_text_color="#9271f6" price_font="|on|||" price_text_color="#9271f6" price_line_height="54px" use_border_color="on" border_width="0px" text_orientation="center" custom_margin="|||" custom_padding="30px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_use_icon="off" button_letter_spacing_hover="2px" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(9,225,192,0.36)" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="10%" animation_starting_opacity="100%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" custom_css_pricing_top="border: none;" center_list_items="off" show_featured_drop_shadow="on" button_icon_placement="right"] [et_pb_pricing_table title="Essentials" currency="$" sum="29.99" button_url="#" button_text="PREORDER (50% OFF)" pricing_item_excluded_color="rgba(0,0,0,0.49)" _builder_version="3.0.89" currency_frequency_text_color="#e09900" price_text_color="#e09900" button_bg_color="#e09900" button_border_color="#d1a470" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(114,114,255,0.3)" use_background_color_gradient="off" background_color_gradient_start="#2b87da" background_color_gradient_end="#29c4a9" background_color_gradient_type="linear" background_color_gradient_direction="180deg" background_color_gradient_direction_radial="center" background_color_gradient_start_position="0%" background_color_gradient_end_position="100%" background_color_gradient_overlays_image="off" parallax="off" parallax_method="on" background_size="cover" background_position="center" background_repeat="no-repeat" background_blend="normal" allow_player_pause="off" featured="off" header_font="||||||||" subheader_font="||||||||" currency_frequency_font="||||||||" price_font="||||||||" button_font="||||||||" button_use_icon="on" box_shadow_horizontal_button="0px" box_shadow_style="none" box_shadow_color="rgba(0,0,0,0.3)" price_font_size="60" price_font_size_tablet="60" price_font_size_phone="60" box_shadow_style_button="none" text_orientation="left" custom_button="off" currency_frequency_font_size="40px" currency_frequency_font_size_tablet="40px" currency_frequency_font_size_phone="40px" price_text_align="center" header_text_align="center" button_alignment="center" body_text_color="#000000" header_font_size="30px" header_font_size_tablet="30px" header_font_size_phone="30px"] Get the Full Book Build, test and deploy one full application -No support -No bonus content [/et_pb_pricing_table][et_pb_pricing_table title="Premium" currency="$" sum="49.99" button_url="#" button_text="PREORDER (50% OFF)" _builder_version="3.0.89" currency_frequency_text_color="#e09900" price_text_color="#e09900" box_shadow_style="preset4" box_shadow_horizontal="-1px" box_shadow_vertical="0px" use_background_color_gradient="off" background_color_gradient_start="#2b87da" background_color_gradient_end="#29c4a9" background_color_gradient_type="linear" background_color_gradient_direction="180deg" background_color_gradient_direction_radial="center" background_color_gradient_start_position="0%" background_color_gradient_end_position="100%" background_color_gradient_overlays_image="off" parallax="off" parallax_method="on" background_size="cover" background_position="center" background_repeat="no-repeat" background_blend="normal" allow_player_pause="off" featured="off" header_font="||||||||" subheader_font="||||||||" currency_frequency_font="||||||||" price_font="||||||||" button_font="||||||||" button_use_icon="on" box_shadow_style_button="preset4" box_shadow_color_button="rgba(224,153,0,0.3)" box_shadow_blur="0px" box_shadow_spread="0px" box_shadow_color="rgba(0,0,0,0.3)" text_orientation="left" header_text_align="center" currency_frequency_font_size="40px" currency_frequency_font_size_tablet="40px" currency_frequency_font_size_phone="40px" price_text_align="center" price_font_size="60" price_font_size_tablet="60" price_font_size_phone="60" button_alignment="center" body_text_color="#000000" header_font_size="30px" header_font_size_tablet="30px" header_font_size_phone="30px" button_text_color="#e09900" button_icon="%%36%%" box_shadow_horizontal_button="10px" box_shadow_vertical_button="10px" box_shadow_blur_button="0px" box_shadow_spread_button="0px" button_border_radius="0" custom_button="off"] All the benefits of Essentials Package How to Dockerize your Application mini book HTTP reference tables Exclusive Slack channel [/et_pb_pricing_table] [/et_pb_pricing_tables][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Features" _builder_version="3.0.89" use_background_color_gradient="on" background_color_gradient_start="#595959" background_color_gradient_end="#565656" background_blend="overlay" custom_padding="100px|0px|100px|0px" background_image="https://code-maze.com/wp-content/uploads/2019/02/geometric-1732847_1920.jpg" custom_css_main_element="display:none;"][et_pb_row admin_label="Become an Expert" padding_top_2="60px" _builder_version="3.0.82" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%"][et_pb_column type="1_2"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-02.png" _builder_version="3.0.89" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="right" animation_delay="100ms" animation_intensity_zoom="20%" /][/et_pb_column][et_pb_column type="1_2"][et_pb_text background_layout="dark" _builder_version="3.0.89" header_font="|on|||" header_font_size="36px" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off" text_text_color="rgba(0,0,0,0.97)" text_text_shadow_style="preset5" text_text_shadow_horizontal_length="0.3em" text_text_shadow_vertical_length="0.3em"]

    Don't get left behind

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.89" text_font_size="16px" text_text_color="#ffffff" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off" background_layout="light"] We've been using "latest and greatest" technologies to make the best guide out there! These technologies are cutting edge in web development, and the knowledge you gain is applicable to any modern web application. Any company that cares about their business will want to hire you! [/et_pb_text][et_pb_button button_url="#" button_text="PREORDER (50% OFF)" _builder_version="3.0.89" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#e09900" button_border_width="10px" button_border_color="#e09900" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset2" box_shadow_blur="0px" box_shadow_color="rgba(147,88,0,0.6)" saved_tabs="all" locked="off" url_new_window="off" background_layout="light" button_icon_placement="right" box_shadow_horizontal="5px" global_module="47185" /][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Testimonial" _builder_version="3.0.89" use_background_color_gradient="on" background_color_gradient_start="#383838" background_color_gradient_end="#3f3f3f" background_image="https://code-maze.com/wp-content/uploads/2019/02/geometric-1732847_1920.jpg" background_blend="overlay" custom_margin="|||" custom_padding="0px||0px|" box_shadow_style="preset7" box_shadow_horizontal="0px" box_shadow_vertical="80px" box_shadow_color="#ffffff" locked="off" custom_css_main_element="display:none;"][et_pb_row use_custom_width="on" custom_width_px="1280px" custom_padding="0px|0px|0px|0px" custom_padding_tablet="|10%||10%" custom_padding_phone="|||" make_equal="on" bg_img_2="https://code-maze.com/wp-content/uploads/2019/02/coding-dot-bg.png" padding_top_1="150px" padding_bottom_1="80px" parallax_2="on" padding_1_tablet="100px|||" padding_1_last_edited="on|phone" custom_padding_last_edited="on|tablet" _builder_version="3.0.89"][et_pb_column type="1_2"][et_pb_text background_layout="dark" _builder_version="3.0.89" header_font="|on|||" header_font_size="36px" header_line_height="1.5em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

    My entire team was prototyping by the end of the first day!

    [/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="18px" text_text_color="#d4ccff" text_line_height="1.9em" animation_style="slide" animation_direction="top" animation_intensity_slide="6%" locked="off"] Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. [/et_pb_text][et_pb_blurb image="https://code-maze.com/wp-content/uploads/2019/02/testimonial-avatar.png" icon_placement="left" background_layout="dark" content_max_width="800px" _builder_version="3.0.89" header_font="|on|||" header_text_color="#2e2545" body_font_size="16px" body_text_color="#d4ccff" custom_margin="|||" custom_padding="|||" animation_style="fade" locked="off"] John Smith, Divi Design Initiative [/et_pb_blurb][/et_pb_column][et_pb_column type="1_2"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-dots.png" _builder_version="3.0.89" custom_margin="|||" animation_style="slide" animation_direction="top" animation_intensity_slide="3%" animation_speed_curve="ease-out" show_in_lightbox="off" url_new_window="off" use_overlay="off" always_center_on_mobile="off" force_fullwidth="off" show_bottom_space="on" /][/et_pb_column][/et_pb_row][/et_pb_section]]]>
    47109 0 0 0
    Book Pricing Page https://code-maze.com/?page_id=47374 https://code-maze.com/?page_id=47374 Choose a Plan that Works for You [/et_pb_text][et_pb_text global_parent="47376" _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" locked="off"] Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. [/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row global_parent="47376" custom_padding="27px|0px|0px|0px" _builder_version="3.0.82"][et_pb_column type="4_4"][et_pb_pricing_tables global_parent="47376" header_background_color="rgba(0,0,0,0)" show_bullet="off" _builder_version="3.0.82" header_font="|on|||" header_font_size="20px" header_text_color="#2e2545" header_line_height="2em" body_font_size="16px" body_text_color="#8585bd" body_line_height="19px" subheader_font="|on|||" subheader_font_size="14px" subheader_text_color="#8585bd" currency_frequency_font="|on|||" currency_frequency_font_size="26px" currency_frequency_text_color="#9271f6" price_font="|on|||" price_text_color="#9271f6" price_line_height="54px" use_border_color="on" border_width="0px" text_orientation="center" custom_margin="|||" custom_padding="30px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_use_icon="off" button_letter_spacing_hover="2px" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(9,225,192,0.36)" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="10%" animation_starting_opacity="100%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" custom_css_pricing_top="border: none;"] [et_pb_pricing_table title="7 Day Trial" subtitle="Limited Access" currency="$" per="mo" sum="0" button_url="#" button_text="Try For Free" pricing_item_excluded_color="rgba(133,133,189,0.34)" _builder_version="3.0.82" currency_frequency_text_color="#7272ff" price_text_color="#7272ff" custom_button="on" button_text_size="16px" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(114,114,255,0.3)"]Try 5 lessons in each course No credit card required You can upgrade anytime - Projects with our Cloud IDE - Course completion certificates - Exclusive Slack channel[/et_pb_pricing_table][et_pb_pricing_table title="Premium Account" subtitle="Unlimited Access" currency="$" per="mo" sum="29" button_url="#" button_text="Signup Now" _builder_version="3.0.82" currency_frequency_text_color="#09e1c0" price_text_color="#09e1c0" box_shadow_style="preset4" box_shadow_horizontal="-1px" box_shadow_vertical="0px" box_shadow_color="rgba(71,74,182,0.12)"]Get unlimited access Test your knowledge 100+ guided projects Projects with our Cloud IDE Course completion certificates Exclusive Slack channel[/et_pb_pricing_table][et_pb_pricing_table title="Premium Account" subtitle="Unlimited Access" currency="$" per="mo" sum="49" button_url="#" button_text="Signup Now" _builder_version="3.0.82" custom_button="on" button_text_size="16px" button_bg_color="#9271f6" button_border_width="10px" button_border_color="#9271f6" box_shadow_style_button="preset1" box_shadow_vertical_button="10px" box_shadow_blur_button="50px" box_shadow_spread_button="5px" box_shadow_color_button="rgba(146,113,246,0.3)" box_shadow_style="preset4" box_shadow_horizontal="-1px" box_shadow_vertical="0px" box_shadow_color="rgba(71,74,182,0.12)"]Get unlimited access Test your knowledge 100+ guided projects Projects with our Cloud IDE Course completion certificates Exclusive Slack channel[/et_pb_pricing_table] [/et_pb_pricing_tables][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="What You Get" _builder_version="3.0.82" custom_margin="|||" custom_padding="60px|0px|100px|0px"][et_pb_row _builder_version="3.0.82"][et_pb_column type="4_4"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" text_orientation="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

    What you Get by Enrolling

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"] Himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. [/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_3"][et_pb_blurb title="100+ Interactive Courses" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_15.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_3"][et_pb_blurb title="Video Tutorials" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_20.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_3"][et_pb_blurb title="Coding Challenges" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-iconArtboard-19-copy-10.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="200ms" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_3"][et_pb_blurb title="New Monthly Content" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_4.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_3"][et_pb_blurb title="Mobile App" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_21.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_3"][et_pb_blurb title="Coding Community" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_16.jpg" icon_placement="left" image_max_width="64px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="200ms" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. [/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Become an Expert" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay" custom_padding="100px|0px|100px|0px"][et_pb_row padding_top_2="60px" _builder_version="3.0.82" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%"][et_pb_column type="1_2"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-11.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="zoom" animation_direction="right" animation_delay="100ms" animation_intensity_zoom="20%" /][/et_pb_column][et_pb_column type="1_2"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.3em" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

    Become an expert

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"] Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. [/et_pb_text][et_pb_button button_url="#" button_text="View All Courses" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off" /][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="FAQ" _builder_version="3.0.82" custom_padding="100px|0px|180px|0px"][et_pb_row _builder_version="3.0.82" locked="off"][et_pb_column type="4_4"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" text_orientation="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

    FAQ

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="on|on|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"] Himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. [/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82"][et_pb_column type="1_2"][et_pb_blurb title="How does the free trial work?" icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet [/et_pb_blurb][et_pb_blurb title="How often do you release new content?" icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet [/et_pb_blurb][et_pb_blurb title="Do you offer Virtual Reality Development?" icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet [/et_pb_blurb][/et_pb_column][et_pb_column type="1_2"][et_pb_blurb title="Can I cancel at any time?" icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet [/et_pb_blurb][et_pb_blurb title="What age groups do you support? " icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet [/et_pb_blurb][et_pb_blurb title=" What if I get stuck on a lesson?" icon_color="#7272ff" icon_placement="left" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" custom_margin="||0px|" custom_padding="20px||20px|" animation_style="zoom" animation_direction="left" animation_delay="100ms" animation_intensity_zoom="10%" locked="off"] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ornare in neque rutrum imperdiet. Quisque ante ante, lobortis at dapibus et, congue a orci. Sit Etiam porttitor ligula id massa lorem ipsum dolor sit amet, consectetur amet [/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Footer" background_color="#f7f8fc" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" saved_tabs="all"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"] Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_2"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"] Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor. [/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

    Ready to get started?

    [/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"] Get in touch, or create an account [/et_pb_text][/et_pb_column][et_pb_column type="1_4"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off" /][/et_pb_column][et_pb_column type="1_4"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off" /][/et_pb_column][/et_pb_row][/et_pb_section]]]>
    47374 0 0 0
    Latest Articles https://code-maze.com/?page_id=47871 https://code-maze.com/?page_id=47871 47871 0 0 0 OWASP Top 10 Vulnerabilities https://code-maze.com/?page_id=49797 https://code-maze.com/?page_id=49797 OWASP and OWASP Top 10 List. When we create a web application, one of the biggest challenges we face is its security. Most of our web applications are exposed to the internet. Hence, attackers could easily target those. Today, there is a large community working on improving web application security. They identify various types of attacks that can happen in a web application and steps to prevent those. Here, we are going to discuss one such organization called OWASP

    OWASP

    Open Web Application Security Project(OWASP) is a global non-profit organization. They provide information about web application security, thereby helping us make informed decisions. The OWASP Foundation came online in 2001. Later in 2004, they established it as a non-profit charitable organization in the United States. OWASP operates as an open community. They provide tools and documentation on application security. OWASP runs a lot of projects through collaboration between community members. As a result, today there are close to 100 open projects. These projects are either tools, documents or code libraries. Furthermore, they have classified their projects as Flagship Projects, Lab Projects, and Incubator Projects.

    OWASP Top 10

    OWASP Top 10 is a regularly-updated report outlining the security concerns for web applications. It focuses on the 10 most critical risks. They have last updated the list in 2017. OWASP refers to this report as an awareness document. They recommend that everyone should consider this report while developing web applications. That way, we can minimize security risks. The OWASP top 10 vulnerabilities are:
    • Injection
    • Broken Authentication
    • Sensitive Data Exposure
    • XML External Entities (XXE)
    • Broken Access Control
    • Security Misconfigurations
    • Cross-Site Scripting (XSS)
    • Insecure De-Serialization
    • Using Components with Known Vulnerabilities
    • Insufficient Logging and Monitoring
    We are going to discuss each of these in detail in the upcoming articles. We'll start with the injection attack in the first part of this series.]]>
    49797 0 0 0 OWASP and OWASP Top 10 List. When we create a web application, one of the biggest challenges we face is its security. We expose most of our web applications to the internet. Hence they could easily be attacked. Today, there is a large community working in this area. They identify various types of attacks that can happen in a web application and steps to prevent those. Here, we are going to discuss one such organization called OWASP

    OWASP

    OWASP stands for Open Web Application Security Project. It is a global non-profit organization. They provide information about web application security. Hence, they help us to make informed decisions. The OWASP Foundation came online in 2001. Later in 2004, they established it as a non-profit charitable organization in the United States. OWASP operates as an open community. They provide tools and documentation on application security. OWASP runs a lot of projects through collaboration between community members. As a result, there are close to 100 open projects. These projects are either tools, documents or code libraries. Furthermore, they have classified their projects as Flagship Projects, Lab Projects, and Incubator Projects.

    OWASP Top 10

    OWASP Top 10 is a regularly-updated report outlining the security concerns for web applications. It focuses on the 10 most critical risks. They have updated this list in 2017. OWASP refers to this report as an awareness document. They recommend that all organizations should incorporate this report. As a result, they can minimize security risks. The OWASP top 10 vulnerabilities are:
    • Injection
    • Broken Authentication
    • Sensitive Data Exposure
    • XML External Entities (XXE)
    • Broken Access Control
    • Security Misconfigurations
    • Cross-Site Scripting (XSS)
    • Insecure De-serialization
    • Using Components with known vulnerabilities
    • Insufficient logging and monitoring
    We'll discuss each of these in detail in the upcoming articles.]]>
    GUMROAD BUTTON -TEST https://code-maze.com/?page_id=50104 https://code-maze.com/?page_id=50104 Buy Me!]]> 50104 0 0 0 New HP https://code-maze.com/?page_id=50145 https://code-maze.com/?page_id=50145 50145 0 0 0 testhphp https://code-maze.com/?page_id=50148 https://code-maze.com/?page_id=50148 50148 0 0 0 https://code-maze.com/?page_id=50168 https://code-maze.com/?page_id=50168 ]]> 50168 0 0 0 Docker Series https://code-maze.com/docker-series/ Wed, 25 Apr 2018 06:16:53 +0000 https://code-maze.com/?page_id=1995 docker series Docker is one of the greatest innovations that happened in the last few years. It has opened new horizons in software development and it spun off many innovative solutions and projects. Docker images and containers are rapidly becoming THE way to do software development. Everything is dockerized. You can find an image for almost everything. If you can't, well, make your own and push it. Now there is an image for that too. We won't go too deep into the Docker itself since it already has fantastic documentation, and there are plenty of in-depth Docker guides throughout the internet. There are some concepts you'll need to understand to be able to follow along with the series since it gets progressively harder.

    What's the Point?

    The main goal of the series will be to show off the tremendous power of Docker and ASP.NET Core when combined together. We love the hands-on approach, so we will use concrete examples to demonstrate the concepts and explain the most important aspects of these technologies. This series relies heavily on the .NET Core Series, so if you are not sure about why something is implemented a certain way, be sure to check it out first.

    What Are We Going To Learn?

    Through the series you are going to learn about:

    Technology Stack

    The technologies you'll need to follow along with the complete series:
    • Windows 10 or any newer Linux OS (We're going to use Win10 with Linux containers through the series)
    • Docker Desktop for Windows
    • A Continuous Integration Server
    • Visual Studio 2019 or any other IDE of your choice
    ]]>
    1995 0 0 0
    Angular Series https://code-maze.com/angular-series/ Sat, 07 Apr 2018 16:55:45 +0000 https://code-maze.com/?page_id=2336 Logo-1100x620 Welcome to the Angular series. In this blog series, we are going to go through a detailed example of how to use Angular to create a fully functional client application that consumes the .NET Core 2.0 Web API server.

    .NET Core Web API Section

    If you want to learn how to create the server part of this series by using .NET Core 2.0 Web API, you may do that by visiting the page: .Net Core 2.0 Web API.

    What Are We Going to Do in This Guide?

    With the Angular tutorial, we are going to dive into the client-side part of our complete application. You’ll see how easy it is to use Angular to consume the REST API we have created on the server side with Web API. While we progress through this Angular tutorial, we will get familiar with modules, services, components and many other features of angular. We are going to create reusable services and use some third party libraries to complete the client part of the app.

    Prerequisites

    Background

    • Basic knowledge of Javascript
    • Familiarity with HTML and CSS

    This Tutorial Is Separated Into Several Parts

    What Next?

    After you are finished with the Angular tutorial, and you want to learn more about Angular Material, we have a detailed series about that as well. Just visit this link Angular Material Series and have a good time learning how to make Material applications with Angular.]]>
    2336 0 0 0
    React Series https://code-maze.com/react-series/ Tue, 08 May 2018 05:03:22 +0000 https://code-maze.com/?page_id=2778 React series Welcome to the React series. In this blog series, we are going to go through a detailed example of how to use React to create a fully functional client application that consumes the .NET Core 2.0 Web API server.

    .NET Core Web API Section

    If you want to learn how to create the server part of this series by using .NET Core 2.0 Web API, you may do that by visiting the page: .Net Core 2.0 Web API.

    What Are We Going to Do in This Guide?

    After the server part, we are going to dive into the client-side part. You’ll see how easy it is to use React to consume the REST API we have created on the server side with Web API. You'll also get familiar with the React classes, states, components(stateful and stateless) and many other React's features. We are going to use Redux as well and some third-party libraries to complete the client part of the app.

    Prerequisites

    • Node.js which includes the npm 5.6.0 or higher (depends on the time you are reading this post)
    • Visual Studio Code, or any other editor you like

    Background

    • Basic knowledge of Javascript
    • Familiarity with HTML and CSS

    This Tutorial Is Separated Into Several Parts

    ]]>
    2778 0 0 0
    Vue.js Series https://code-maze.com/vue-js-series/ Mon, 13 Aug 2018 05:58:19 +0000 https://code-maze.com/?page_id=4115 Vue.js series Welcome to the Vue.js Series. In this blog series, we are going to go through a detailed example of how to use Vue.js to create a fully functional client application that consumes the .NET Core 2.0 Web API server.

    .NET Core Web API Section

    If you want to learn how to create the server part of this series by using .NET Core 2.0 Web API, you may do that by visiting the page: .Net Core 2.0 Web API.

    What Are We Going to Do in This Guide?

    Our server part is finished, so, it is time to dive into the client part. We will find out how easy it is to use Vue.js to create a single page application that will consume our .NET Core Web API application. We will dive into the components, services, sending HTTP requests, events, navigation, routing and many different but awesome Vue.js features. So, stay with us through this fantastic journey.

    Prerequisites

    • Node.js which includes the npm 5.6.0 or higher (depends on the time you are reading this post)
    • Visual Studio Code, or any other editor you like

    Background

    • Basic knowledge of Javascript
    • Familiarity with HTML and CSS

    This Tutorial Is Separated Into Several Parts

    ]]>
    4115 0 0 0
    C# Back to Basics https://code-maze.com/csharp-back-to-basics/ Thu, 26 Jul 2018 06:26:07 +0000 https://code-maze.com/?page_id=4131 back to basics csharp Hello and welcome to the C# Back to Basics series.

    What are We Going to Learn About

    In this series, we are going to cover the C# language basics. We will learn how to create our first project, how to use operators, variables, expressions, and data types.  Furthermore, we will continue by learning how to use conditions and loops and what type of conditions and loops we have in C#. Of course, we won't forget to talk about methods, arrays and value and reference types. We will cover all the topics with the examples which will help us to better understand the topic we are talking about. So, the complete series is divided into the following sections: As we can see, there is a lot to learn and a lot to do in this series. So, we don't want to leave all the fun waiting, let's go get it...]]>
    4131 0 0 0
    C# Intermediate https://code-maze.com/csharp-intermediate-tutorial-oop/ Wed, 19 Sep 2018 06:04:16 +0000 https://code-maze.com/?page_id=4439 csharp intermediate Hello and welcome to the C# Intermediate tutorial.

    What are We Going to Learn

    In this tutorial, we are going to learn about C# intermediate concepts. We will try to show how to write a C# code in an object-oriented manner and how to use the object-oriented concepts while writing that code. The object-oriented approach is very important when we write our applications due to the possibility to reuse our applications or to reuse parts of it.  Furthermore, OOP concepts help us writing cleaner and maintainable code, which is a huge advantage in bigger projects. To learn about the OOP concept, we are going to cover the following topics: So, a lot of topics to cover, but a lot of knowledge that awaits us. Before we dive into this module, we strongly recommend going through our module 1 about C# basics. You will find many valuable pieces of information, which will help you significantly to follow along with this module. After this tutorial, you will have expertise in OOP concepts in C# and won't have any trouble applying them to your projects.]]>
    4439 0 0 0
    Angular Material Series https://code-maze.com/angular-material-series/ Mon, 22 Oct 2018 06:00:54 +0000 https://code-maze.com/?page_id=4837 angular material Welcome to the Angular Material tutorial. In this tutorial, we are going to go through a detailed example of how to use Angular Material components to create a fully functional client application that consumes the ASP.NET Core Web API server (or you can use any server to consume its responses).

    ASP.NET Core Web API Section

    If you want to learn how to create the server part with ASP.NET Core Web API, you may do that by visiting our series about it .NET Core 2.2 Web API.

    What We Recommend

    If you are not familiar with Angular at all, we have a great tutorial about Angular on our blog: Angular Series. We strongly recommend reading it first because you will gain all the required knowledge of the Angular framework and it will be much easier to follow along with this tutorial as well. If you are familiar with Angular, then just feel free to continue towards the lectures.

    What Are We Going to Learn?

    First of all, we are going to start with the Angular Material Installation. Then, we are going to use various components to create a fully functional client application. We are going to create a fully responsive navigation menu, use material tables with filter, sort and pagination functionalities. Furthermore, we will show you how to use spinners, a progress bar, card, select, expansion panel, and many other components from Angular Material.

    Prerequisites

    Background

    • Basic knowledge of Javascript
    • Familiarity with HTML and CSS
    • Decent Angular programming knowledge

    This Tutorial Is Separated Into Several Parts

    ]]>
    4837 0 0 0
    ASP.NET Core MVC Series https://code-maze.com/asp-net-core-mvc-series/ Mon, 27 May 2019 06:54:43 +0000 https://code-maze.com/?page_id=5403 mvc series In this series, we are going to have a detailed discussion about ASP.Net Core MVC. We'll start by looking into the MVC architecture and then move to the ASP.NET Core MVC framework. After that, we'll examine the project structure and look at how the various components fit in. We'll then discuss various features supported by the framework, working with data, implementing unit tests, etc. The approach that we follow will be to learn each topic by doing a small and easy to follow through a code example. The idea is that by the end of this series, we should have a fairly good knowledge of ASP.NET Core MVC concepts. Also, we should be able to start developing ASP.NET Core MVC applications.

    MVC Architecture

    The Model-View-Controller (MVC) architectural pattern separates an application into three components: Models, Views, and Controllers. This pattern helps to achieve separation of concerns. In this pattern, user requests are routed to a Controller. A Controller invokes the Model to perform user actions or retrieve data. The controller then passes this Model to a View and it is returned to the user. mvc architecture - ASP.Net Core MVC The Model in an MVC application represents the state of the application and any business logic or operations that should be performed by it. A model can also contain the logic for persisting the state of the application. Views are responsible for presenting content through the user interface. A view should ideally contain minimal logic and it should only be related to presenting the content. Controllers are the components that handle user interaction, work with the model, and ultimately select a view to render. In the MVC pattern, the controller is the initial entry point and is responsible for selecting which model types to work with and which view to render. In other words, the controller controls how the app responds to a given request. So the advantage of this pattern is that each of these components has a single responsibility and it's easier to code, debug, and test them in isolation.

    ASP.NET Core MVC

    The ASP.NET Core MVC is a lightweight, open source and highly testable framework which seamlessly integrates with the ASP.NET Core. ASP.NET Core MVC provides a patterns-based way to build dynamic websites which enables a clean separation of concerns. It gives us full control over the markup, supports test-driven development and adheres to the latest web standards. So that's all about an overview of ASP.NET Core MVC. Here's what we'll cover in this series: In the first part of the series, we are going to start by creating a brand new ASP.NET Core MVC project.]]>
    5403 0 0 0
    Start Here https://code-maze.com/start-here/ Wed, 30 Jan 2019 18:45:39 +0000 https://code-maze.com/?page_id=5755 Want to compete with the best? This guide will help you get an edge over your competition and build the robust but flexible, secure and production ready REST API that you'll be proud of!

    [thrive_2step id='2797']Download it now![/thrive_2step]

    [/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" padding_bottom="20px" padding_left="20px" padding_right="20px" padding_top="20px" background_color="#405061" parallax="off" parallax_method="on"][et_pb_blurb title="Mastering Angular Material Book" url="#" use_icon="on" font_icon="%%127%%" icon_color="#ffffff" icon_placement="left" background_layout="dark" use_icon_font_size="on" icon_font_size="120px" _builder_version="3.0.89" header_text_color="#ffffff" body_text_color="#ffffff" background_color="#75322c"]

    Do you know how to work with Angular already? Let's take that knowledge to another level by mastering Angular Material and making cool, responsive, and user-friendly apps!

    [thrive_2step id='47515']Download it now![/thrive_2step]

    [/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.47" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    ASP.NET Core, EF Core, GraphQL

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-06.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/net-core-series/" target="_blank"><span class="bold">ASP.NET Core Series: </span>Full guide to ASP.NET Core Web Application, MySql and Angular </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/net-core-series/" class="series" target="_blank">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/graphql-asp-net-core-tutorial/" target="_blank"><span class="bold">GraphQL Series: </span>Full guide to the integration process of GraphQL and ASP.NET Core </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/graphql-asp-net-core-tutorial/" class="series" target="_blank">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href=https://code-maze.com/entity-framework-core-series/" target="_blank"><span class="bold">EF Core Series: </span>Full guide to the Entity Framework Core in ASP.NET Core </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/entity-framework-core-series/" class="series" target="_blank">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Download ASP.NET Core Best Practices Book</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#" style="background-color: #bc4f3f;">[thrive_2step id='2797']DOWNLOAD[/thrive_2step]</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    C# And MVC Topics

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-01.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/csharp-back-to-basics/" target="_blank"><span class="bold">Back to Basics in C#: </span>A great place to start learning C#! </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/csharp-back-to-basics/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/csharp-intermediate-tutorial-oop/" target="_blank"><span class="bold">Intermediate C#: </span>Continuation to basic C# topics and a bit of OOP to spice it up </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/csharp-intermediate-tutorial-oop/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/asp-net-core-mvc-series/" target="_blank"><span class="bold">ASP .NET Core MVC: </span> Learn to create web applications with ASP.NET Core MVC</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/asp-net-core-mvc-series/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/asp-net-core-mvc-testing/" target="_blank"><span class="bold">ASP .NET Core MVC Testing: </span> Learn to test ASP.NET Core MVC web applications</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/asp-net-core-mvc-testing/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    HTTP & REST

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-11.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/http-series/" target="_blank"><span class="bold">HTTP Series:</span> basics, security, reference tables </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/http-series/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/different-ways-consume-restful-api-csharp/" target="_blank">A Few Great Ways to Consume RESTful API in C# </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/different-ways-consume-restful-api-csharp/" target="_blank">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/consuming-github-api-rest-with-flurl/" target="_blank">Consuming GitHub API (REST) With Flurl</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/consuming-github-api-rest-with-flurl/" target="_blank">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Download HTTP Basics Book</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#" style="background-color: #bc4f3f;">[thrive_2step id='2792']DOWNLOAD[/thrive_2step]</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.47" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Design Patterns

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-10.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/solid-principles/" target="_blank"><span class="bold">SOLID Principles Series</span></a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/solid-principles/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/design-patterns-csharp/" target="_blank"><span class="bold">Design Patterns in C#</span></a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/design-patterns-csharp/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Best Practices

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-09.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/aspnetcore-webapi-best-practices/" target="_blank">ASP.NET Core Web API Best Practices</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/aspnetcore-webapi-best-practices/" target="_blank">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/top-rest-api-best-practices/" target="_blank">REST API Best Practices</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/top-rest-api-best-practices/" target="_blank">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/angular-best-practices/" target="_blank">Angular Development Best Practices</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/angular-best-practices/" target="_blank">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Docker

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-03.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/docker-series/" target="_blank"><span class="bold">Docker Series:</span> Containerize and Build your ASP.NET applications with Docker</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/docker-series/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/top-docker-monitoring-tools/" target="_blank">Top Docker Monitoring Tools</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/top-docker-monitoring-tools/" target="_blank">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][/et_pb_row][et_pb_row custom_padding="29.5938px|0px|29px|0px" _builder_version="3.0.47" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Continuous Integration

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-07.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/top-8-continuous-integration-tools/" target="_blank">Top 8 Continuous Integration Tools</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/top-8-continuous-integration-tools/" target="_blank">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/top-mobile-continuous-integration-tools/" target="_blank">Top Mobile Continuous Integration Tools</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/top-mobile-continuous-integration-tools/" target="_blank">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/ci-jenkins-docker/" target="_blank">Continuous Integration with Jenkins and Docker</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/ci-jenkins-docker/" target="_blank">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Download The Definitive Guide to Continuous Integration Book</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#" style="background-color: #bc4f3f;">[thrive_2step id='3820']DOWNLOAD[/thrive_2step]</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    JS Frameworks

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-12.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/angular-series/" target="_blank"><span class="bold">Angular Series:</span> How to connect Angular to ASP.NET Core Web API</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/angular-series/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/react-series/" target="_blank"><span class="bold">React Series:</span> How to connect Angular to ASP.NET Core Web API</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/react-series/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/vue-js-series/" target="_blank"><span class="bold">Vue.js Series:</span> How to connect Vue.js to ASP.NET Core Web API</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/vue-js-series/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Download Angular Best Practices Book</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#" style="background-color: #bc4f3f;">[thrive_2step id='3785']DOWNLOAD[/thrive_2step]</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Best of Web Development

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-02.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/upload-files-dot-net-core-angular/" target="_blank">Uploading Files With .NET Core Web API and Angular</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/angular-material-series/" target="_blank">Read More</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/netcore-signalr-angular/" target="_blank">.NET Core with SignalR and Angular – Real-Time Charts</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/netcore-signalr-angular/" target="_blank">Read More</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/global-error-handling-aspnetcore/" target="_blank">Global Error Handling in ASP.NET Core Web API</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/global-error-handling-aspnetcore/" target="_blank">Read More</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/action-filters-aspnetcore/" target="_blank">Implementing Action Filters in ASP.NET Core</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/action-filters-aspnetcore/" target="_blank">Read More</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/unit-testing-aspnetcore-web-api//" target="_blank">Unit Testing in ASP.NET Core Web API</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/unit-testing-aspnetcore-web-api/" target="_blank">Read More</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.47" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Angular Material

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-05.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/angular-material-series/" target="_blank">Angular Material Series</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/angular-material-series/" target="_blank" class="series">SERIES</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Download Angular Material Book</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#" style="background-color: #bc4f3f;">[thrive_2step id='47515']DOWNLOAD[/thrive_2step]</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Other Great Articles

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/04/1-08.png" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/create-pdf-dotnetcore/" target="_blank">How to Easily Create a PDF Document in .NET Core Web API</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/create-pdf-dotnetcore/" target="_blank">Read More</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/net-core-web-api-ef-core-code-first/" target="_blank">ASP.NET Core Web API with EF Core Code-First Approach</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/net-core-web-api-ef-core-code-first/" target="_blank">Read More</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/authentication-aspnetcore-jwt-1/" target="_blank">ASP.NET Core Authentication with JWT and Angular</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/authentication-aspnetcore-jwt-1/" target="_blank">Read More</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="https://code-maze.com/different-ways-consume-restful-api-csharp/" target="_blank">A Few Great Ways to Consume RESTful API in C#</a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="https://code-maze.com/different-ways-consume-restful-api-csharp/" target="_blank">Read More</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_3" _builder_version="3.0.47" parallax="off" parallax_method="on"][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section fb_built="1" disabled_on="on|on|on" _builder_version="3.0.89" custom_css_main_element="background:#ecf0f5" disabled="on"][et_pb_row make_fullwidth="on" background_color_1="#405061" background_color_2="#405061" padding_top_1="20px" padding_right_1="20px" padding_bottom_1="20px" padding_left_1="20px" padding_top_2="20px" padding_right_2="20px" padding_bottom_2="20px" padding_left_2="20px" module_class="tablet-row" _builder_version="3.0.89"][et_pb_column type="1_2" _builder_version="3.0.47" padding_bottom="20px" padding_left="20px" padding_right="20px" padding_top="20px" background_color="#405061" parallax="off" parallax_method="on"][et_pb_blurb title="Java dev" url="#" use_icon="on" font_icon="%%101%%" icon_color="#ffffff" use_icon_font_size="on" icon_font_size="120px" _builder_version="3.0.89" body_text_color="#ffffff"]Lorem ipsum dolor sit amet, id ius appareat contentiones, quo cetero aperiri numquam id. Ei incorrupte liberavisse duo, eum malis repudiandae cu [/et_pb_blurb][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" padding_bottom="20px" padding_left="20px" padding_right="20px" padding_top="20px" background_color="#405061" parallax="off" parallax_method="on"][et_pb_blurb title="Java dev" url="#" use_icon="on" font_icon="%%98%%" icon_color="#ffffff" use_icon_font_size="on" icon_font_size="120px" _builder_version="3.0.89" body_text_color="#ffffff"]Lorem ipsum dolor sit amet, id ius appareat contentiones, quo cetero aperiri numquam id. Ei incorrupte liberavisse duo, eum malis repudiandae cu [/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row module_class="tablet-row" _builder_version="3.0.89"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Post About Angular

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_blog posts_number="3" include_categories="168" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_pagination="off" offset_number="0" module_class="home-page-content" _builder_version="3.0.89" header_font="|300||on|||||" header_font_size="18" pagination_font_size_tablet="51" pagination_line_height_tablet="2"][/et_pb_blog][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Post About C#

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/homepage-latest_articles.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_blog posts_number="3" include_categories="12" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_pagination="off" offset_number="0" module_class="home-page-content" _builder_version="3.0.89" header_font="|300||on|||||" header_font_size="18" pagination_font_size_tablet="51" pagination_line_height_tablet="2"][/et_pb_blog][/et_pb_column][/et_pb_row][et_pb_row module_class="tablet-row" _builder_version="3.0.89"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Post About Web Dev

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/homepage-rest_with_spring.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_blog posts_number="3" include_categories="13" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_pagination="off" offset_number="0" module_class="home-page-content" _builder_version="3.0.89" header_font="|300||on|||||" header_font_size="18" pagination_font_size_tablet="51" pagination_line_height_tablet="2"][/et_pb_blog][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Post About HTTP

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_blog posts_number="3" include_categories="171" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_pagination="off" offset_number="0" module_class="home-page-content" _builder_version="3.0.89" header_font="|300||on|||||" header_font_size="18" pagination_font_size_tablet="51" pagination_line_height_tablet="2"][/et_pb_blog][/et_pb_column][/et_pb_row][et_pb_row module_class="tablet-row" _builder_version="3.0.89"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Latest Posts

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/homepage-latest_articles.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_blog posts_number="3" include_categories="168,12,4,3,66,171,86,169,1,170,13" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_pagination="off" offset_number="0" module_class="home-page-content" _builder_version="3.0.89" header_font="|300||on|||||" header_font_size="18" pagination_font_size_tablet="51" pagination_line_height_tablet="2"][/et_pb_blog][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Post About DevOps

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/homepage-rest_with_spring.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_blog posts_number="3" include_categories="66" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_pagination="off" offset_number="0" module_class="home-page-content" _builder_version="3.0.89" header_font="|300||on|||||" header_font_size="18" pagination_font_size_tablet="51" pagination_line_height_tablet="2"][/et_pb_blog][/et_pb_column][/et_pb_row][et_pb_row module_class="tablet-row" _builder_version="3.0.89"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Post About Continuous Delivery

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_blog posts_number="3" include_categories="4" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_pagination="off" offset_number="0" module_class="home-page-content" _builder_version="3.0.89" header_font="|300||on|||||" header_font_size="18" pagination_font_size_tablet="51" pagination_line_height_tablet="2"][/et_pb_blog][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Post About Continuous Integration

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/homepage-latest_articles.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_blog posts_number="3" include_categories="3" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_pagination="off" offset_number="0" module_class="home-page-content" _builder_version="3.0.89" header_font="|300||on|||||" header_font_size="18" pagination_font_size_tablet="51" pagination_line_height_tablet="2"][/et_pb_blog][/et_pb_column][/et_pb_row][et_pb_row module_class="tablet-row" _builder_version="3.0.89"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Post About Vue.js

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/homepage-rest_with_spring.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_blog posts_number="3" include_categories="170" show_thumbnail="off" show_more="on" show_author="off" show_date="off" show_categories="off" show_pagination="off" offset_number="0" module_class="home-page-content" _builder_version="3.0.89" header_font="|300||on|||||" header_font_size="18" pagination_font_size_tablet="51" pagination_line_height_tablet="2"][/et_pb_blog][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Download Book

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Download</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][/et_pb_row][et_pb_row module_class="tablet-row" _builder_version="3.0.89"][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Complete series

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Full guide</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][et_pb_column type="1_2" _builder_version="3.0.47" parallax="off" parallax_method="on"][et_pb_text _builder_version="3.0.89" header_2_font="|700|||||||" header_2_text_color="#3e5062"]

    Custom posts

    [/et_pb_text][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/01/homepage-weekly_reviews.jpg" _builder_version="3.0.89"][/et_pb_image][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][et_pb_code _builder_version="3.0.89"]<div class="wrapper"><!-- [et_pb_line_break_holder] --> <div class="wrapper-title"><!-- [et_pb_line_break_holder] --> <h2><a href="#">Lorem ipsum dolor sit amet, et ridens vituperatoribus </a></h2><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --> <div class="read-more-wrapper"><!-- [et_pb_line_break_holder] --> <a href="#">Read more</a><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --></div>[/et_pb_code][/et_pb_column][/et_pb_row][/et_pb_section]]]>
    5755 0 0 0 [et_pb_section bb_built="1"][et_pb_row][/et_pb_row][/et_pb_section]

    ]]>
    Sponsor https://code-maze.com/sponsor/ Thu, 14 Mar 2019 07:05:57 +0000 https://code-maze.com/?page_id=5804 sponsorship featured Sponsorship of Code Maze is available for the parties interested in reaching the international audience of over 13,669 unique visitors daily at peak days or 194k monthly. Our readership consists mostly of IT professionals and those who are interested and/or specialize in Microsoft Stack (Web Development, .NET Core), DevOps and JavaScript Frameworks.

    Audience

    Although our audience consists primarily of US, India, UK, German, and France readers, our blog is read by people all over the world (hi Brasil!). Here is how the typical month looks like (measured by Google Analytics): Overview audience june With a 306k page views per month, Code Maze offers a great opportunity for any company or product to be visible worldwide. Also, most of our traffic is coming from organic sources, which are regarded as the best quality sources of traffic. And that's not all. Code Maze has been steeply growing the entire 2018, amounting to whopping 855% growth in organic traffic and at the same time improving bounce rate and average session duration. More details (and access to GA for existing sponsors) available on request.

    Email List

    Our email list is also growing rapidly currently counting about 10k engaged subscribers that receive our emails weekly and monthly. During 2018. we've grown our list by roughly 4200% with the exponential trend in subscriber growth. Our emails have about a 35% open rate which is well above the industry average, and a 13% click rate.

    Social Media

    We're present on every relevant social media channel including LinkedIn, Twitter, and Facebook with a combined audience of several thousand followers. We are frequently featured and shared by many influential websites like DotNetKicks, .NET Libhunt, and ng-newsletter. Many of our articles are available on DZone and Codeproject as well, which diversifies our audience even more.

    Sponsorship Message Positioning

    The message will appear in the most prominent place on the website and will consist of the "SPONSORED BY:" prefix, followed by your unique message in which you can present your company, product or service. For example, here's how the message looks like in full screen on desktop monitors: Sponsor message desktop Or on iPhone and other mobile devices: Sponsor message mobile

    What you get:

    1. Global exposure and reach of 194k unique users/306k page views per month
    2. Custom message in the most prominent place on the blog, available on every page (not affected by ad-blockers)
    3. Direct and prompt communication with us regarding any queries you might have
    4. "This week is sponsored by: <message>" in the weekly newsletter to our 10k+ email subscribers
    5. Tweet with the thank you message during the week of the sponsorship
    6. For the bonus price, you can get an article that you write, promoting your product/service on our blog and advertised on our social media

    The Sponsor Message

    The message should be about 140-150 characters long, and it should accurately and honestly describe your company/product/service as we will not accept false or misleading advertising of any sort. The whole message itself will be hyperlinked to the destination of your choosing, and the inbound traffic tracking through URLs is fine. You should also make sure that links are not blocked by commonly used ad-blockers.

    Pricing

    Note: The trial period is available for first-time sponsors. Contact us to try if the sponsorship works out for you! No strings attached! Just do it! Pricing is changing all the time, depending on the blog's growth. Sponsorship is sold in weekly periods, and you should make sure to buy a few terms in advance if you want your ad to show for a longer period of time. The price won't change if you've already bought the available slots. For the current price and available slots, please do get in touch with us and we'll be happy to share the details.

    Need More Information?

    If you have any additional questions or need more information to help you decide, you can also contact us through our contact page.]]>
    5804 0 0 0
    SOLID Principles https://code-maze.com/solid-principles/ Sun, 17 Feb 2019 09:48:55 +0000 https://code-maze.com/?page_id=47308 SOLID SOLID principles represent a set of principles that, if implemented properly should improve our code significantly. As with any other principle in life, every SOLID principle can be misused and overused to the point of being counterproductive. Instead of getting an understandable, maintainable and flexible code, we could end up with the code that's in the worse shape than without SOLID. So, careful consideration and implementing these principles only where needed is the key to the clean codebase. SOLID is a mnemonic acronym and each of the letters in it stands for: The source code for this series of articles can be found in this GitHub repo. You can switch to different branches to get to the different parts of the series. We hope these articles will help you discern when and how to implement these principles the right way.]]> 47308 0 0 0 Design Patterns in C# https://code-maze.com/design-patterns-csharp/ Sat, 23 Mar 2019 07:18:19 +0000 https://code-maze.com/?page_id=47667 design patterns There are a lot of design patterns. You can find a pattern for every situation. Should we use them whenever you can? Of course not! While design patterns generally should improve our code in theory, in practice it's not uncommon to find a lot of misused and wrongly implemented design patterns. The most common mistakes happen when people learn design patterns and try to implement them everywhere without the real need for them. The most important part of applying patterns to your codebase is knowing when and where to apply each pattern. You can easily find the implementation anywhere on the internet. Even multiple implementations of the same pattern. But if you implement them just for the sake of the pattern, you will probably do more damage to your project. So we've decided to go over the most commonly used patterns in C# and to explain where you can apply them and give some nice examples to give you some context. Here are what we think are the most useful patterns in C#:
    ]]>
    47667 0 0 0
    GraphQL ASP.NET Core Tutorial https://code-maze.com/graphql-asp-net-core-tutorial/ Mon, 15 Apr 2019 06:00:49 +0000 https://code-maze.com/?page_id=48139 graphql series cover In this tutorial, we are going to learn how to integrate GraphQL with ASP.NET Core Web API application, how to write types, queries, and mutations. Furthermore, we are going to learn how to consume the GraphQL API with the ASP.NET Core app and Angular client application as well. We are going to show you how to create different queries, how to use aliases and arguments, as well as fragments and directives while creating a GraphQL query. After finishing with this tutorial, you are going to have the knowledge to integrate GraphQL into the ASP.NET Core app and you’ll have no problem using Queries and Schemas to retrieve and mutate data from a server.

    Background

    For this tutorial, it is recommended that you have knowledge of the following:
    • ASP.NET Core Web API - If you are not familiar with it, we have a great tutorial on that topic ASP.NET Core Series
    • Base knowledge of Angular framework (required for the consuming client application) - If you want to learn or upgrade your knowledge on this topic you can read our Angular Tutorial

    This tutorial will be separated into several parts:

    We hope you are going to enjoy this tutorial as much as we did writing it. So, without further ado, let’s get started.]]>
    48139 0 0 0
    Entity Framework Core Series https://code-maze.com/entity-framework-core-series/ Mon, 29 Jul 2019 06:00:47 +0000 https://code-maze.com/?page_id=48536 entity framework core series Entity Framework Core is a library that allows us to access the database from our applications. It is designed as an object-relational mapper (ORM) and it works by mapping the relational database to the applications database model. EF Core is a cross-platform library and it runs on Windows as well as on Linux. It was introduced with the .NET Core framework thus the “Core” part in its name to distinguish it from the .NET Framework version. In this series, we are going to cover various topics related to EF Core and its relation with ASP.NET Core. We will show you how to create a basic configuration, how to use models and context classes, and how to use different configuration styles. Furthermore, we are going to learn about migrations, relationships in EF Core, querying the database and modifying the database content. Of course, this is just an overview of this series. With each article and with each section of that article, we will go in detail through the examples and explanations.

    Background

    For this tutorial, we recommend having:
    • An ASP.NET Core knowledge – If you are not familiar with it, we have a great tutorial on that topic
    • Knowledge of SQL
    • A LINQ basic knowledge
    • Visual Studio 2019 installed (preferred) or at least VS 2017

    We are going to separate this tutorial into several parts:

    As a result of going through these articles, you should have a great understanding of using EF Core in your applications. Therefore, using migrations, creating configuration or writing optimized queries will be no problem at all. So, let's get going.]]>
    48536 0 0 0
    ASP.NET Core MVC Testing https://code-maze.com/asp-net-core-mvc-testing/ Mon, 16 Sep 2019 06:00:22 +0000 https://code-maze.com/?page_id=48760 Testing ASP.NET Core MVC ASP.NET Core MVC Testing is a series of articles which will help you learn about writing automated tests at different levels for our ASP.NET Core MVC application. What we are going to cover is the testing process for model and controller classes, integration tests and automated UI tests. To test our code, we are going to use the xUnit library and talk about the testing attributes and functionalities it provides for us. Furthermore, we are going to explain how to isolate classes being tested with mock objects and how to use the Moq library for that purpose. Once we start writing integration tests, we are going to explain how to set up the in-memory database instead of using the real one and how to include anti-forgery tokens in our test code. The last part of this series is going to cover automated UI testing with the Selenium tool.

    But, Why Should We Test Our Code?

    The primary reason to check the quality of the software we are writing. By writing tests we can discover potential bugs in a development phase, which is a much better situation than finding them in a production phase. As a result, once our application is published, we won't spend time fixing bugs, but developing new features, which is a much better scenario, don’t you agree? What’s more, we develop a better understanding of the system by testing our code, thus providing better documentation as well. Overall, all these reasons and many others as well lead to reduced costs of the system.

    Background

    For this tutorial, we recommend having: We are going to separate this tutorial into several parts: So, by the end of this series, you will have a good understanding of testing your MVC application at different levels. We hope you will enjoy this tutorial and learn a lot. Let’s get started.]]>
    48760 0 0 0
    Leave Us a Review https://code-maze.com/review-form/ Mon, 23 Sep 2019 21:30:39 +0000 https://code-maze.com/?page_id=48838 Important: Although email is required, we won't use it to send you any unwanted/promotional emails, it's just for confirmation purposes and to root our false feedback. [testimonial_view id="2"]]]> 48838 0 0 0 Divi Template Test https://code-maze.com/divi-template-test/ Fri, 03 Jan 2020 23:16:47 +0000 https://code-maze.com/?page_id=48847 Supercharge Your Classroom with a Free Trial of Divi Coding Academy [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="off|off|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="left" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" animation_style="zoom" animation_intensity_zoom="6%" locked="off"] Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque. [/et_pb_text][et_pb_button button_url="#" button_text="View All Courses" button_alignment="left" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off" /][/et_pb_column][et_pb_column type="1_2"][et_pb_image src="https://code-maze.com/wp-content/uploads/2019/02/coding-isometric-12.png" _builder_version="3.0.82" max_width="90%" module_alignment="center" animation_style="slide" animation_direction="left" animation_duration="500ms" animation_delay="100ms" animation_intensity_slide="10%" /][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Features" _builder_version="3.0.82" custom_margin="|||" custom_padding="100px|0px|100px|0px"][et_pb_row custom_padding="27px|0px|24px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="4_4"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" module_alignment="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

    What We Offer

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="off|off|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#8585bd" text_line_height="1.9em" text_orientation="center" max_width="540px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"] Himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. [/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_3"][et_pb_blurb title="Beginner Courses" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_12.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"] Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_3"][et_pb_blurb title="Gamified Learning" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_13.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"] Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_3"][et_pb_blurb title="Realworld Projects" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_14.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"] Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. [/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_3"][et_pb_blurb title="Progress Tracking" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_17.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"] Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_3"][et_pb_blurb title="Lessons Plans" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_4.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"] Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_3"][et_pb_blurb title="Teacher Training" url="#" icon_color="#7272ff" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_18.jpg" icon_placement="left" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#2e2545" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="|||" custom_padding="||20px|" animation_style="zoom" animation_direction="left" animation_intensity_zoom="10%" animation_starting_opacity="100%" locked="off"] Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. [/et_pb_blurb][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Our Courses" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay" custom_padding="100px|0px|0px|0px" box_shadow_style="preset7" box_shadow_horizontal="0px" box_shadow_vertical="-80px" box_shadow_color="#ffffff"][et_pb_row padding_top_2="60px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%"][et_pb_column type="4_4"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" max_width="540px" module_alignment="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

    Our Most Popular Courses

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="off|off|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="600px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"] Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor. [/et_pb_text][/et_pb_column][/et_pb_row][et_pb_row _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2"][et_pb_video src="https://www.youtube.com/watch?v=FkQuawiGWUw&t=6s" image_src="https://code-maze.com/wp-content/uploads/2019/02/video-overlay.jpg" play_icon_color="#7272ff" _builder_version="3.0.82" box_shadow_style="preset1" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" /][/et_pb_column][et_pb_column type="1_2"][et_pb_video src="https://www.youtube.com/watch?v=FkQuawiGWUw&t=6s" image_src="https://code-maze.com/wp-content/uploads/2019/02/video-overlay.jpg" play_icon_color="#7272ff" _builder_version="3.0.82" box_shadow_style="preset1" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" /][/et_pb_column][/et_pb_row][et_pb_row custom_padding="27px|0px|0px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2"][et_pb_video src="https://www.youtube.com/watch?v=FkQuawiGWUw&t=6s" image_src="https://code-maze.com/wp-content/uploads/2019/02/video-overlay.jpg" play_icon_color="#7272ff" _builder_version="3.0.82" box_shadow_style="preset1" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" /][/et_pb_column][et_pb_column type="1_2"][et_pb_video src="https://www.youtube.com/watch?v=FkQuawiGWUw&t=6s" image_src="https://code-maze.com/wp-content/uploads/2019/02/video-overlay.jpg" play_icon_color="#7272ff" _builder_version="3.0.82" box_shadow_style="preset1" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off" /][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Testimonials" _builder_version="3.0.82" custom_margin="40px|||" custom_padding="100px|0px|100px|0px"][et_pb_row _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" locked="off"][et_pb_column type="4_4"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" module_alignment="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

    What People Are Saying

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="off|off|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][/et_pb_column][/et_pb_row][et_pb_row custom_padding="0px|0px|0px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="4_4"][et_pb_slider admin_label="Testimomial Slider" _builder_version="3.0.82" header_font="||||" header_line_height="1.5em" body_text_color="#8585bd" custom_padding="|||%22" custom_padding_tablet="|||" custom_padding_phone="|||" show_inner_shadow="off"] [et_pb_slide background_layout="light" _builder_version="3.0.82" body_line_height="1.9em" text_orientation="center" background_color="#ffffff"] "Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque." David Cole, Monarch [/et_pb_slide][et_pb_slide background_layout="light" _builder_version="3.0.82" body_line_height="1.9em" text_orientation="center" background_color="#ffffff"] "Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque." David Cole, Monarch [/et_pb_slide][et_pb_slide background_layout="light" _builder_version="3.0.82" body_line_height="1.9em" text_orientation="center" background_color="#ffffff"] "Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque." David Cole, Monarch [/et_pb_slide][et_pb_slide background_layout="light" _builder_version="3.0.82" body_line_height="1.9em" text_orientation="center" background_color="#ffffff"] "Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor nulla, id sagittis nisi ante nec arcu. Fusce porta bibendum convallis. Morbi fringilla sollicitudin scelerisque." David Cole, Monarch [/et_pb_slide] [/et_pb_slider][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Join" _builder_version="3.0.82" use_background_color_gradient="on" background_color_gradient_start="#474ab6" background_color_gradient_end="#9271f6" background_image="https://code-maze.com/wp-content/uploads/2019/02/coding-background-texture.jpg" background_blend="overlay" custom_padding="100px|0px|180px|0px" locked="off"][et_pb_row padding_top_2="60px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%"][et_pb_column type="4_4"][et_pb_text background_layout="dark" _builder_version="3.0.82" header_font="|on|||" header_font_size="36px" header_line_height="1.3em" background_size="initial" background_position="top_left" background_repeat="repeat" text_orientation="center" max_width="540px" module_alignment="center" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%" locked="off"]

    Elevate Your Classroom

    [/et_pb_text][et_pb_divider color="#09e1c0" show_divider="on" divider_weight="4px" disabled_on="off|off|off" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat" max_width="40px" module_alignment="center" custom_margin="||10px|" animation_style="zoom" animation_direction="left" saved_tabs="all" locked="off" /][et_pb_text _builder_version="3.0.82" text_font_size="16px" text_text_color="#d4ccff" text_line_height="1.9em" text_orientation="center" max_width="600px" module_alignment="center" animation_style="zoom" animation_intensity_zoom="6%" locked="off"] Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie, velit ut eleifend sollicitudin, neque orci tempor. [/et_pb_text][et_pb_button button_url="#" button_text="Start a Free Trial" button_alignment="center" _builder_version="3.0.82" custom_margin="20px|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#09e1c0" button_border_width="10px" button_border_color="#09e1c0" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="10%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(0,188,160,0.6)" saved_tabs="all" locked="off" /][/et_pb_column][/et_pb_row][/et_pb_section][et_pb_section bb_built="1" admin_label="Footer" background_color="#f7f8fc" _builder_version="3.0.82" custom_padding="0px|0px|100px|0px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="6%" animation_starting_opacity="100%" saved_tabs="all"][et_pb_row custom_padding="0px|0px|100px|0px" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2"][et_pb_blurb title="Free Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_2.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"] Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor. [/et_pb_blurb][/et_pb_column][et_pb_column type="1_2"][et_pb_blurb title="Premium Courses" url="#" image="https://code-maze.com/wp-content/uploads/2019/02/coding-icon_8.jpg" icon_placement="left" image_max_width="64px" content_max_width="1100px" _builder_version="3.0.82" header_font="|on|||" header_text_color="#7272ff" header_line_height="1.5em" body_text_color="#8585bd" body_line_height="1.9em" background_color="#ffffff" custom_margin="-80px|||" custom_margin_tablet="0px|||" custom_margin_last_edited="on|phone" custom_padding="30px|40px|30px|40px" animation_style="zoom" animation_direction="bottom" animation_delay="100ms" animation_intensity_zoom="20%" animation_starting_opacity="100%" box_shadow_style="preset2" box_shadow_horizontal="0px" box_shadow_vertical="0px" box_shadow_blur="60px" box_shadow_color="rgba(71,74,182,0.12)" locked="off"] Duis egestas aliquet aliquet. Maecenas erat eros, fringilla et leo eget, viverra pretium nulla. Quisque sed augue tincidunt, posuere dui tempor. [/et_pb_blurb][/et_pb_column][/et_pb_row][et_pb_row use_custom_gutter="on" gutter_width="2" _builder_version="3.0.82" background_size="initial" background_position="top_left" background_repeat="repeat"][et_pb_column type="1_2"][et_pb_text _builder_version="3.0.82" text_text_color="#7272ff" header_font="|on|||" header_font_size="36px" header_text_color="#7272ff" header_line_height="1.5em" background_size="initial" background_position="top_left" background_repeat="repeat" custom_margin="||20px|" animation_style="slide" animation_direction="bottom" animation_intensity_slide="10%"]

    Ready to get started?

    [/et_pb_text][et_pb_text _builder_version="3.0.82" text_font_size="22px" text_text_color="#8585bd" text_line_height="1.9em" background_size="initial" background_position="top_left" background_repeat="repeat" animation_style="fade" locked="off"] Get in touch, or create an account [/et_pb_text][/et_pb_column][et_pb_column type="1_4"][et_pb_button button_url="#" button_text="Create Account" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#ffffff" button_bg_color="#7272ff" button_border_width="10px" button_border_color="#7272ff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_delay="100ms" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(114,114,255,0.4)" locked="off" /][/et_pb_column][et_pb_column type="1_4"][et_pb_button button_url="#" button_text="Contact Us" button_alignment="left" _builder_version="3.0.82" custom_margin="|||" custom_button="on" button_text_size="16px" button_text_color="#7272ff" button_bg_color="#ffffff" button_border_width="10px" button_border_color="#ffffff" button_border_radius="100px" button_letter_spacing="1px" button_font="|on||on|" button_icon="%%36%%" button_on_hover="off" button_letter_spacing_hover="2px" animation_style="zoom" animation_intensity_zoom="6%" box_shadow_style="preset1" box_shadow_vertical="10px" box_shadow_blur="50px" box_shadow_spread="5px" box_shadow_color="rgba(181,181,255,0.38)" locked="off" /][/et_pb_column][/et_pb_row][/et_pb_section]]]>
    48847 0 0 0
    Code Maze Reviews https://code-maze.com/code-maze-reviews/ Fri, 27 Sep 2019 16:56:52 +0000 https://code-maze.com/?page_id=48858 48858 0 0 0 Giveaway November 2019 https://code-maze.com/giveaway-november-2019/ Mon, 25 Nov 2019 02:10:22 +0000 https://code-maze.com/?page_id=49620 49620 0 0 0 Book-download]]> Buy Us a Coffee https://code-maze.com/buy-us-a-coffee/ Thu, 05 Dec 2019 18:27:11 +0000 https://code-maze.com/?page_id=49694 .bmc-button img{width: 35px !important;margin-bottom: 1px !important;box-shadow: none !important;border: none !important;vertical-align: middle !important;}.bmc-button{padding: 7px 5px 7px 10px !important;line-height: 35px !important;height:51px !important;min-width:217px !important;text-decoration: none !important;display:inline-flex !important;color:#ffffff !important;background-color:#FF813F !important;border-radius: 5px !important;border: 1px solid transparent !important;padding: 7px 5px 7px 10px !important;font-size: 20px !important;letter-spacing:-0.08px !important;box-shadow: 0px 1px 2px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important;margin: 0 auto !important;font-family:'Lato', sans-serif !important;-webkit-box-sizing: border-box !important;box-sizing: border-box !important;-o-transition: 0.3s all linear !important;-webkit-transition: 0.3s all linear !important;-moz-transition: 0.3s all linear !important;-ms-transition: 0.3s all linear !important;transition: 0.3s all linear !important;}.bmc-button:hover, .bmc-button:active, .bmc-button:focus {-webkit-box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important;text-decoration: none !important;box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important;opacity: 0.85 !important;color:#ffffff !important;} Buy us a coffeeBuy us a coffee Thanks for being awesome!]]> 49694 0 0 0 TeamCity Basic Concepts https://code-maze.com/teamcity-basic-concepts/ Sat, 06 Feb 2016 09:36:45 +0000 http://www.code-maze.com/blog/?p=13 In the previous post, we've talked about Continuous Integration and its benefits in software development automation. Hopefully, by now you understand how important continuous integration is and want to learn how to improve your software by using it. Now, you will learn how to implement continuous integration with TeamCity, one of the best continuous integration tools available out there. TeamCity offers free Professional Server License that allows you to use the full set of features it offers but is limited to 3 build agents and 100 build configurations. It is easily one of the best choices if you need stable, mature and user-friendly on-premises CI tool that to start with. We are going to cover: So, let's start off by learning some basic TeamCity terms.

    TeamCity Vocabulary

    Build Agent. This is the "engine" of the TeamCity. Build agents do all the heavy lifting needed to build and test the project. They are configured through the TeamCity server and can be set up to build and run on different platforms and operating systems. A build agent utilizes the power of the machine that it is installed on. TeamCity Server. The Main purpose of the server part is to configure, monitor, distribute queued builds, manage users, and send reports. These tasks are just the small part of the TeamCity server responsibilities. Build Queue. It represents the list of triggered builds, which are the server delegates to the build agent for further processing. As soon as the compatible agent becomes idle, the TeamCity server assigns the build to it. Build. It has the dual meaning. It refers both to the process of creation of the application version and the version itself. Build Artifacts. Build artifacts represent the result of a successful build. For example, they can be zip archives, WAR files, log files, installers or any other packaging format you like or use. Project. A project represents one software application/project. One project can have multiple build configurations. Build configuration. It's the combination of settings used to build an application. In the build configuration, you can create build steps, set triggers, and a version control system (VCS) root for example. Build step. A single task. Build configuration usually consists of several build steps. Each build step executes one part of the build process. For example, in one build step, you can compile the source code and in the other run tests, or perhaps deploy the project. Build trigger. It is the rule that defines when the build should be started. The build can be scheduled to run at specific times of the day, automatically on VCS changes, or when other builds are completed.

    Supported Platforms and Environments

    When you do continuous integration with TeamCity you have a variety of different IDEs, platforms, frameworks, and tools that help you have a better environment to build your software project in. TeamCity supports runners for the Java, .NET and Ruby by default and you can utilize command line runners for other languages as well. Various tools for monitoring, code coverage, code analysis, issue tracking are an integral part of the TeamCity repertoire and the TeamCity itself can be integrated with a few IDEs as well. Some of the features and tools come by default bundled with TeamCity and others can be installed as plugins. JetBrains have their own set of plugins, but there are also many third-party plugins available. For the list of publicly available plugins, you can check this page. You can find the list of the supported environments on the diagram on the TeamCity documentation page, as well as a more comprehensive list here.

    Creating Your First .NET Project in TeamCity

    So how do you go about setting TeamCity up? Whether you have some legacy code, or you are thinking about creating a brand new .NET project, your application is probably using one of the version control systems like Git or Subversion. TeamCity, like other CI servers, relies heavily on VCS, so make sure that TeamCity supports your VCS of choice. Once you downloaded and installed both TeamCity server and, at least, one build agent, you can start configuring TeamCity. The first thing you want to do is to create a new project. You can do this by going to the administration page located at the top right of the TeamCity panel. On the administration page, you will immediately see two buttons for the project creation. The first one is for the manual creation of the project, and the second one helps you with the process by using the URL of the project repository you provided. create new project If you opt-in for the manual creation, you'll need to fill in the name and the description of the project, and add the repository URL later. Create project from URL option does this for you and, in addition, adds a build configuration for you. You can see the list of the created configurations on the project settings page. The first thing that you will see when you select the build configuration is the general settings page.
    The first thing that you will see when you select the build configuration is the general settings page. build configuration panel Properties that you want to change here are the build number format and artifact paths, although you might want to build your project first so you can easily select files that will go into artifacts later. The build number format is something that you can use later by invoking %build.number%. It represents your project version: major.minor.build counter (eg. 1.0.83). The build counter increments automatically, but you can reset it if you need to. For now, let's leave the artifacts intact, and go to the next page on the build configurations panel, which is version control settings.
    If you create the project using the repo URL, you can skip this step. Otherwise, now is your chance to set up your repository URL which the TeamCity should monitor for changes. The next phase of build configuration is the build steps definition. The build steps page gives you the option to automatically detect build steps for your project, or you can add them one by one manually. Automatic build step detection is useful if the TeamCity supports your project runners by default. Otherwise, you can choose from various build tools like Ant or NAnt, or just use the command line or Powershell. This is where you can setup all your scripts and tests, do the deployment and code analytics. Some of the tools you need are supported by TeamCity by default, others need to be added using the TeamCity plugin manager.

    Continuous Integration With TeamCity

    So how do we implement the most basic continuous integration cycle with TeamCity? As I explained in my article about Continuous Integration, we need to:
    • Build our project
    • Run the automated tests
    • Deploy the application
    • Do the acceptance testing
    • Send the notification on the completion of that process whether it finished successfully or failed
    Since the deployment procedure is a bit harder to grasp, and a bit complicated to execute, it is described in a separate post. So how do we do continuous integration with TeamCity?

    Adding the Visual Studio Solution Runner

    Before we can package and distribute our application, we need to build it first. In order to do this, we are going to add build step and then choose the Visual Studio (sln) option from the drop-down menu. After that you should see something like this: build steps After you enter the step name, you should select the path to your project solution file using the little tree button to the right side of the Solution file path input field. Both targets and solution configuration are already set for you, but if you want to change them just enter the new values and click Save.

    Adding NUnit

    Now that we have the build step configured, we can add the test runner to our project. Most popular testing tools for .NET projects are xUnit and NUnit. While NUnit comes bundled with the TeamCity by default, if you use xUnit in your project you need to install it as a plugin. Let's say for example your project is using NUnit framework. To automatically run tests for your project, you can go to the Add build step button again, and from the drop-down menu select the NUnit. When you do that, TeamCity will generate a few properties you need to configure in order to run your tests. Your page should look like this: nunit build step After naming your step, selecting runner, platform and .NET version, you need to add the path to the assembly with your tests. This editor does not give you the option to select the assembly path from the tree structure like some other editors in TeamCity. But there is a practical solution for that problem. You can look up the path of assembly in your project manually, you can use wildcards like **\*Test.dll, or you can find the editor that does have the path select button and find the path to the assembly there and copy paste it in this editor. A bit of inconvenience, but nothing too serious.

    Setting Triggers

    If you followed this post so far, you have probably seen the little light bulb near the run button in TeamCity. This little light bulb is trying to tell you that you forgot to add the VCS trigger to your project. lightbulb trigger If you click on it, it directs you to the Triggers page where you can select a trigger that you need. There are two triggers you should care about for now. First one is the VCS trigger. It triggers your build when it detects the changes in the VCS repository. The other one is the Schedule trigger with which you can schedule your build at the regular time intervals (nightly builds anyone?).

    Sending Notifications

    The last thing we need to do in order to complete our Continuous Integration cycle is to manage notifications. This step is important because both you and your team need to have some feedback in order to act quickly if the project does not build successfully or tests fail. To send the notifications, you must first set the email notifier in the administration section of the TeamCity. Once you set the SMTP settings, you need to go to the My Settings and Tools section that you can find in the drop-down below your profile name. After that, go to the notification rules tab, and simply add the rule you want to the appropriate user groups. Notification settings are pretty flexible so use them to your preference. At the very least you should receive the notification when the build fails, so you can fix the problem quickly. notification settings That's about it. Just hit the run button and watch the magic happen. After your first build completes successfully, you should revisit the General Settings page and choose the artifacts for your project. Also, you might want to try committing some changes to the repository if you selected the VCS trigger. Together with deployment to the staging environment and acceptance testing these steps make the most basic continuous integration with TeamCity. TeamCity offers a plethora of advanced features, so I encourage you to try them out and experiment a lot to get the most out of this powerful tool.

    Conclusion

    As you can see, continuous integration with TeamCity is not that hard. And the magic doesn't stop there. You can automatically deploy your projects to the staging and production environments, integrate code analysis and code coverage tools, but more on these features in the future posts. [sc name="subscribe" formNumber="3820" contentType="CI/CD"]]]>
    13 0 0 0
    Top 8 Continuous Integration Tools https://code-maze.com/top-8-continuous-integration-tools/ Sat, 20 Feb 2016 11:24:27 +0000 http://www.code-maze.com/?p=91 If you are familiar with the concept of Continuous Integration we can agree that using it has become mandatory. There are many Continuous Integration tools out there, and in this article, we will go through some of the greatest tools available on the market and see how they fare. After reading this list you will have a better understanding what tools are available to you and choose the perfect tool for yourself. On the other hand, if you are interested in mobile apps, there is a specific subset of tools that might suit you better, so check out our Top Mobile Continuous Integration Tools List. So without further ado and in no particular order of importance, I present you the list of the top 8 Continuous Integration tools:

    Buddy

    logo buddy Buddy is a modern and intuitive CI tool. By keeping the interface simple Buddy makes the user experience the top priority and the time you need to invest in learning the tool to the minimum (which is nice). Wizard-like type of configurations make the tool easy to work with, and there is practically no need to read the documentation until you need to configure some of the more complex pipelines. And what makes a modern CI tool? Variety of supported tools/integrations, fast build times, smart but flexible UI, different hosting options (cloud/on-premises) and configurable pricing plans. Buddy fulfills all the above and goes beyond. Although some of the technologies we are going to mention are de facto standards these days, it is worth mentioning that Buddy supports Docker, Kubernetes, all the major frameworks, languages and cloud providers, including PHP, Node, Python, .NET Core and Rails, as well as AWS and Google Cloud services. You can also rely on Buddy to provide monitoring and notifications for your builds. We've worked with many tools, and there's a lot of great tools on this list, but in terms of how intuitive and up-to-date with modern trends they are, Buddy leads the way. Verdict: The most modern and intuitive tool on the list, supports all the features you might need but doesn't sacrifice either the flexibility or user experience in return. Official website: Buddy Availability: Free 7 day trial/paid plans Platform: Hosted and On-premises Linux/Mac

    TeamCity

    TeamCity logo TeamCity is the mature CI server, coming from the labs of the JetBrains company. JetBrains has established authority in the software development world, and developers all over the world use their tools like WebStorm and ReSharper. TeamCity offers all the features in its free version, but it is limited to the 100 build configurations and 3 build agents. Additional build agents and build configurations need to be purchased. Recently JetBrains started offering a cloud trial of TeamCity where you can try it out for one project without a hassle of having to install it on-premises. It lasts 60 days and you can export the project afterward. Out of the box, TeamCity works on many different platforms and has the support for wide variety of tools and frameworks. There are many publicly available plugins, developed both by JetBrains and third parties. Despite being the Java-based solution, TeamCity offers the best .NET support among the tools on this list. There are also different enterprise packages, that scale by the number of agents your company needs. You can find a great in-depth case study on TeamCity here on our blog. Verdict: Great solution overall, but due to its complexity and price, better suited for enterprise needs. Official website: TeamCity Availability: Free for 3 agents and 100 build configurations and paid enterprise edition Platform: Servlet container (On-premises), cloud trial

    Jenkins

    jenkins logo Jenkins is an open-source CI tool written in Java. It originated as the fork of Hudson when the Oracle bought the Sun Microsystems. Jenkins is a cross-platform CI tool and it offers configuration both through GUI interface and console commands. What makes Jenkins very flexible is the feature extension through plugins. Jenkins plugin list is very comprehensive and you can easily add your own. Besides extensibility, Jenkins prides itself on distributing builds and test loads on multiple machines. It is published under MIT license so it is free to use and distribute. Cloudbees also offers hosted solution in the form of the Jenkins in the Cloud. Verdict: One of the best solutions out there, both powerful and flexible at the same time. The learning curve could be a bit steep, but if you need flexibility it very well pays off to learn how to use it. Official website: Jenkins Availability: Free Platform: Cross-platform

    Travis CI

    Travis CI is one of the oldest hosted solutions out there and it has won the trust of many people. Although it's mostly known for the hosted solution, it offers the on-premise version too in a form of enterprise package. Travis CI is free for all open source projects hosted on the GitHub and for the first 100 builds otherwise. There are a few pricing plans you can choose from, the main difference being the number of concurrent builds you can run. Builds are configured using .travis.yml file which contains the build tasks that will be executed on running the build. It supports a variety of different languages and a good documentation to back them up. Verdict: A Mature solution that offers both hosted and On-premises variants, loved and used by many teams, very well documented. Official website: Travis CI Availability: Free for open source plans and first 100 builds, paid plans for everything else Platform: Hosted and On-premises

    Bamboo

    bamboo logo Atlassian is the company focused on providing tools for software development teams and you might know them by their tools like JIRA and Bitbucket. Bamboo originally offered both cloud and On-premises solutions, but in the May 2016 the cloud version was discontinued in the favor of the Bitbucket pipelines (accessible through the left panel of your Bitbucket account). By utilizing the power of Docker, Bitbucket Pipelines is offering very efficient and fast builds that and is rapidly growing and becoming a worthy successor to the Bamboo Cloud. Bamboo is free to try for 30 days, and after that, there are two plans for small and growing teams. Being the Atlassian tool, it has the native support for JIRA and BitBucket and you can even import your Jenkins configurations into the Bamboo easily. Verdict: Great On-premises CI tool that originally offered Cloud solution too. Bitbucket Pipelines replaced the cloud solution. Pipelines is a modern and fast cloud CI tool integrated into Bitbucket. Has a free trial for 30 days, and paid plans after that. Official website: Bamboo Availability: Paid with a free trial Platform: On-premises

    GitLab CI

    gitlab logo GitLab CI is an integral part of the open-source Rails project GitLab, which was brought to light by the company GitLab inc. It is hosted on GitLab.com, a free hosted service and it provides detailed git repository management with features like access control, issue tracking, code reviews and much more. GitLab CI integrates seamlessly with GitLab and it can easily hook projects using the GitLab API. GitLab runners that process builds are written in Go language and can run on Windows, Linux, OSX, FreeBSD, and Docker. The official Go runner can run multiple jobs concurrently and has inbuilt Docker support. Gitlab CI comes with both the open-source GitLab Community Edition and with the GitLab Enterprise Edition. Verdict: A Phenomenal hosted tool with impressive list of features, offers both free and enterprise solutions. Official website: GitLab CI Availability: Free and paid with trial Platform: Hosted (can be hosted for you on Gitlab.com)

    CircleCI

    circleci logo Another cloud alternative that comes from the company with the same name. CircleCI currently only supports GitHub and the list of supported languages includes Java, Ruby/Rails, Python, Node.js, PHP, Haskell, and Scala. What separates CircleCI from the other tools is the way they offer services. The main pricing block for the CircleCI is the "container". One container is free and you can build as many projects on it as you need. Once you start adding more containers (at a fixed price each) you can choose the level of parallelization that suits your needs. There are 5 levels of parallelization (1x, 4x, 8x, 12x and 16x). So, starting with the 16 containers, you can achieve maximum parallelization of 16x on one build. Or you can run 4 builds on 16 containers with 4x parallelization. It is up to you. And did I mention CircleCI supports Docker? Verdict: Flexible cloud CI tool that offers parallelization up to 16x. Excellent if you need something built fast and money is not the biggest issue (can reach up to $3150/mo). Official website: CircleCI Availability: Free and paid with trial Platform: Hosted

    Codeship

    codeship logo If you haven't had enough hosted solutions up until now, here is another one. Codeship comes in two different versions: Basic and Pro. Basic version offers out-of-the-box Continuous Integration service but doesn't have docker support and its main purpose is to build applications with common workflows through the UI. Pro version offers more flexibility and docker support. The basic version comes in several paid packages, where the more expensive ones have more parallelization power. In the pro version, you get to choose your instance type and the amount of parallelization up to 20x). It can get a bit pricey, but some teams may need that kind of power. Verdict: Powerful hosted solution with docker support, flexible plans suited both for small teams and enterprises alike. Official website: Codeship Availability: Free for 100 builds per month and paid for more than that Platform: Hosted

    Honorable mention: Codefresh

    codefresh logoMany tools on this list have Docker support, but Codefresh was designed and built from the ground up specifically with the containers in mind. Docker can be a bit overwhelming to figure out at first, and the guys from the Codefresh inc. are well aware of that. In addition to working with existing docker files, you can choose from several different templates to ease the migration of your project to Docker containers. UI is clean and intuitive, there is almost no need to parse through the documentation to start using it. The reason this CI tool deserves to be on the list lies in a feature that surprised me a bit. And that feature is launching your images to a stage-like environment. When the build finishes, you can launch the image to see if it works! That effectively means you get a staging environment without a need to provision additional virtual machines or deploy anything. And that's great! Codefresh is still very young and has room for improvement and new features, (eg. .NET core template and more deployment options), but it treats containers as a first class citizen and that makes it an ideal solution for any team that plans to utilize Docker. Verdict: Easy to use tool with Docker containers at its core and very nice feature of launching the built Docker images to the hosted environment. Official website: Codefresh Availability: Free for 200 builds per month, 5 concurrent builds, and 1 hosted environment, paid for additional stuff Platform: Hosted and On-Premises via Kubernetes and Helm Charts

    So, What Is the Perfect Continuous Integration Tool for You and Your Team?

    There are several things to keep in mind when choosing the right CI tool for your projects. On-premises solutions offer a great deal of build process flexibility and store the artifacts locally. This may or may not be important to you, but in some cases and for some companies, it might be mandatory. On the other hand, the hosted solutions offer no hassle setup and greater scalability since you don't need hardware to host them. Another important thing is the Docker support. Docker revolutionized the way we distribute our apps and has become something you should not ignore. Although the vast majority of the tools support Docker, some take it more seriously than others. And the last and often neglected aspect is the user interface. I found some of the tools from the list much easier to use than others. You cannot say with the clear conscious that the UI is not important because one of the main roles of any good CI tool is to make a build process easier. It should not be hard or complicated. [sc name="subscribe" formNumber="3820" contentType="CI/CD"]]]>
    91 0 0 0 40 0 0 41 http://www.code-maze.com/blog 40 0 42 41 0 43 0 0 44 http://www.code-maze.com/blog 43 0 45 0 0 46 https://app.shippable.com 0 0 49 http://www.code-maze.com/blog 46 0 50 0 0 67 0 0 71 http://www.techbeamers.com/ 0 0 79 http://todaywasawesome.com 40 0 80 0 0 127 0 0 128 https://code-maze.com/ 127 0 180 0 0 181 https://code-maze.com/ 180 0 1645 0 0
    What Is Continuous Integration and Why Do You Need It? https://code-maze.com/what-is-continuous-integration/ Wed, 03 Feb 2016 18:50:37 +0000 http://www.code-maze.com/?p=161 Continuous Integration.  It was originally adopted as the extreme programming practice and its main purpose is to prevent integration problems and to avoid "integration hell". So let's learn what Continuous Integration is and how it can help you become a better software developer. In this article you will learn: This knowledge will make you more proficient than most people that blindly walk into the CI world. After you've learned about what is the Continuous Integration here head to the top Continuous Integration tools to choose the one that fits you best. If you are more into the mobile app development, you can also check the Top Mobile Continuous Integration Tools list.

    What Is Continuous Integration?

    Continuous Integration is the practice of continuously integrating the changes made to the project and testing them accordingly at least on a daily basis or more frequently. Martin Fowler put it nicely:
    Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily – leading to multiple integrations per day. Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible. Many teams find that this approach leads to significantly reduced integration problems and allows a team to develop cohesive software more rapidly. Martin Fowler
      Automating your build, test and deploy processes can alleviate much of the headaches and problems commonly happening on projects. Having a reliable method of integrating changes more frequently ensures that errors can be found sooner than later. Having a blocking issue that appeared right on the demo day as the result of some part of the code you implemented a few months earlier and haven't had a proper chance to test against other parts of your system is not a pleasant event at all. We can all agree that having a blocking issue that appeared right in the demo is not a pleasant experience. CI can help with that. A lot. Here is how the basic CI lifecycle looks like. continuous integration cycle

    Benefits of Using Continuous Integration

    Using the CI is beneficial for many reasons. Reduced integration risk. More often than not, working on projects means multiple people are working on the separate tasks or parts of the code. The more people, the riskier the integration. Depending on how bad the problem really is, debugging and solving the issue can be really painful and can potentially mean a lot of changes to the code. Integrating on a daily basis or even more frequently can help reduce these kinds of problems to a minimum. Higher code quality. Not having to worry about the problems, and focusing more on the functionality of the code results in a higher quality product. The code in version control works. If you commit something that breaks the build, you and your team get the notice immediately and the problem is fixed before anyone else pulls the "broken" code. Reduced friction between team members. Having the impartial system in place reduces the frequency of quarrels between team members. The quality of life improvement for testers. Having different versions and builds of the code can help isolate and trace bugs efficiently, and it makes life easier for the QA team. Less time deploying. Deploying projects can be very tedious and time-consuming, and automating that process makes perfect sense. Increased confidence and morale. People that don't work for fear of breaking something, are more likely to produce better results and can focus their energy and concentration on producing instead of worrying about potential consequences of their actions. One side effect of all these benefits is that new team members will have a much easier time getting into the project. Having a clear vision of the building process can greatly speed up adaptation of the newest dev on the team.

    Requirements

    But you might be wondering what are the requirements for the installing of the CI system for your needs. If you want to install CI server in your own environment, you'll need a few things first. The first requirement is having the version control system (VCS). There is no way around it and there shouldn't be a way around it. VCS provides a reliable method to centralize and preserve changes made to your project over time. If you are using onsite solutions another requirement is to have a spare server or workstation or at the very least a virtual machine. Having a clean machine to build your system on is of the essential importance. If you don't want to mess with either servers or virtual machines, there are many hosted CI tool solutions that abstract the maintenance of the whole process and offer easier scalability. Disadvantages of the hosted systems are usually the lack of configuration options that self-hosted tools offer. If you opt-in for the self-hosted variant, you will need to install one of the many available continuous integration tools. Technically, the CI tool is not required per se, like the IDE is not required for software development but it would be significantly harder to implement Continuous Integration without the help of one. The most widely known and used CI tools include Jenkins, TeamCity, Bamboo, Go... Read more about the top CI tools available.

    Continuous Integration Servers

    Continuous integration server (aka build server, aka CI server) is a software tool that centralizes all your CI operations and provides a reliable and stable environment for you to build your projects on. CI servers are highly configurable and adjustable to be able to build a variety of projects for different platforms. Running builds and tests are the basic features of every build server. The most important things to consider when using CI server is to have a clean machine prepared for its installation. Having a neutral environment, untainted by unnecessary tools, environment variables, and other configurations, is crucial for the successful usage of the CI server and CI overall. If it's not possible to install the CI server physical machine, you can set up a virtual environment and use it as the last resort. Using development machines without setting up virtual environments will probably leave you with false assumptions and results. Once you deploy the application to another machine, you could potentially run into new problems. Typically CI server uses a version control system like Subversion or Git or any other to pull your project files. It monitors your project's repository and on the successful commit it pulls the changes and performs the tasks you defined previously for that project. Upon completion of the tasks, CI server sends feedback to the relevant project members with the details of the build. Checking out the latest version of your project, running the build scripts, running the tests, and sending notifications are the most basic functionalities of the CI servers. Besides these, features like code analysis, code coverage, code quality reports, agent pooling, pipelines, build comparisons, IDE integration, third-party tools support and many others make the CI servers very flexible and comfortable to use.

    Everyone is responsible

    Even more important than hardware or software requirements is the ability of the team to take the responsible approach to implementation of the CI. In order to use CI effectively, developers must change their day-to-day software development habits. These habits are good software development practices that you should apply even without continuous integration system in place. There are six practices that help individuals and teams running CI on a project:
    • Commit code frequently
    • Don’t commit broken code
    • Write unit tests
    • Fix broken builds immediately
    • All tests must pass
    • Avoid breaking code
    That means you should not be checking in the latest changes just before the lunchtime or just before you head home. You should wait for the build report to be sure the build is successful, and if it is not you should be the one fixing it. On the other hand, you should always be on a lookout for the broken builds, even if it wasn't your fault because checking out broken build or checking in over it can prolong the fixing process.

    Continuous Delivery and Continuous Deployment

    Typical CI lifecycle consists of building the project, unit testing, deploying to stage and acceptance testing. Once the project successfully passes all of these stages, it is ready for the deployment to the production environment. Delivering a project to the production environment can be done automatically like the rest of the stages. But due to the business concerns, it might be more suitable to do it manually. In the first case, we are talking about Continuous Delivery, and in the second Continuous Deployment. The graph below shows the differences between these three terms. continuous development and delivery

    Potential pitfalls

    There are some concerns that might discourage people from using the CI on their projects.
    1. Increased project maintenance overhead.
    You already need to build, test and deploy your systems. If you don't manage those processes with the CI, you will be managed by those processes. Once the project gets to the certain complexity, it becomes incredibly hard to manage.
    1. Too much change.
    People might be unwilling to make so many changes to implement the CI on their legacy system. If that is the case, you can easily introduce CI to the system one part at a time. Once the team members are comfortable with the results, CI can be introduced to the other parts as well.
    1. Hardware/Software costs.
    Costs of implementing a CI could be perceived as unnecessary by both developers and management on a project. But these costs are marginal compared to the costs of the maintenance and finding the problems later down the line.
    1. Developers are already doing all these operations.
    Certainly, but they could be investing more time in building the functionality and solving business problems. Machines excel in doing repetitive tasks reliably.
    1. Project too small.
    Even the smallest projects can benefit from CI. Especially with the plethora of online Continuous Integration tools that are very easy to set up. Even the smallest project can benefit from transparency of the development process and centralization of project resources and builds.

    Conclusion

    Agile practices and Continuous Integration go hand in hand. You can't consider your work or business serious without using CI anymore. Infrequent software releases are a thing of the past and most of the leading and successful companies are already actively using these techniques with good results, Amazon being one of the leaders by making changes to production every 11.6 seconds (May 2011). Setting up the continuous integration system for your project could be potentially costly and time-consuming task. But technical debt caused by not using CI can be multiple times bigger. [sc name="subscribe" formNumber="3820" contentType="CI/CD"]]]>
    161 0 0 0 47 https://app.shippable.com 0 0 48 http://www.code-maze.com/blog 47 0 223 0 0
    A Few Great Ways to Consume RESTful API in C# https://code-maze.com/different-ways-consume-restful-api-csharp/ Mon, 05 Jun 2017 18:00:42 +0000 http://www.code-maze.com/?p=296 What is a RESTful API? So, before we start, you might be wondering what does API stand for, and what is the RESTful part all about? To put things simply, APIs are the layers between software applications. You can send the request to the API, and in return, you get the response from it. APIs hide all the nitty-gritty details of the concrete implementation of a software application and expose the interface you should use to communicate with that application. [sc name="part_of_series" headline="Recommended Articles"] The whole internet is the one big spider web made of APIs. We use APIs to communicate and relate information between applications. We have an API for pretty much anything out there. Most of the services you use daily have their own APIs (GoogleMaps, Facebook, Twitter, Instagram, weather portals...) RESTful part means that API is implemented in accordance with the principles and rules of the REST (Representational State Transfer) which is the underlying architectural principle of the web. RESTful APIs in most cases return the plain text, JSON or XML response. Explaining REST in more detail is out of the scope of this article, but you can read more about REST in our article Top REST API best practices.

    How to Consume RESTful APIs

    Ok, let's go to the meaty part of this whole story. There are several ways to consume a RESTful API in C#: Every one of these has pros and cons, so let us go through them and see what they offer. As an example, we will be collecting information about RestSharp repo releases and their publish dates via GitHub API. This information is available publicly and you can see how raw JSON response looks here: RestSharp releases We are going to utilize the help of the Json.NET library to deserialize the response we get. Also, for some examples, we are going to use the inbuilt deserialization mechanisms of the libraries. It's up to you to choose which way you prefer because no way is the right way. (You can see the implementation for both mechanisms in the source code). What I expect to get as a result of the next few examples is a deserialized JArray (for simplicity) that contains RestSharp release information. After that, we can iterate through it to get the following result.

    HttpWebRequest/Response Class

    It's the HTTP-specific implementation of WebRequest class which was originally used to deal with HTTP requests, but it was made obsolete and replaced by the WebClient class. The HttpWebRequest class offers fine-grained control over every aspect of the request making process. As you can imagine, this can be the double-edged sword and you can easily end up losing enormous amounts of time fine-tuning your requests. On the other hand, this might just be what you need for your specific case. HttpWebRequest class does not block the user interface, which is, I am sure you will agree with this one, pretty important. HttpWebResponse class provides a container for the incoming responses. This is a simple example of how to consume an API using these classes.
    public class HttpWebRequestHandler : IRequestHandler
    {
        public string GetReleases(string url)
        {
            var request = (HttpWebRequest)WebRequest.Create(url);
    
            request.Method = "GET";
            request.UserAgent = RequestConstants.UserAgentValue;
            request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
    
            var content = string.Empty;
    
            using (var response = (HttpWebResponse)request.GetResponse())
            {
                using (var stream = response.GetResponseStream())
                {
                    using (var sr = new StreamReader(stream))
                    {
                        content = sr.ReadToEnd();
                    }
                }
            }
    
            return content;
        }
    }
    Although a simple example, it becomes much more complicated when you need to deal with more sophisticated scenarios like posting form information, authorizing etc.

    WebClient Class

    This class is a wrapper around HttpWebRequest. It simplifies the process by abstracting the details of the HttpWebRequest from the developer. The code is easier to write and you are less likely to make mistakes this way. If you want to write less code, not worry about all the details, and the execution speed is a non-factor, consider using WebClient class. This example should give you the rough idea how much easier is to use WebClient compared to the HttpWebRequest/HttpWebResponse approach.
    public string GetReleases(string url)
    {
        var client = new WebClient();
        client.Headers.Add(RequestConstants.UserAgent, RequestConstants.UserAgentValue);
    
        var response = client.DownloadString(url);
    
        return response;
    }
    Much easier, right? Other then DownloadString method, WebClient class offers a host of other useful methods to make our life easier. We can easily manipulate strings, files or byte arrays using it, and for a price of just a few milliseconds slower than HttpWebRequest/HttpWebResponse approach. Both the HttpWebRequest/HttpWebResponse and WebClient classes are available in the older versions of .NET. Be sure to check out the MSDN if you are interested what else WebClient has to offer.

    HttpClient Class

    HttpClient is the "new kid on the block", and offers some of the modern .NET functionalities that older libraries lack. For example, you can send multiple requests with the single instance of HttpClient, it is not tied to the particular HTTP server or host, makes use of async/await mechanism. You can find out about the five good reasons to use HttpClient in this video:
    • Strongly typed headers.
    • Shared Caches, cook­ies, and credentials
    • Access to cook­ies and shared cookies
    • Con­trol over caching and shared cache.
    • Inject your code mod­ule into the ASP.NET pipeline. Cleaner and mod­u­lar code.
    Here is HttpClient in action in our example:
    public string GetReleases(string url)
    {
        using (var httpClient = new HttpClient())
        {
            httpClient.DefaultRequestHeaders.Add(RequestConstants.UserAgent, RequestConstants.UserAgentValue);
    
            var response = httpClient.GetStringAsync(new Uri(url)).Result;
    
            return response;
        }
    }
    For the simplicity's sake, I implemented it synchronously. Every HttpClient method is meant to be used asynchronously and SHOULD be used that way. Also, I need to mention one more thing. There is a debate whether HttpClient should be wrapped in a using block or statically on the app level. Although it implements IDisposable, it seems that by wrapping it in the using block you can make your app malfunction and get the SocketException. And as Ankit blogs, the performance test results are much in favor of static initialization of the HttpClient. Be sure to read these blog posts as they can help you be more informed about the correct usage of the HttpClient library. And don't forget, being modern, HttpClient is exclusive to the .NET 4.5, so you might have trouble using it on some legacy projects.

    RestSharp

    RestSharp is the OpenSource alternative to standard .NET libraries and one of the coolest .NET libraries out there. It is available as a NuGet package, and there are a few reasons why you should consider trying it out. Like HttpClient, RestSharp is a modern and comprehensive library, easy and pleasant to use, while still having support for older versions of .NET Framework. It has inbuilt Authentication and Serialization/Deserialization mechanisms but allows you to override them with your custom ones. It is available across platforms and supports OAuth1, OAuth2, Basic, NTLM and Parameter-based Authentication. You can choose to work both synchronously or asynchronously. There is a lot more to this library, and these are just some of the great benefits it offers. For the detailed information on usage and capabilities of RestSharp, you can visit the RestSharp page on GitHub. Now let's try to get a list of RestSharp releases using RestSharp :D
    public string GetReleases(string url)
    {
        var client = new RestClient(url);
    
        var response = client.Execute(new RestRequest());
    
        return response.Content;
    }
    Simple enough. But don't let that fool you, RestSharp is very flexible and has all the tools you need to achieve almost anything while working with RESTful API. One thing to note in this example is that I didn't use RestSharp's deserialization mechanism due to the example consistency, which is a bit of a waste, but I encourage you to use it as it is really easy and convenient. So you can easily make a container like this:
    public class GitHubRelease
    {
        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }
        [JsonProperty(PropertyName = "published_at")]
        public string PublishedAt { get; set; }
    }
    And after that use Execute method to directly deserialize the response to that container. You can add just the properties you need and use attribute JsonProperty to map them to C# properties (nice touch).  Since we get the list of releases in our response, we use the List<Release> as a containing type.
    public List<GitHubRelease> GetDeserializedReleases(string url)
    {
        var client = new RestClient(url);
    
        var response = client.Execute<List<GitHubRelease>>(new RestRequest());
    
        return response.Data;
    }
    Pretty straightforward and elegant way to get our data. There is a lot more to RestSharp than sending GET requests, so explore and see for yourself how cool it can be. One final note to add to the RestSharp case is that its repository is in need of maintainers. If you want to learn more about this cool library, I urge you to head over to RestSharp repo and help this project stay alive and be even better.

    ServiceStack Http Utils

    Another library, but unlike RestSharp, ServiceStack seems to be properly maintained and keeping the pace with modern API trends. List of ServiceStack features is impressive and it certainly has various applications. What is most useful to us here is to demonstrate how to consume an external RESTful API. ServiceStack has a specialized way of dealing with 3rd Party HTTP APIs called Http Utils. Let us see how fetching RestSharp releases looks like using ServiceStack Http Utils first using the Json.NET parser.
    public string GetReleases(string url)
    {
        var response = url.GetJsonFromUrl(webReq =>
        {
            webReq.UserAgent = RequestConstants.UserAgentValue;
        });
    
        return response;
    }
    You can also choose to leave it to the ServiceStack parser. We can reuse the Release class defined earlier in the post.
    public List<GitHubRelease> GetDeserializedReleases(string url)
    {
        var releases = url.GetJsonFromUrl(webReq =>
        {
            webReq.UserAgent = RequestConstants.UserAgentValue;
        }).FromJson<List<GitHubRelease>>();
    
        return releases;
    }
    As you can see, either way works fine, and you can choose whether you get the string response or deserialize it immediately. Although ServiceStack is the last library we stumbled upon, we were pleasantly surprised how easy it was for me to use it, and I think it may become my go-to tool for dealing with APIs and services in the future.

    Flurl

    One of the libraries requested by many people in the comments section, and loved by many all over the internet but still gaining traction. Flurl stands for Fluent Url Builder, which is the way the library builds its queries. For those of you not familiar with the fluent way of doing stuff, fluent simply means that library is built in such a way that methods are chained to achieve greater readability, similar to that of human language. To make things even easier to understand, let's give some examples (this one is from official docs):
    // Flurl will use 1 HttpClient instance per host
    var person = await "https://api.com"
        .AppendPathSegment("person")
        .SetQueryParams(new { a = 1, b = 2 })
        .WithOAuthBearerToken("my_oauth_token")
        .PostJsonAsync(new
        {
            first_name = "Claire",
            last_name = "Underwood"
        })
        .ReceiveJson<Person>();
    You can see how the methods chain together to complete the "sentence". In the background, Flurl is using HttpClient or rather enhancing HttpClient library with its own syntactic sugar. So that means Flurl is an async library and it's good to have that in mind. As with other advanced libraries, we can do this in two different ways:
    public string GetReleases(string url)
    {
        var result = url
            .WithHeader(RequestConstants.UserAgent, RequestConstants.UserAgentValue)
            .GetJsonAsync<List<GitHubRelease>>()
            .Result;
    
        return JsonConvert.SerializeObject(result);
    }
    This way is rather terrible since we are serializing result only to deserialize it a little bit later. If you are using a library such as Flurl, you shouldn't be doing things this way. A better way of doing things would be:
    public List<GitHubRelease> GetDeserializedReleases(string url)
    {
        var result = url
            .WithHeader(RequestConstants.UserAgent, RequestConstants.UserAgentValue)
            .GetJsonAsync<List<GitHubRelease>>()
            .Result;
    
        return result;
    }
    With .Result we are forcing synchronous behavior of the code. The real and intended way to use Flurl would look like this:
    public async Task<List<GitHubRelease>> GetDeserializedReleases(string url)
    {
        var result = await url
            .WithHeader(RequestConstants.UserAgent, RequestConstants.UserAgentValue)
            .GetJsonAsync<List<GitHubRelease>>();
    
        return result;
    }
    Which shows off the full potential of the Flurl library. If you want to learn more about how to use Flurl in different real-life scenarios, check out our Consuming GitHub API (REST) With Flurl article In summary, it's like advertised: easy to use, modern, readable and testable. What more can you expect of a library? To be open source? Check out: Flurl repo and contribute if you like it!

    DalSoft.RestClient

    Now this one is a bit different than anything on this list so far. But this one does it a bit differently. Let's see how we can use DalSoft.RestClient to consume the GitHub API and then talk about what we've done. First things first, you can download DalSoft.RestClient either via NuGet Package Manager by typing : Install-Package DalSoft.RestClient or via .NET Core CLI: dotnet add package DalSoft.RestClient Either way is fine. Once we have our library, we can do something like this:
    public string GetReleases(string url)
    {
        dynamic client = new RestClient(RequestConstants.BaseUrl,
            new Headers { { RequestConstants.UserAgent, RequestConstants.UserAgentValue } });
    
        var response = client.repos.restsharp.restsharp.releases.Get().Result.ToString();
    
        return response;
    }
    or preferably by using DalSoft.RestClient to deserialize the response immediately while utilizing its full power:
    public async Task<List<GitHubRelease>> GetDeserializedReleases(string url)
    {
        dynamic client = new RestClient(RequestConstants.BaseUrl,
            new Headers { { RequestConstants.UserAgent, RequestConstants.UserAgentValue } });
    
        var response = await client.repos.restsharp.restsharp.releases.Get();
    
        return response;
    }
    So, let's talk about these examples a bit. At first glance, it doesn't seem much simpler than some other modern libraries that we've used. But it comes down to the way form our requests and that's by utilizing the dynamic nature of our RestClient. For example, our BaseUrl is https://api.github.com and we need to get to the https://api.github.com/repos/restsharp/restsharp/releases. We can do that by creating our client dynamically and then forming the Url by chaining "parts" of the Url:
    await client.repos.restsharp.restsharp.releases.Get();
    A pretty unique way to form a request. And a very flexible one too! So, once we have our base Url set, we can play with different endpoints easily. It's also worth mentioning that JSON response we get is being automatically type-casted. As you can see in the second example, the return value of our method is Task<List<GitHubReleases>>. So the library is smart enough to cast the response to our type (relying on Json.NET). That makes our life much easier. Besides being easy to understand and use, DalSoft.RestClient has everything a modern library should have. It is configurable, asynchronous, extensible, testable and it supports multiple platforms. We've demonstrated just a small portion of the DalSoft.RestClient features. If this got you interested in using DalSoft.RestClient, head over to our article about it to learn how to use it in different scenarios or refer to official GitHub repo and documentation.

    Other Options

    There are a lot of other options available for your specific problems. You can use any of these libraries to consume a specific RESTful API. For example, octokit.net is used to work with GitHub API specifically, Facebook SDK is used for consuming Facebook API and there are many others for almost anything. While these libraries are made specifically for those APIs and may be great at doing what they are meant for, their usefulness is limited because you often need to connect with more than one API in your applications. This may result in having different implementations for each one, and more dependencies which potentially leads to repetitiveness and is error-prone. The more specific the library, the less flexibility it has.

    Source Code on GitHub

    Source Code on GitHub

    Conclusion

    So, to summarize, we've talked about different tools you can use to consume a RESTful API. We've mentioned some .NET libraries that can do that like HttpWebRequest, WebClient, and HttpClient, as well as some of the amazing third-party tools like RestSharp and ServiceStack. You also gave you very short introductions to those tools and made some very simple examples to show you how you can start using them. I consider you now at least 95% ready to consume some REST. Go forth and spread your wings, explore and find even more fancy and interesting ways to consume and connect different APIs. Sleep restfully now, you know the way :) [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
    296 0 0 0 61 0 0 62 https://www.code-maze.com/ 61 0 88 0 0 89 https://code-maze.com/ 88 0 203 0 0 205 https://code-maze.com/ 203 0 208 0 0 209 https://code-maze.com/ 208 0 232 0 0 233 https://code-maze.com/ 232 0 268 0 0 270 https://code-maze.com/ 268 0 367 0 0 368 367 0 369 https://code-maze.com/ 367 0 376 0 0 377 https://code-maze.com/ 61 0 378 https://code-maze.com/ 376 0 386 378 0 387 https://code-maze.com/ 386 0 522 0 0 523 https://code-maze.com/ 522 0 547 http://www.id6075242.sexyxyo.website 523 0 999 0 0 1000 https://code-maze.com/ 999 0 1008 0 0 1013 https://code-maze.com/ https://code-maze.com/upload-files-dot-net-core-angular/ Once the file is uploaded and stored safely on the server, you can use a GET request with that resource id and an appropriate MIME type to get it. Hope this clears it up for you! If you have any more questions, do feel free to ask.]]> 1008 0 1076 0 0 1079 https://code-maze.com/ 1076 0 1563 0 0 1564 public static class RequestConstants { public const string BaseUrl = "https://api.github.com"; public const string Url = "https://api.github.com/repos/restsharp/restsharp/releases"; public const string UserAgent = "User-Agent"; public const string UserAgentValue = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"; } Please verify that you have the public access modifier defined on that class. Your error has nothing to do with the environment you are developing in, just with the lower accessibillity level of the class. We hope this would solve your problem. Best regards.]]> 1563 0 1565 1564 0 1566 1565 0 1593 0 0 1597 https://code-maze.com/ 1593 0
    The HTTP Series (Part 1): Overview of the HTTP Basic Concepts https://code-maze.com/http-series-part-1/ Mon, 19 Jun 2017 05:29:35 +0000 http://www.code-maze.com/?p=350 But why HTTP? Why should I read about HTTP you may ask yourself? Well, if you are a software developer, you will understand how to write better applications by learning how they communicate. If you are system architect or network admin, you will get deeper knowledge on designing complicated network architectures. REST, which is very important architectural style nowadays is relying completely on utilizing HTTP features, so that makes HTTP even more important to understand. If you want to make great RESTful applications, you must understand HTTP first. I should note that REST doesn't rely on HTTP only. It can be implemented using other protocols, but it seems that HTTP won that battle by a far margin, and you'll hardly find the REST implementation using other protocols. So are you willing to pass on the chance to understand and learn the fundamental concepts of the World Wide Web and network communication? I hope not :) The article will focus on the most important parts of HTTP and attempt to explain them as simply as possible. The idea is to organize all the useful information about HTTP in one place, to save you the time of going through books and RFCs to find the information you need. This is the first article of the HTTP series. It gives a short introduction of the basic concepts of the HTTP. You will learn about: Without further ado, let's dive in.

    HTTP Definition

    The founder of HTTP is Tim Berners-Lee (the guy also considered to be the inventor of the World Wide Web). Among other names important to the development of HTTP is also Roy Fielding, who is also the originator of REST architectural style. The Hypertext Transfer Protocol is the protocol that applications use to communicate with each other. In essence, HTTP is in charge of delegating all of the internet's media files between clients and servers. That includes HTML, images, text files, movies and everything in between. And it does this quickly and reliably. HTTP is the application protocol and not the transport protocol because we are using it for the communication in the application layer. To jog your memory here is what the Network Stack looks like. Network stack From this image, you can clearly see the that HTTP is the application protocol and that TCP works on the transport layer.

    Resources

    resource

    Everything on the internet is a resource, and HTTP works with resources. That includes files, streams, services and everything else. An HTML page is a resource, a youtube video is a resource, your spreadsheet of daily tasks on a web application is a resource... You get the point. And how do you differentiate one resource from another? By giving them URLs (Uniform resource locators). A URL points to the unique location where the resource is located.

    How To Exchange Messages Between a Web Client and a Web Server

    Every piece of content, every resource lives on some Web server (HTTP server). These servers are expecting HTTP requests for those resources. But how do you request a resource from a Web server? You need an HTTP client of course :) You are using an HTTP client right now to read this article. Web browsers are HTTP clients. They communicate with HTTP servers to fetch the resources to your computer. Some of the most popular clients are Google's Chrome, Mozilla's Firefox, Opera, Apple's Safari, and unfortunately still the infamous Internet Explorer.

    Some Message Examples

    So what does HTTP message look like? Without talking too much about it, here are some examples of HTTP messages: GET request
    GET /repos/CodeMazeBlog/ConsumeRestfulApisExamples HTTP/1.1
    Host: api.github.com
    Content-Type: application/json
    Authorization: Basic dGhhbmtzIEhhcmFsZCBSb21iYXV0LCBtdWNoIGFwcHJlY2lhdGVk
    Cache-Control: no-cache
    POST request
    POST /repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks?access_token=5643f4128a9cf974517346b2158d04c8aa7ad45f HTTP/1.1
    Host: api.github.com
    Content-Type: application/json
    Cache-Control: no-cache
    
    {
      "url": "http://www.example.com/example",
      "events": [
        "push"
      ],
      "name": "web",
      "active": true,
      "config": {
        "url": "http://www.example.com/example",
        "content_type": "json"
      }
    }
    Here is an example of one GET and one POST request. Let's go quickly through the different parts of these requests. The first line of the request is reserved for the request line. It consists of the request method name, the request URI, and the HTTP version. The next few lines represent the request headers. Request headers provide additional info to the requests, like the content types the request expects in response, authorization information etc, For a GET request, the story ends right there. A POST request can also have a body and carry additional info in the form of a body message. In this case, it is a JSON message with additional info on how to create the GitHub webhook for the given repo specified in the URI. That message is required for the webhook creation so we are using a POST request to provide that information to the GitHub API. The Request line and request headers must be followed by <CR><LF> (carriage return and line feed \r\n), and there is a single empty line between the message headers and the message body that contains only CRLF. Reference for HTTP request: https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html And what do we get as a response to these requests? Response message
    HTTP/1.1 200 OK
    Server: GitHub.com
    Date: Sun, 18 Jun 2017 13:10:41 GMT
    Content-Type: application/json; charset=utf-8
    Transfer-Encoding: chunked
    Status: 200 OK
    X-RateLimit-Limit: 5000
    X-RateLimit-Remaining: 4996
    X-RateLimit-Reset: 1497792723
    Cache-Control: private, max-age=60, s-maxage=60
    
    [
      {
        "type": "Repository",
        "id": 14437404,
        "name": "web",
        "active": true,
        "events": [
          "push"
        ],
        "config": {
          "content_type": "json",
          "insecure_ssl": "0",
          "url": "http://www.example.com/example"
        },
        "updated_at": "2017-06-18T12:17:15Z",
        "created_at": "2017-06-18T12:03:15Z",
        "url": "https://api.github.com/repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks/14437404",
        "test_url": "https://api.github.com/repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks/14437404/test",
        "ping_url": "https://api.github.com/repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks/14437404/pings",
        "last_response": {
          "code": 422,
          "status": "misconfigured",
          "message": "Invalid HTTP Response: 404"
        }
      },
    ]
    The response message is pretty much structured the same as the request, except the first line, called the status line, which surprising as it is, carries information about the response status. Response headers and response body come right after the status line. Reference for HTTP response: https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html

    MIME Types

    MIME types represent a standardized way to describe the file types on the internet. Your browser has a list of MIME types and same goes for web servers. That way we can transfer files in the fashion regardless of the operating system. Fun fact is that MIME stands for the Multipurpose Internet Mail Extension because they were originally developed for the multimedia email. They were adapted to be used for HTTP and several other protocols since. Every MIME type consists of a type, subtype and a list of optional parameters in the following format: type/subtype; optional parameters. Here are a few examples:
    Content-Type: application/json
    Content-Type: text/xml; charset=utf-8
    Accept: image/gif
    
    You can find the list of commonly used MIME types and subtypes in the HTTP reference.

    Request Methods

    HTTP request methods (referred to also as "verbs") define the action that will be performed on the resource. HTTP defines several request methods. The most commonly known/used are GET and POST methods. A request method can be idempotent or not idempotent. This is just a fancy term for explaining that method is safe/unsafe to be called several times on the same resources. In other words, that means that GET method, that has a sole purpose of retrieving information, should by default be idempotent. Calling GET on the same resource over and over should not result in a different response. On the other hand, the POST method is not an idempotent method. Prior to HTTP/1.1, there were just three methods: GET, POST and HEAD, and the specification of HTTP/1.1 brought a few more methods into the play: OPTIONS, PUT, DELETE, TRACEand CONNECT. Find more how each one of these methods works in the HTTP Reference.

    Headers

    Header fields are colon-separated name-value fields you can find just after the first line of a request or response message. They provide more context to the HTTP messages and inform clients and servers about the nature of the request or response. There are five types of headers:
    • General headers: These headers are useful to both the server and the client. One good example is the Date header field which provides the information about the time of the message creation.
    • Request headers: Specific to the request messages. They provide the server with additional information. For example, Accept: */* header field informs the server that the client is willing to receive any media type.
    • Response headers: Specific to the response messages. They provide the client with the additional information. For example, Allow: GET, HEAD, PUT header field informs the client which methods are allowed for the requested resource.
    • Entity headers: These headers deal with the entity body. For example, Content-Type: text/html header lets the application know that the data is HTML document.
    • Extension headers: These are nonstandard headers application developers can construct. Although they are not part of HTTP, it tolerates them.
    You can find the list of commonly used request and response headers in the HTTP Reference.

    Status Codes

    404batman The status code is a three digit number that denotes the result of a request. Reason phrase which is humanly readable status code explanation, comes right after. Some examples include:
    • 200 OK
    • 404 Not Found
    • 500 Internal Server Error
    The status codes are classified by the range in five different groups. Both the status code classification and the entire list of status codes and their meaning can be found in the HTTP Reference.

    Conclusion

    Phew, that was a lot of information. The knowledge you gain by learning HTTP basic concepts is not the kind that helps you to solve some problem directly. But it gives you the understanding the underlying principle of the internet communication which you can apply to almost every other problem on the higher level than HTTP. Whether it is REST, APIs, web application development or network, you can now be at least a bit more confident while solving these kinds of problems. Of course, HTTP is a pretty large topic to talk about and there is still a lot more to it than the basic concepts. Read about the architectural aspects of HTTP in part 2 of the HTTP series. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
    350 0 0 0 65 0 0 66 https://www.code-maze.com/ 65 0 68 0 0 69 https://www.code-maze.com/ 68 0 70 69 0 77 0 0 78 https://www.code-maze.com/ 77 0
    The HTTP Reference https://code-maze.com/the-http-reference/ Mon, 19 Jun 2017 05:29:58 +0000 http://www.code-maze.com/?p=381
  • Request Methods
  • Status Codes
  • Headers
  • MIME Types
  • Request Methods List of HTTP Request methods (verbs).

    Request Methods

    [table id=10 /]

    Status Codes

    These two tables define status code ranges (classification) and describe all the status codes.

    Status Code Classification

    [table id=1 /]

    Status Codes

    [table id=2 /] Reference: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

    Headers

    Both HTTP request and HTTP response can contain header fields. These two tables describe those fields and provide simple examples.

    Request Headers

    [table id=3 /]

    Response Headers

    [table id=4 /] Reference: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

    MIME Types

    Because of the sheer quantity of Internet Media Types, just the most commonly used ones are listed here.

    Primary MIME Types

    [table id=8 /]

    Application MIME Types

    [table id=5 /]

    Multipart MIME Types

    [table id=6 /]

    Text MIME Types

    [table id=7 /] References: https://www.iana.org/assignments/media-types/media-types.xhtml Everything mentioned in this reference article can be found in more detail in the HTTP1.1 spec document: http://www.ietf.org/rfc/rfc2616.txt [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
    381 0 0 0 53 0 0 58 0 0 59 https://www.code-maze.com/ 58 0 60 59 0 63 0 0 64 https://www.code-maze.com/ 63 0
    The HTTP series (Part 2): Architectural Aspects https://code-maze.com/http-series-part-2/ Mon, 26 Jun 2017 14:55:44 +0000 http://www.code-maze.com/?p=490 first article of the series, we talked about the basic concepts of the HTTP. Now that we have some foundation to build upon, we can talk about some of the architectural aspects of the HTTP. There is more to HTTP than just sending and receiving data. HTTP cannot function by itself as an application protocol. It needs infrastructure in form of a hardware and software solutions that provide different services and make the communication over the World Wide Web possible and efficient. This is the second part of the HTTP Series. In this article, you will learn more about: These are an integral part of our internet life, and you will learn exactly what the purpose of each one of these is, and how it works. This knowledge will help you connect the dots from the first article, and understand the flow of the HTTP communication even better. So let's start.

    Web Servers

    As the first article explained, the primary function of a Web server is to store the resources and to serve them upon receiving requests. You access the Web server using a Web client (aka Web browser) and in return get the requested resource or change the state of existing ones. Web servers can be accessed automatically too, using Web crawlers, that we will talk about later in the article. http servers Some of the most popular Web servers out there and probably the ones you heard of are Apache HTTP Server, Nginx, IIS, Glassfish... Web servers can vary from the very simple and easy to use, to sophisticated and complicated pieces of software. Modern Web servers are capable of performing a lot of different tasks. Basic tasks that Web server should be able to do:
    • Set up connection - accept or close client connection
    • Receive request - read an HTTP request message
    • Process request - interpret the request message and take action
    • Access resource - access the resource specified in the message
    • Construct response - create the HTTP response message
    • Send response - send the response back to the client
    • Log transaction - write about the completed transaction in a log file
    I will break up the basic flow of the Web server in a few different Phases. These phases represent a very simplified version of the Web server flow.

    Phase 1: Setting up connection

    When Web client wants to access the Web server, it must try to open a new TCP connection. On the other side, the server tries to extract the IP address of the client. After that, it is up to the server to decide to open or close the TCP connection to that client. If the server accepts the connection, it adds it to the list of existing connections and watches the data on that connection. It can also close the connection if the client is not authorized or blacklisted (malicious). The server can also try to identify the hostname of the client by using the "reverse DNS". This information can help when logging the messages, but hostname lookups can take a while, slowing the transactions.

    Phase 2: Receiving/Processing requests

    When parsing the incoming requests, Web servers parse the information from the message request line, headers, and body (if provided). One thing to note is that the connection can pause at any time, and in that case, the server must store the information temporarily until it receives the rest of the data. High-end Web servers should be able to open many simultaneous connections. This includes multiple simultaneous connections from the same client. A typical web page can request many different resources from the server.

    Phase 3: Accessing the resource

    Since Web servers are primarily the resource providers, they have multiple ways to map and access the resources. The simplest way is to map the resource is to use the request URI to find the file in the Web server's filesystem. Typically, the resources are contained in a special folder on the server, called docroot. For example, docroot on the Windows server can be located on F:\WebResources\. If a GET request wants to access the file on the /images/codemazeblog.txt, the server translates this to F:\WebResources\images\codemazeblog.txt and returns that file in the response message. When more than one website is hosted on a Web server, each one can have its separate docroot. If a Web server receives a request for a directory instead of a file, it can resolve it in a few ways. It can return an error message, return default index file instead of the directory or scan the directory and return the HTML file with contents. The server may also map the request URI to the dynamic resource - a software application that generates some result. There is a whole class of servers called application servers which purpose is to connect web servers to the complicated software solutions and serve dynamic content.

    Phase 3: Generating and sending the response

    Once the server identified the resource it needs to serve, it forms the response message. The response message contains the status code, response headers, and response body if one was needed. If the body is present in the response, the message usually contains the Content-Length header describing the size of the body and the Content-Type header describing the MIME type of the returned resource. After generating the response, the server chooses the client it needs to send the response to. For the nonpersistent connections, the server needs to close the connection when the entire response message is sent.

    Phase 4: Logging

    When the transaction is complete, the server logs all the transaction information in the file. Many servers provide logging customizations.

    Proxy Servers

    Proxy servers (proxies) are the intermediary servers. They are often found between the Web server and Web client. Due to their nature, proxy servers need to behave both like Web client and Web server. But why do we need Proxy servers? Why don't we just communicate directly between Web clients and Web servers? Isn't that much simpler and faster? Well, simple it may be, but faster, not really. But we will come to that. Before explaining what proxy servers are, we need to get one thing out of the way. That is the concept of reverse proxy or the difference between the forward proxy and reverse proxy. The forward proxy acts as a proxy for the client requesting the resource from a Web server. It protects the client by filtering requests through the firewall or hiding the information about the client. The reverse proxy, on the other hand, works exactly the opposite way. It is usually placed behind the firewall and protects the Web servers. For all the clients know, they talk to the real Web server and remain unaware of the network behind the reverse proxy. Proxy server proxy server Reverse proxy server reverse proxy server Proxies are very useful and their application is pretty wide. Let's go through some of the ways you can use proxy servers.
    • Compression - Compressing the content directly increases the communication speed. Simple as that.
    • Monitoring and filtering - Want to deny access to adult websites to the children in the elementary school? The proxy is the right solution for you :)
    • Security - Proxies can serve as a single entry point to the entire network. They can detect malicious applications and restrict application level protocols.
    • Anonymity - Requests can be modified by the proxy to achieve greater anonymity. It can strip the sensitive information from the request and leave just the important stuff. Although sending less information to the server might degrade the user experience, anonymity is sometimes the more important factor.
    • Access control - Pretty straightforward, you can centralize the access control of the many servers on a single proxy server.
    • Caching - You can use the proxy server to cache the popular content, and thus greatly reduce the loading speeds.
    • Load balancing - If you have a service that gets a lot of "peak traffic" you can use a proxy to distribute the workload on more computing resources or Web servers. Load balancers route traffic to avoid overloading the single server when the peak happens.
    • Transcoding - Changing the contents of the message body can also be the proxy's responsibility
    As you can see, proxies can be very versatile and flexible.

    Caching

    Web caches are devices that automatically make copies of the requested data and save them in the local storage. By doing this, they can:
    • Reduce traffic flow
    • Eliminate network bottlenecks
    • Prevent server overload
    • Reduce the response delay on long distances
    So you can clearly say that Web caches improve both user experience and Web server performance. And of course, potentially save a lot of money. The fraction of the requests served from the cache is called Hit Rate. It can range from 0 to 1, where 0 is 0% and 1 is 100% request served. The ideal goal is of course to achieve 100%, but the real number is usually closer to 40%. Here is how the basic Web cache workflow looks like: CacheFlow

    Gateways, Tunnels, and Relays

    In time, as the HTTP matured, people found many different ways to use it. HTTP became useful as a framework to connect different applications and protocols. Let's see how.

    Gateways

    Gateways refer to pieces of hardware that can enable HTTP to communicate with different protocols and applications by abstracting a way to get a resource. They are also called the protocol converters and are far more complex than routers or switches due to the usage of multiple protocols. You can, for example, use a gateway to get the file over FTP by sending an HTTP request. Or you can receive an encrypted message over SSL and convert it to HTTP (Client-Side Security Accelerator Gateways) or convert HTTP to more secure HTTPs message (Server-Side Security Gateways).

    Tunnels

    Tunnels make use of the CONNECT request method. They enable sending non-HTTP data over HTTP. The CONNECT method asks the tunnel to open a connection to the destination server and to relay the data between client and server. CONNECT request:
    CONNECT api.github.com:443 HTTP/1.0
    User-Agent: Chrome/58.0.3029.110
    Accept: text/html,application/xhtml+xml,application/xml
    
    CONNECT response:
    HTTP/1.0 200 Connection Established
    Proxy-agent: Netscape-Proxy/1.1
    The CONNECT response doesn't need to specify the Content-Type unlike a normal HTTP response would. Once the connection is established, the data can be sent between client and server directly.

    Relays

    Relays are the outlaws of the HTTP world and they don't need to abide by the HTTP laws. They are dumbed-down versions of proxies that relay any information they receive as long as they can establish a connection using the minimal information from the request messages. They sole existence stems from the need to implement a proxy with as little trouble as possible. That can also potentially lead to trouble, but its use is very situational and there is certainly a risk to benefit ratio to consider when implementing relays.

    Web Crawlers

    Web-crawler Also, popularly called spiders, they are bots that crawl over the World Wide Web and index its contents. So, the Web crawler is the essential tool for Search engines and many other websites. The web crawler is a fully automated piece of software and it doesn't need human interaction to work. The complexity of web crawlers can vary greatly and some of the web crawlers are pretty sophisticated pieces of software (like the ones search engines use). Web crawlers consume the resources of the website they are visiting. For this reason, public websites have a mechanism to tell the crawlers which parts of the website to crawl, or to tell them not to crawl anything at all. You can do this by using the robots.txt (robots exclusion standard). Of course, since it is just a standard, robots.txt cannot prevent uninvited web crawlers to crawl the website. Some of the malicious robots include email harvesters, spambots, and malware. Here are a few examples of the robots.txt files:
    User-agent: *
    Disallow: /
    This one tells all the crawlers to stay out.
    User-agent: *
    Disallow: /somefolder/
    Disallow: /notinterestingstuff/
    Disallow: /directory/file.html
    And this one refers only to these two specific directories and a single file.
    User-agent: Googlebot
    Disallow: /private/
    You can disallow a specific crawler, like in this case. But given the vast nature of the World Wide Web, even the most powerful crawlers ever made cannot crawl and index the entirety of it. And that's why they use selection policy to crawl the most relevant parts of it. Also, the WWW changes frequently and dynamically, so the crawlers must use the freshness policy to calculate whether to revisit websites or not. And since crawlers can easily overburden the servers by requesting too much too fast, there is a politeness policy in place. The most of the know crawlers use the intervals of 20 seconds to 3-4 minutes to poll the servers to avoid generating the load on the server. You might have heard the news of the mysterious and evil deep web or dark web. But it's nothing more than the part of the web, that is intentionally not indexed by search engines to hide the information.

    Conclusion

    This wraps it up for this part of the HTTP series. You should now have an even better picture of how the HTTP works, and that there is a lot more to it than requests, responses and status codes. There is a whole infrastructure of different hardware and software pieces that HTTP utilizes to achieve its potential as an application protocol. Every concept I talked about in this article is large enough to cover the whole article or even a book. Our goal was to roughly present you with the different concepts so that you know how it all fits together, and what to look for when needed. If you found some of the explanations a bit short and unclear and you missed my previous articles, be sure to visit part 1 of the series and the HTTP reference where I talk about basic concepts of the HTTP. Thank you for reading and stay tuned for part 3 of the series where I explain different ways servers can use to identify the clients. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
    490 0 0 0 54 0 0 55 54 0 56 https://www.code-maze.com/ 54 0 57 https://www.code-maze.com/ 55 0
    The HTTP series (Part 3): Client Identification https://code-maze.com/http-series-part-3/ Fri, 07 Jul 2017 18:58:14 +0000 https://code-maze.com/?p=542 HTTP Series. In this article, you will learn more about: First, let's see why would websites need to identify you.

    Client Identification and Why It's Extremely Important

    As you are most definitely aware, every website, or at least those that care enough about you and your actions, include some form of content personalization. What do I mean by that? Well, that includes suggested items if you visit e-commerce website, or "the people you might know/want to connect with" on social networks, recommended videos, ads that almost spookily know what you need, news articles that are relevant to you and so on. This effect feels like a double edged sword. On one hand, it's pretty nifty having personalized, custom content delivered to you. On the other hand, it can lead to Confirmation bias that can result in all kinds stereotypes and prejudice. There is an excellent Dilbert comic that touches upon Confirmation bias. Yet, how can we live without knowing how our favorite team scored last night, or what celebrities did last night? Either way, content personalization has become part of our daily lives we can't and we probably don't even want to do anything about it. Let's see how the Web servers can identify you to achieve this effect.

    Different Ways to Identify the Client

    multipass There are several ways that a Web server can identify you:
    • HTTP request headers
    • IP address
    • Long URLs
    • Cookies
    • Login information (authentication)
    Let's go through each one. HTTP authentication is described in more detail in part 4 of the HTTP series.

    HTTP Request Headers Used for Identification

    Web servers have a few ways to extract information about you directly from the HTTP request headers. Those headers are:
    • From - contains user's email address if provided
    • User-Agent - contains the information about Web client
    • Referer - contains the source user came from
    • Authorization - contains username and password
    • Client-ip - contains user's IP address
    • X-Forwarded-For - contains user's IP address (when going through the proxy server)
    • Cookie - contains server-generated ID label
    In theory, the From header would be ideal to uniquely identify the user, but in practice, this header is rarely used due to the security concerns of email collection. The user-agent header contains the information like the browser version, operating system. While this is important for customizing content, it doesn't identify the user in a more relevant way. The Referer header tells the server where the user is coming from. This information is used to improve the understanding of the user behavior, but less so to identify it. While these headers provide some useful information about the client, it is not enough to personalize content in a meaningful way. The remaining headers offer more precise mechanisms of identification.

    IP Address

    The method of client identification by IP address has been used more in the past when IP addresses weren't so easily faked/swapped. Although it can be used as an additional security check, it just isn't reliable enough to be used on its own. Here are some of the reasons why:
    • It describes the machine, not the user
    • NAT firewalls - many ISPs (Internet service providers) use NAT firewalls to enhance security and deal with IP address shortage
    • Dynamic IP addresses - users often get the dynamic IP address from the ISP
    • HTTP proxies and gateways - these can hide the original IP address. Some proxies use Client-ip or X-Forwarded-For to preserve the original IP address

    Long (Fat) URLs

    It is not that uncommon to see websites utilize URLs to improve the user experience. They add more information as the user browses the website until URLs look complicated and illegible. You can see what the long URL looks like by browsing Amazon store.
    https://www.amazon.com/gp/product/1942788002/ref=s9u_psimh_gw_i2?ie=UTF8&fpl=fresh&pd_rd_i=1942788002&pd_rd_r=70BRSEN2K19345MWASF0&pd_rd_w=KpLza&pd_rd_wg=gTIeL&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=&pf_rd_r=RWRKQXA6PBHQG52JTRW2&pf_rd_t=36701&pf_rd_p=1cf9d009-399c-49e1-901a-7b8786e59436&pf_rd_i=desktop
    There are several problems when using this approach.
    • It's ugly
    • Not shareable
    • Breaks caching
    • It's limited to that session
    • Increases the load on the server

    Cookies

    The best client identification method up to date excluding the authentication. Developed by Netscape, but now every browser supports them. There are two types of cookies: session cookies and persistent cookies. A session cookie is deleted upon leaving the browser, and persistent cookies are saved on disk and can last longer. For the session cookie to be treated as the persistent cookie, Max-Age or Expiry property needs to be set. Modern browsers like Chrome and Firefox can keep background processes working when you shut them down so you can resume where you left off. This can result in the preservation of the session cookies, so be careful. So how do the cookies work? Cookies contain a list of name=value pairs that server sets using Set-Cookie or Set-Cookie2 response header. Usually, the information stored in a cookie is some kind of client id, but some websites store other information as well. The browser stores this information in its cookie database and returns it when the user visits the page/website next time. The browser can handle thousands of different cookies and it knows when to serve each one. Here is example flow. 1. User Agent -> Server
    POST /acme/login HTTP/1.1
    [form data]
    User identifies itself via form input 2. Server -> User Agent
    HTTP/1.1 200 OK
    Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"
    The server sends the Set-Cookie2 response header to instruct the User Agent (browser) to set the information about the user in a cookie. 3. User Agent -> Server
    POST /acme/pickitem HTTP/1.1
    Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
    [form data]
    The user selects the item to the shop basket. 4. Server -> User Agent
    HTTP/1.1 200 OK
    Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"
    Shopping basket contains an item. 5. User Agent -> Server
    POST /acme/shipping HTTP/1.1
    Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"; 
            Part_Number="Rocket_Launcher_0001";
    [form data]
    The user selects the shipping method. 6. Server -> User Agent
    HTTP/1.1 200 OK
    Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"
    New cookie reflects shipping method. 7. User Agent -> Server
    POST /acme/process HTTP/1.1
    Cookie: $Version="1";
            Customer="WILE_E_COYOTE"; $Path="/acme";
            Part_Number="Rocket_Launcher_0001"; $Path="/acme";
            Shipping="FedEx"; $Path="/acme"
    [form data]
    That's it. There is one more thing I want you to be aware of. The cookies are not perfect either. Besides security concerns, there is also a problem with cookies colliding with REST architectural style. (The section about misusing cookies). You can learn more about cookies in the RFC 2965.

    Conclusion

    This wraps it up for this part of the HTTP series. You have learned about the strengths of content personalization as well as it's potential pitfalls. You are also aware of the different ways that servers can use to identify you. In part 4 of the series, we will talk about the most important type of client identification: authentication. If you found some of the concepts in this part unclear, refer to the part 1 and part 2 of the HTTP series. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
    542 0 0 0 ]]>
    The HTTP series (Part 4): Authentication Mechanisms https://code-maze.com/http-series-part-4/ Mon, 17 Jul 2017 16:54:59 +0000 https://code-maze.com/?p=594 previous part, we've talked about the different ways that websites can use to identify the visiting user. But identification itself represents just a claim. When you identify yourself, you are claiming that you are someone. But there is no proof of that. Authentication, on the other hand, is showing a proof that you are what you claim to be, like showing your personal id or typing in your password. More often than not, the websites need that proof to serve you sensitive resources. HTTP has its own authentication mechanisms that allow the servers to issue challenges and get the proof they need. In this article, you are going to learn about what they are and how they work. We're also going to cover the pros and cons of each one and find out if they are really good enough to use on their own (spoiler: they are not). This is the fourth part of the HTTP Series. In this article, you will learn more about: Before venturing deeper into the concrete HTTP authentication mechanisms, let's explore what the HTTP authentication is.

    How Does the HTTP Authentication Work?

    Authentication is a way to identify yourself to the Web server. You need to show proof that you have the right to access the requested resources. Usually, this is done by using a combination of username and password (key and secret) which the server validates and then decides if you can access the resource. HTTP offers two authentication protocols:
    • Basic Authentication
    • Digest Authentication
    Before learning more about each one, let's go through some of the basic concepts.

    Challenge/Response Authentication Framework

    What does this mean? It means that when someone sends a request, instead of responding to it immediately, the server sends authentication challenge. It challenges the user to provide the proof of identity by entering the secret information (username and password). After that, the request is repeated using the provided credentials, and if they are correct, the user gets the expected response. In case the credentials are wrong, the server can reissue the challenge or just send the error message.

    Authentication Related request/response headers

    The server issues the challenge by utilizing the WWW-Authenticate response header. It contains the information about the authentication protocol and the security realm. After the client inputs the credentials, the request is sent again. This time with the Authorization header containing the authentication algorithm and the username/password combination. If the credentials are correct, the server returns the response and additional info in an optional Authentication-Info response header.

    Security Realms

    Security realms provide the way to associate different access right to different resource groups on the server. These are called protection spaces. What this means effectively is that depending on the resource you want to access, you might need to enter different credentials. The server can have multiple realms. For example, one would be for website statistics information that only website admins can access. Another would be for website images that other users can access and upload images to. /admin/statistics/financials.txt -> Realm="Admin Statistics" /images/img1.jpg -> Realm = "Images" When you try to access the financials.txt the server will challenge you and the response from would look like this:
    HTTP/1.0 401 Unauthorized
    WWW-Authenticate: Basic realm="Admin Statistics"
    More about security realms: https://tools.ietf.org/html/rfc7235#section-2.2

    Simple HTTP authentication example

    Now let's connect the dots by looking at the simplest HTTP authentication example (Basic authentication, explained below): 1. User Agent -> Server The user requests access to some image on the server.
    GET /gallery/personal/images/image1.jpg HTTP/1.1
    Host: www.somedomain.com
    
    2. Server -> User Agent The server sends the authentication challenge to the user.
    HTTP/1.1 401 Access Denied
    WWW-Authenticate: Basic realm="gallery"
    
    3. User Agent -> Server The user identifies itself via form input.
    GET /gallery/personal/images/image1.jpg HTTP/1.1
    Authorization: Basic Zm9vOmJhcg==
    4. Server -> User Agent The server checks the credentials and sends the 200 OK status code and the image data.
    HTTP/1.1 200 OK
    Content-type: image/jpeg
    ...<image data>
    Not that complicated, right? Now let's drill down and look into basic authentication.

    Basic Authentication

    The most prevalent and supported authentication protocol out there. It has been around since the HTTP/1.0 and every major client implements it. The example above depicts how to authenticate by using Basic authentication. It's rather simple to implement and use, but it has some security flaws. Before going to the security issues, let's see how the Basic authentication deals with username and password. Basic authentication packs the username and password into one string and separates them using the colon (:). After that, it encodes them using the Base64 encoding. Despite what it looks like, the scrambled sequence of characters is not secure and you can decode it easily. The purpose of the Base64 encoding is not to encrypt, but to make the username and password HTTP compatible. The main reason for that is because you can't use international characters in HTTP headers.
    GET /gallery/personal/images/image1.jpg HTTP/1.1
    Authorization: Basic Zm9vOmJhcg==
    The "Zm9vOmJhcg==" from this example is nothing more than Base64 encoded "foo:bar" string. So anyone listening to the requests can easily decode and use the credentials. Even worse than that, encoding the username and password wouldn't help. A malicious third party could still send the scrambled sequence to achieve the same effect. There is also no protection against proxies or any other type of attack that changes the request body and leaves the request headers intact. So, as you can see, Basic authentication is less than perfect authentication mechanism. Still, despite that, you can use it to prevent accidental access to protected resources and to it offers a degree of personalization. To make it more secure and usable, Basic authentication can be implemented by using HTTPS over SSL which we talk about in part 5 of the series. Some would argue it's only as secure as your transport mechanism.

    Digest Authentication

    Digest authentication is a more secure and reliable alternative to simple but insecure Basic authentication. So, how does it work? Digest authentication uses MD5 cryptographic hashing combined with the usage of nonces. That way it hides the password information to prevent different kinds of malicious attacks. This might sound a bit complicated, but it will get clearer when you see how it works on a simple example.

    Example

    1. User Agent -> Server
    GET /dir/index.html HTTP/1.0
    Host: localhost
    The client sends an unauthenticated request. 2. Server -> User Agent
    HTTP/1.0 401 Unauthorized
    WWW-Authenticate: Digest realm="shire@middleearth.com",
                            qop="auth,auth-int",
                            nonce="cmFuZG9tbHlnZW5lcmF0ZWRub25jZQ",
                            opaque="c29tZXJhbmRvbW9wYXF1ZXN0cmluZw"
    Content-Type: text/html
    Content-Length: 153
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <title>Error</title>
      </head>
      <body>
        <h1>401 Unauthorized.</h1>
      </body>
    </html>
    The server challenges the client to authenticate using the Digest authentication and sends the required information to the client. 3. User Agent -> Server
    GET /dir/index.html HTTP/1.0
    Host: localhost
    Authorization: Digest username="Gandalf",
                         realm="shire@middleearth.com",
                         nonce="cmFuZG9tbHlnZW5lcmF0ZWRub25jZQ",
                         uri="/dir/index.html",
                         qop=auth,
                         nc=00000001,
                         cnonce="0a4f113b",
                         response="5a1c3bb349cf6986abf985257d968d86",
                         opaque="c29tZXJhbmRvbW9wYXF1ZXN0cmluZw"
    The client calculates the response value and sends it together with username, realm, URI, nonce, opaque, qop, nc and cnonce. A lot of stuff.

    Detailed Explanation

    Let's define these:
    • nonce and opaque - the server defined strings that client returns upon recieving them
    • qop (quality of protection) - one or more of the predefined values ("auth" | "auth-int" | token). These values affect the computation of the digest.
    • cnonce - client nonce, must be generated if qop is set. It is used to avoid chosen plaintext attacks and to provide message integrity protection.
    • nc - nonce count, must be sent if qop is set.  This directive allows the server to detect request replays by maintaining its own copy of this count - if the same nc value appears twice, then the request is a replay.
    The response attribute is calculated in the following way:
    HA1 = MD5("Gandalf:shire@middleearth.com:Lord Of The Rings")
           = 681028410e804a5b60f69e894701d4b4
    
    HA2 = MD5("GET:/dir/index.html")
           = 39aff3a2bab6126f332b942af96d3366
    
    Response = MD5( "681028410e804a5b60f69e894701d4b4:
                     cmFuZG9tbHlnZW5lcmF0ZWRub25jZQ:
                     00000001:0a4f113b:auth:
                     39aff3a2bab6126f332b942af96d3366" )
             = 5a1c3bb349cf6986abf985257d968d86
    If you are interested in learning how to compute the response depending on qop, you can find it in the RFC 2617. 4. Server -> User Agent
    HTTP/1.0 200 OK
    Content-Type: text/html
    Content-Length: 2345
    ... <content data>
    The server computes the hash on its own and compares the two. If they match it serves the client with the requested data.

    Short Summary

    As you can see the Digest authentication is more complicated to understand and implement. It is also more secure than Basic authentication, but still vulnerable to man-in-the-middle attack. RFC 2617 recommends that Digest authentication is used instead of the Basic authentication since it remedies some of its weaknesses. It also doesn't hide the fact that Digest authentication is still weak by modern cryptographic standards. Its strength largely depends on the implementation. So in summary digest authentication:
    • Does not send plain text passwords over the network
    • Prevents replay attacks
    • Guards against message tampering
    Some of the weaknesses:
    • Vulnerability to the man-in-the-middle attack
    • Many of the security options are not required and thus make Digest authentication function in a less secure manner if not set
    • Prevents the use of strong password hashing algorithms when storing passwords
    Due to these facts, the Digest authentication still hasn't gained major traction. The Basic authentication is much simpler and combined with SSL still more secure than the Digest authentication.

    Conclusion

    This's it for this part of the HTTP series. We've gone through different authentication mechanisms that HTTP offers by default and talked about their advantages and disadvantages. These concepts are hopefully not just the letters on the screen anymore, and the next time you hear about them you will know precisely what they are and when to apply them. You are also aware that there are security risks that haven't been solved by these authentication mechanisms. That's why the concepts like HTTPS and SSL/TLS exist. We talk more about security risks and how to solve them in the next part of the series. If you found some of the concepts in this part unclear, refer to the part 1, part 2, and part 3 of the HTTP series. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
    594 0 0 0
    The HTTP series (Part 5): Security https://code-maze.com/http-series-part-5/ Mon, 31 Jul 2017 17:09:32 +0000 https://code-maze.com/?p=661 HTTP series, you are ready now to embark on a journey of HTTP security. And a journey it will be, I promise :) Many companies have been a victim to security breaches. To name just a few prominent ones: Dropbox, Linkedin, MySpace, Adobe, Sony, Forbes and many others were on the receiving end of malicious attacks. Many accounts were compromised and the chances are, at least one of those was yours :) You can actually check that on Have I Been Pwned. My email address was found on 4 different websites that were victims of a security breach. There are many aspects of the Web application security, too much to cover in one article, but let's start right from the beginning. Let's learn how to secure our HTTP communication first. This is the fifth part of the HTTP Series. In this article, you will learn more about: There is a lot to cover, so let's go right into it.

    Do You Really Need HTTPS?

    You might be thinking: "Surely not all websites need to be protected and secured". If a website doesn't serve sensitive data or doesn't have any form submissions, it would be overkill to buy certificates and slow the website down, just to get the little green mark at the URL bar that says "Secured". If you own a website, you know it is crucial that it loads as fast as possible, so you try not to burden it with unnecessary stuff. Why would you willingly go through the painful process of migration to the HTTPS just to secure the website that doesn't need to be protected in the first place? And on top of that, you even need to pay for that privilege. Let's see if it's worth the trouble.

    HTTPS Encrypts Your Messages and Solves the MITM Problem

    In the previous part of the HTTP series I talked about different HTTP authentication mechanisms and their security flaws. The problem that both Basic and Digest authentication cannot solve is the Man in the middle attack. Man in the middle represents any malicious party that inserts itself between you and the website you are communicating with. Its goal is to intercept the original messages both ways and hide its presence by forwarding the modified messages. MITM Original participants of the communication might not be aware that their messages are being listened to. HTTPS solves the MITM attacks problem by encrypting the communication. Now, that doesn't mean that your traffic cannot be listened to anymore. It does mean that anyone that listens and intercepts your messages, won't be able to see its content. To decrypt the message you need the key. We will learn how that works exactly a bit later on. Let's move on.

    HTTPS as a Ranking Signal

    Not that recently, Google made HTTPS a ranking signal. What does that mean? It means that if you are a webmaster, and you care about your Google ranking, you should definitely implement the HTTPS on your website. Although it's not as potent as some others signals like quality content and backlinks, it definitely counts. By doing this, Google gives incentive to webmasters to move to HTTPS as soon as possible and improve the overall security of the internet.

    It's Completely Free

    To enable HTTPS (SSL/TLS) for a website you need a certificate issued by a Certificate Authority. Until recently, certificates were costly and had to be renewed every year. Thanks to the folks at Let's encrypt you can get very affordable certificates now ($0!). Seriously, they are completely free. Let's encrypt certificates are easily installed, have a major companies support and a great community of people. Take a look at the Major sponsors and see for yourself the list of companies that support them. You might recognize a few :) Let's encrypt issues DV (domain validation) certificates only and have no plan of issuing organizational (OV) or extended validation (EV) certificates. The certificate lasts for 90 days and is easily renewed. Like any other great technology, it has a down side too. Since certificates are easily available now, they are being abused by Phishing websites.

    It's All About the Speed

    Many people associate HTTPS with additional speed overhead. Take a quick look at the httpvshttps.com. Here are my results for the HTTP and HTTPS: http vs https results So what happened there? Why is HTTPS so much faster? How is that possible? HTTPS is the requirement for using the HTTP 2.0 protocol. If we look at the network tab, we will see that in the HTTPS case, the images were loaded over h2 protocol. And the waterfall looks very different too. The HTTP 2.0 is the successor of the currently prevalent HTTP/1.1. It has many advantages over HTTP/1.1:
    • It's binary, instead of textual
    • It's fully multiplexed, which means it can send multiple requests in parallel over a single TCP connection
    • Reduces overhead by using HPACK compression
    • It uses the new ALPN extension which allows for faster-encrypted connections
    • It reduces additional round trip times (RTT), making your website load faster
    • Many others

    You Will Be Frowned Upon by Browsers

    If you are not convinced by now, you should probably know, that some browsers started waging war against unencrypted content. Google has published a blog last September that clearly explains how will Chrome treat insecure websites. Here is how it looked before and after Chrome version 56. And here is how it will look eventually. Are you convinced yet? :D

    Moving to HTTPS Is Complicated

    This is also the relic of the past times. While moving to HTTPS might be harder for the websites that exist for a long time because of the sheer amount of resources uploaded to over HTTP, the hosting providers are generally trying to make this process easier. Many hosting providers offer automatic migration to HTTPS. It can be as easy as clicking one button in the options panel. If you plan to setup your website over HTTPS, check if the hosting provider offers HTTPS migration first. Or if it has shell access, so you can do it yourself easily with let's encrypt and a bit of server configuration. So, these are the reasons to move to HTTPS. Any reason not to? Hopefully, by now, I convinced you of the HTTPS value and you passionately want to move your website to HTTPS and understand how it works.

    HTTPS Fundamental Concepts

    HTTPS stands for Hypertext Transfer Protocol Secure. This effectively means that client and server communicate through HTTP but over the secure SSL/TLS connection. In the previous parts of the series, we've learned how HTTP communication works, but what does the SSL/TLS part stand for and why do I use both SSL and TLS? Let's start with that.

    SSL vs TLS

    Terms SSL (Secure Socket Layer) and TLS (Transport Layer Security) are used interchangeably, but in fact, today, when someone mentions SSL they probably mean TLS. SSL was originally developed by Netscape but version 1.0 never saw the light of the day. Version 2.0 was introduced in 1995 and version 3.0 in 1996, and they were used for a long time after that (at least what is considered long in IT), even though their successor TLS already started taking traction. Up until 2014. fallback from TLS to SSL was supported by servers, and that was the main reason the POODLE attack was so successful. After that, the fallback to SSL was completely disabled. If you check yours or any other website with Qualys SSL Labs tool, you will probably see something like this: ssl test As you can see, SSL 2 and 3 are not supported at all, and TLS 1.3 hasn't still taken off. But, because SSL was so prevalent for so long, it became a term that most people are familiar with and now it's used for pretty much anything. So when you hear someone using SSL instead of TLS it is just for historical reasons, not because they really mean SSL. Now that we got that out of the way, I will use TLS from now on since it's more appropriate. So, how do client and server establish a secure connection?

    TLS Handshake

    Before the real, encrypted communication between the client and server starts, they perform what is called the "TLS handshake". Here is how TLS handshake works (very simplified, additional links below). TLS handshake The encrypted communication starts after the connection is established. The actual mechanism is much complicated than this, but to implement the HTTPS, you don't need to know all the actual details of the TLS handshake implementation. What you need to know is that there is an initial handshake between the client and the server, in which they exchange keys and certificates. After that handshake, encrypted communication is ready to start. If you want to know exactly how TLS Handshake works, you can look it up in the RFC 2246. In the TLS handshake image, certificates are being sent, so let's see what a certificate represents and how it's being issued.

    Certificate and Certification Authorities (CAs)

    Certificates are the crucial part of the secure communication over HTTPS. They are issued by one of the trusted Certification Authorities. A digital certificate allows the users of the website to communicate in the secure fashion when using a website. For example, the certificate you are using when browsing through my blog looks like this: Code Maze Certificate If you are using Chrome, for example, you can inspect certificates yourself by going to the Security tab in Developer Tools (F12). I would like to point out two things. In the first red box, you can see what the real purpose of the certificate is. It just ensures that you are talking to the right website. If someone was to for example impersonate the website you think you are communicating with, you would certainly get notified by your browser. That would not prevent malicious attackers to steal your credentials if they have a legitimate domain with a legitimate certificate. So be careful. Green "Secure" in the top left just means that you are communicating with the right website. It doesn't say anything about the honesty of that website's owner :) Extended validation certificates, on the other hand, prove that the legal entity is controlling the website. There is an ongoing debate whether EV certs are all that useful to the typical user of the internet. You can recognize them by the custom text left of your URL bar. For example, when you browse twitter.com you can see: Twitter EV certificate That means they are using EV certificate to prove that their company stands behind that website.

    Certificate Chains

    So why would your browser trust the certificate that server sends back? Any server can send a piece of digital documentation and claim it is what you want it to be. That's where root certificates come in. Typically certificates are chained and the root certificate is one your machine implicitly trusts. For my blog it looks like this: certificate chain Lowest one is my domain certificate, which is signed by the certificate above it and so on... Until the root certificate is reached. But who signs the root certificate you might wonder? Well, it signs itself :D Issued to: AddTrust External CA Root, Issued by: AddTrust External CA Root. And your machine and your browsers have a list of trusted root certificates which they rely upon to ensure the domain you are browsing is trusted. If the certificate chain is broken for some reason (happened to me because I enabled CDN for my blog), your site will be displayed as insecure on some machines. You can check the list of trusted root certificates on Windows by running the certificate manager by pressing windows button + R and typing certmgr.msc. You can then find machine trusted certificates in the  Trusted Root Certification Authorities section. This list is used by Windows, IE, and Chrome. Firefox, on the other hand, manages its own list. By exchanging certificate, client and server know that they are talking to the right party and can begin encrypted message transfer.

    HTTPS Weaknesses

    HTTPS can provide a false sense of security if site backend is not properly implemented. There are a plethora of different ways to extract customer information, and many sites are leaking data even if they are using HTTPS. There are many other mechanisms besides MITM to get sensitive information from the website. Another potential problem that the websites can have HTTP links even though they run over HTTPS. This can be a chance for MITM attack. While migrating websites to HTTPS, this might get by unnoticed. And here is another one as a bonus: login forms accessed through an insecure page could potentially be at risk even though they are loaded on a secure page. So it's best to keep entire website secure to avoid this one.

    Conclusion

    That wraps up entire HTTP series. I hope you got something useful out of it and understood some concepts you didn't or couldn't before. I know I had fun writing it and learned a bunch of new things in the process. Hopefully, it was as much fun reading (or at least close). :) [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
    661 0 0 0 72 0 0 73 https://www.code-maze.com/ 72 0 74 73 0 75 74 0 76 https://www.code-maze.com/ 75 0
    ASP.NET Core Web API - Creating MySQL Database https://code-maze.com/net-core-web-development-part1/ Mon, 08 Jan 2018 06:36:00 +0000 https://code-maze.com/?p=943 If you wonder is it hard to manipulate the MySql database by using Workbench, well let me tell you that it is not that hard at all but quite opposite. Creating schemas, tables, relations and populating those tables is very easy, once you are familiar with the MySql Workbench tool. By following instructions from this post, you will master skills for creating a database, creating and populating tables, creating relations between those tables and having the backup of your data. Later on, we will use this database to connect .NET Core project to it. [sc name="part_of_series" headline="This article is part of the series"] If you want to see all the basic instructions and complete navigation for this tutorial, please click on the following link: Introduction page for this tutorial. You can download all the files for this part from here:  .NET Core, Angular and MySQL. Part 1 - Source Code This post is divided into several parts:

    MySQL, Creating Schema Model Diagram

    After the MySQL server has been installed (we are using 5.7.17 version for this series), we will start the MySQL Workbench, which will be our tool for creating tables. When you open the Workbench, click on the existing connection, add a password and you will be redirected to the database view. Go back on a home screen and click on the Models icon. mysql_models_menu By clicking on the plus button, next to the Models text, we will enter into the model view which will allow us to create our new schema. mysql-models-view Click twice on the Add Diagram button and you'll see the screen for creating a schema. Here we will create our tables and the relationships between them. mysql-schema-view  

    Creating Tables in Schema Model Diagram

    We will create two tables. Click on the icon for the table and then click again on the work ground. Repeat this step one more time to create another table. After, click twice on the first table so we could create columns and give a name to the table. mysql-create-table Name the first table Owner and add the columns: first-table-created Double click on the second database table, name it Account and add the columns: second-table-created Just want to explain why the value Char (36) for the Primary Keys in our tables. It is because we will use GUID’s for our keys, and representation of a GUID in MySQL is the Char (36).

    Adding Table Relations

    You can save your model by pressing CTRL+S and after that, we will add the relation to our tables. We will assume that one owner can have multiple accounts (Domestic, Foreign currency, Savings…), therefore we are seeing relation one-to-many between owner and account. Taking this knowledge into consideration, we want to connect two tables by making the OwnerId foreign key in the Account table, so it could provide us with the relationship between those two tables. Click on the 1:N relation, one marked in the above picture, then first click on the Account table and then click on the Owner table. Right after that action, you will see the new column inside the Account table. That is our foreign key. Double click on that new column to change its name. Name it OwnerId. Now if you look all the way down, you will see several tabs. We are currently in the Columns tab. If you go right and click on the foreign keys tab you will see information about our foreign key in the Account table. mysql-foreign-key-settings Let's change the default settings for the OnUpdate and OnDelete. For the OnUpdate, we will choose the Cascade option. That means if a row with a primary key, in the Owner table, is updated automatically, update a row with the corresponding foreign key in the Account table. Also, for the OnDelete, we will choose the Restrict option. That's because we don’t want to allow deleting a row with a primary key, from the Owner table, without previously removing the row with the corresponding foreign key. That way we are preserving the referential integrity of our database. If you look in the Catalog Tree, which is the part of the schema view, you will see "mydb" as the database name. We don’t want to call our database like that, so to change it, right-click on mydb and choose edit schema. Give it the name AccountOwner and just save your model.

    Exporting Schema to the Script File

    We are finished with the create schema actions. Now, we want to export our database schema to a script file, which will provide us with a code to create our database with the tables, by simple executing our generated script. Click on the File menu, then hover over the Export and choose the Forward Engineering SQL Create Script. You will see a new window, in which you have to add the name of the script file and the other options for the generating scripts. In the field “Output SQL Script File”, write the name of the script, if left blank, you will just view generated script and be able to copy it but not save it. Also, click on the “Generate DROP Statements Before Each CREATE Statement” option. This option will drop any existing table, prior to the creation of a new one with the same name. This way you’ll avoid errors if any table already exists inside the database while starting the script file. mysql-generate-sql-script Click the Next button. You will see the SQL Object Explorer Filter and in that window just check the first option: Export MySQL Table Objects. Click the Next again. Here you will notice the way your script looks like. Just click the finish and the script will be exported to your desired location.

    Creating the Database from the Script File

    If your database view is still opened, redirect yourself on that view. If it is not open, click on the home tab, then click the first icon from the top (one with Dolphin on it) and then click on the Local Instance part to enter the database view.   mysql-database-view In the database view, choose the Schemas tab in the Navigator part of the page. There you will find some default databases created and in that section, your new database will appear. Go to the File menu and choose the option Run SQL Script. Select the saved script and then just click the run button. This will create a database. If you refresh your schemas view, you will be able to see our database. complete-database-view Because the account table depends on the owner table, we need to populate the owner table first and account table next. Right-click on the owner table, choose the first option: Select Rows – Limit …, and you are going to see the query window. owner-table-selected

    Populating Tables With the Data

    Now we want to populate our tables, and for that open a new SQL tab for executing the queries. new-sql-tab And execute the code:
    INSERT INTO `accountowner`.`owner` 
    VALUES ('24fd81f8-d58a-4bcc-9f35-dc6cd5641906','John Keen','1980-12-05','61 Wellfield Road'), 
    ('261e1685-cf26-494c-b17c-3546e65f5620','Anna Bosh','1974-11-14','27 Colored Row'),
    ('a3c1880c-674c-4d18-8f91-5d3608a2c937','Sam Query','1990-04-22','91 Western Roads'),
    ('f98e4d74-0f68-4aac-89fd-047f1aaca6b6','Martin Miller','1983-05-21','3 Edgar Buildings');
    INSERT INTO `accountowner`.`account` 
    VALUES ('03e91478-5608-4132-a753-d494dafce00b','2003-12-15','Domestic','f98e4d74-0f68-4aac-89fd-047f1aaca6b6'),
    ('356a5a9b-64bf-4de0-bc84-5395a1fdc9c4','1996-02-15','Domestic','261e1685-cf26-494c-b17c-3546e65f5620'), 
    ('371b93f2-f8c5-4a32-894a-fc672741aa5b','1999-05-04','Domestic','24fd81f8-d58a-4bcc-9f35-dc6cd5641906'), 
    ('670775db-ecc0-4b90-a9ab-37cd0d8e2801','1999-12-21','Savings','24fd81f8-d58a-4bcc-9f35-dc6cd5641906'), 
    ('a3fbad0b-7f48-4feb-8ac0-6d3bbc997bfc','2010-05-28','Domestic','a3c1880c-674c-4d18-8f91-5d3608a2c937'), 
    ('aa15f658-04bb-4f73-82af-82db49d0fbef','1999-05-12','Foreign','24fd81f8-d58a-4bcc-9f35-dc6cd5641906'), 
    ('c6066eb0-53ca-43e1-97aa-3c2169eec659','1996-02-16','Foreign','261e1685-cf26-494c-b17c-3546e65f5620'), 
    ('eccadf79-85fe-402f-893c-32d3f03ed9b1','2010-06-20','Foreign','a3c1880c-674c-4d18-8f91-5d3608a2c937');
    By executing the code above, we will have our tables populated with the required data.
    If you want to back up your data, all you have to do is the following: Click on the Server menu, choose the Data Export, select your database and below check Export to Self-Contained File. Choose the destination for your backup file and click Start Export.

    Conclusion

    That is all for Part 1 of this tutorial. We have learned how to create MySQL schema, how to create tables and table relationships. Furthermore, we now know how to create database scripts and to insert data into our tables. Even though we covered the basics of MySQL, you have enough knowledge to start creating your own relational database schemas.  Thank you for a reading and check out the next part, where we start diving into the .NET CORE world. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
    943 0 0 0 01-mysql_models_menu]]> 85 http://www.chilas.co 0 0 86 85 0 87 http://www.chilas.co 86 0 112 0 0 113 112 0 1171 0 0 1172 1171 0 1174 http://anit.me 0 0 1175 1174 0 1633 0 0 1634 1633 0 1635 1633 0 1641 1634 0 1642 1641 0
    ASP.NET Core Web API - .NET Core Service Configuration https://code-maze.com/net-core-web-development-part2/ Mon, 15 Jan 2018 06:20:25 +0000 https://code-maze.com/?p=1053
  • Creating a Database for Our Project
  • Basic Code Preparations (Current article)
  • Custom Logging in ASP.NET Core
  • Repository Pattern with Entity Framework Core
  • Using Repository for GET Requests
  • Using Repository for POST, PUT and DELETE Requests
  • [sc name="part_of_series" headline="This article is part of the series"] If you want to see all the basic instructions and complete navigation for this series, please click on the following link: Introduction page for this tutorial. For the previous part click on this link: MySQL, creating database data This post is divided into several parts:

    Creating a New Project and Modifying LaunchSettings.json File

    After we have finished creating and populating the database, in the previous post, we are going to create a Visual Studio project for our server part of the application. Open Visual Studio 2019 and create a new ASP.NET Core Web Application with the name AccountOwnerServer: Configure project - .net core configuration In the next window, we are going to choose Web API and from the left drop-down list .NET Core. Also, from the right drop-down, we are going to choose ASP.NET Core 3.1. Finally, let’s click the Create button and the project will load: C:\Users\a\Desktop\Project type - .net core configuration After the project has been created, we are going to modify the launchSettings.json file which is quite an important file for the .NET Core configuration. Let’s change the applicationUrl property and the launchBrowser property to false, to prevent web browser to start when the project starts. In the Solution Explorer expand the Properties and double click on the launchSettings.json file. Let's modify that file:
    {
      "$schema": "http://json.schemastore.org/launchsettings.json",
      "iisSettings": {
        "windowsAuthentication": false, 
        "anonymousAuthentication": true, 
        "iisExpress": {
          "applicationUrl": "http://localhost:5000",
          "sslPort": 0
        }
      },
      "profiles": {
        "IIS Express": {
          "commandName": "IISExpress",
          "launchBrowser": false,
          "environmentVariables": {
            "ASPNETCORE_ENVIRONMENT": "Development"
          }
        },
        "AccountOwnerServer": {
          "commandName": "Project",
          "launchBrowser": false,
          "applicationUrl": "http://localhost:5000",
          "environmentVariables": {
            "ASPNETCORE_ENVIRONMENT": "Development"
          }
        }
      }
    }

    Program.cs and Startup.cs Explanations

    In the Program.cs we can find this piece of code:
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
    
    This code looks very similar to the version from ASP.NET Core 2.2 at first glance, but actually, many things changed. In version 3.0 ASP.NET Core has been rebuilt to use a Generic Host, instead of using a separate Web Host. So basically, the CreateHostBuilder method has been broken into two logical parts:
    • CreateDefaultBuilder(args) – ASP.NET Core uses this to configure the app configuration, logging, and dependency injection container
    • ConfigureWebHostDefaults – This adds everything else required by a typical ASP.NET Core application (kestrel configuration, and using the Startup class, middleware pipeline…)
    We can also notice this part of the code: UseStartup<Startup>(). The Startup class is mandatory for the .NET Core, in which we configure embedded or custom services that our application needs. When we open the Startup class, we can see the constructor and the two methods, which content we are going to change during the application development. In the method ConfigureServices, we will do exactly that, configure our services. Furthermore, in the Configure method, we are going to add different middleware components to the application’s request pipeline. All of our configuration code could be written inside the ConfigureServices method, but large applications could potentially contain many services. As a result, it could make this method unreadable and hard to maintain. Therefore we will create extension methods for each configuration and place the configuration code inside those methods.

    Extension Methods and CORS Configuration

    An extension method is inherently the static method. They play a great role in .NET Core configuration because they increase the readability of our code for sure. What makes it different from the other static methods is that it accepts "this" as the first parameter, and "this" represents the data type of the object which uses that extension method. An extension method must be inside a static class. This kind of method extends the behavior of the types in .NET. So, let’s start writing some code. Let's create a new folder Extensions in the main project and a new class inside with the name ServiceExtensions. We are going to make that class static and it will consist of our service extension methods:
    namespace AccountOwnerServer.Extensions
    {
        public static class ServiceExtensions
        {
        }
    }
    First, we need to configure CORS in our application. CORS (Cross-Origin Resource Sharing) is a mechanism that gives rights to the user to access resources from the server on a different domain. Because we will use Angular as a client-side on a different domain than the server’s domain, configuring CORS is mandatory. So, let's add this code to the ServiceExtensions class:
    public static void ConfigureCors(this IServiceCollection services)
    {
          services.AddCors(options =>
          {
              options.AddPolicy("CorsPolicy",
                  builder => builder.AllowAnyOrigin()
                  .AllowAnyMethod()
                  .AllowAnyHeader());
          });
    }
    
    We are using the basic settings for adding CORS policy because for this project allowing any origin, method, and header is quite enough. But we can be more restrictive with those settings if we want. Instead of the AllowAnyOrigin() method which allows requests from any source, we could use the WithOrigins("http://www.something.com") which will allow requests just from the specified source. Also, instead of AllowAnyMethod() that allows all HTTP methods,  we can use WithMethods("POST", "GET") that will allow only specified HTTP methods. Furthermore, we can make the same changes for the AllowAnyHeader() method by using, for example, the WithHeaders("accept", "content-type") method to allow only specified headers.

    IIS Configuration as Part of .NET Core Configuration

    Additionally, we need to configure an IIS integration which will help us with the IIS deployment. Add the following code to the ServiceExtensions class:
    public static void ConfigureIISIntegration(this IServiceCollection services)
    {
          services.Configure<IISOptions>(options => 
          {
    
          });          
    }
    We do not initialize any of the properties inside the options because we are just fine with the default values. For more pieces of information about those properties, look at the explanation: iis-integration-options .net core configuration In the Startup class we are going to change the ConfigureServices and Configure methods:
    public void ConfigureServices(IServiceCollection services)
    {
        services.ConfigureCors();
        services.ConfigureIISIntegration();
    
        services.AddControllers();
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseHttpsRedirection();
        app.UseStaticFiles();
    
        app.UseCors("CorsPolicy");
    
        app.UseForwardedHeaders(new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.All
        });
    
        app.UseRouting();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    
    In the ConfigureServices method, CORS, and IIS configuration have been added. Furthermore, the CORS configuration has been added to the application’s pipeline inside the Configuration method. But as you may notice, there is a little more code. Therefore, we are going to explain it now.
    • app.UseForwardedHeaders will forward proxy headers to the current request. This will help us during the Linux deployment.
    • app.UseStaticFiles() enables using static files for the request. If we don’t set a path to the static files, it will use a wwwroot folder in our solution explorer by default.

    Conclusion

    Concerning the .NET Core configuration, we could say that this is quite enough. Now you know how to modify the launchSetting.json file, the purpose of the Program and the Startup classes, how to configure services and finally how to create and use extension methods. Thank you for reading and check out the next part in which we will create our first service, logger service, and use it for logging our messages. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
    1053 0 0 0 97 0 0 99 97 0 102 99 0 103 102 0 119 0 0 120 119 0 121 120 0 122 121 0 684 0 0 836 https://brandwebdirect.com/website-design-portfolio-samples/website-design-for-ecommerce-online-sales-web-development-service 0 0 991 99 0 992 991 0 1012 992 0 1173 0 0 1480 0 0 1481 var connectionString = config["mysqlconnection:connectionString"]; services.AddDbContext(o => o.UseMySql(connectionString)); As you can see, I am using o.UseMySql method here.]]> 1480 0 1482 1481 0 1483 1482 0 1484 1483 0 1485 1484 0 1486 1485 0 1487 1486 0 1488 0 0 1551 0 0 1554 1551 0 1555 1554 0 1556 1555 0 1567 0 0 1568 1567 0
    ASP.NET Core Web API - Logging With NLog https://code-maze.com/net-core-web-development-part3/ Tue, 23 Jan 2018 08:02:31 +0000 https://code-maze.com/?p=1164 Why is logging so important during the application development? Well, while your application is in the developing stage it is very easy to debug the code and to find out what went wrong. But, can you debug in the production environment? Of course not.  That is why logging messages are a great way to find out what went wrong and where did errors happen in your code in the production environment. .NET Core has its own implementation of logging messages, but in all my projects, I prefer to create my own custom logger service.  This is what we are going to show you in this post. [sc name="part_of_series" headline="This article is part of the series"] If you want to see all the basic instructions and complete navigation for this series, please follow the following link: Introduction page for this tutorial. For the previous part check out: Creating .NET Core WebApi project - Basic code preparations Source code to download is available at this link: .NET Core, Angular 4 and MySQL. Part 3 - Source Code This post is divided into several sections:

    Creating Required Projects

    Let's create two new projects. Name the first one Contracts. We are going to store interfaces inside this project. Name the second one LoggerService. We are going to use it for the logger logic. To create a new project, right-click on the solution window, choose Add and then NewProject. We are going to choose the Class Library (.NET Core) and name it Contracts: create-new-project-contracts .net core logging Do the same thing for the second project, just name it LoggerService. With these two projects in place, we need to reference the LoggerService project to the main project. In the main project inside solution explorer, right-click on the Dependences and choose the AddReference. Under the Projects click the Solution and check this project: Before we proceed to the implementation of the LoggerService, let's do one more thing. In the LoggerService project right click on the Dependencies and then click on the Add Reference. Inside check the Contracts checkbox to import its reference inside the LoggerService project. This would automatically add the Contracts reference to the main project because we already have a LoggerService referenced inside.

    Creating the Interface and Installing NLog

    Our logger service will contain four methods for logging:
    • Info messages
    • Debug messages
    • Warning messages
    • And error messages
    Additionally, we are going to create the interface ILoggerManager inside the Contracts project containing those four method definitions. So, let’s create the ILoggerManager interface and modify it:
    namespace Contracts
    {
        public interface ILoggerManager
        {
            void LogInfo(string message);
            void LogWarn(string message);
            void LogDebug(string message);
            void LogError(string message);
        }
    }
    
    Before we implement this interface inside the LoggerService project, we need to install the NLog library in our LoggerService project. NLog is a logging platform for the .NET which will help us create and log our massages. We are going to show you two ways to add the NLog library to our project.
    1. In the LoggerService project, right-click on the Dependences and choose Manage NuGet Packages. After the NuGet window appears, just follow the steps:
    NLog-nuget .net core logging
    1. From the View menu choose the Other Windows and then click on the Package Manager Console. After the console appears just follow the leads:
    Install-Package NLog.Extensions.Logging -Version 1.6.1 After a couple of seconds, NLog is up and running in your app.

    Implementing Interface and Nlog.Config File

    In the LoggerService project create the new class LoggerManager. Let's modify the LoggerManager class:
    using Contracts;
    using NLog;
    
    namespace LoggerService
    {
        public class LoggerManager : ILoggerManager
        {
            private static ILogger logger = LogManager.GetCurrentClassLogger();
    
            public void LogDebug(string message)
            {
                logger.Debug(message);
            }
    
            public void LogError(string message)
            {
                logger.Error(message);
            }
    
            public void LogInfo(string message)
            {
                logger.Info(message);
            }
    
            public void LogWarn(string message)
            {
                logger.Warn(message);
            }
        }
    }
    Now, we need to configure it and inject it into the Startup class in the ConfigureServices method. NLog needs to have information about the folder to create log files in, what the name of these files will be and what a minimum level of logging is. Therefore, we need to create one text file in the main project with the name nlog.config and to populate it as in the example below. You need to change the path of the internal log and filename parameters to your paths.
    <?xml version="1.0" encoding="utf-8" ?>
    <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          autoReload="true"
          internalLogLevel="Trace"
          internalLogFile="d:Projects\Blog-AccountOwner\Project\internal_logs\internallog.txt">
    
      <targets>
        <target name="logfile" xsi:type="File"
                fileName="d:/Projects/Blog-AccountOwner/Project/logs/${shortdate}_logfile.txt"
                layout="${longdate} ${level:uppercase=true} ${message}"/>
      </targets>
    
      <rules>
        <logger name="*" minlevel="Debug" writeTo="logfile" />
      </rules>
    </nlog>
    

    Configuring Logger Service for Logging Messages

    Setting up the configuration for a logger service is quite easy. We have to modify the constructor of the Startup class:
    public Startup(IConfiguration configuration)
    {
        LogManager.LoadConfiguration(String.Concat(Directory.GetCurrentDirectory(), "/nlog.config"));
        Configuration = configuration;
    }
    Secondly, we need to add the logger service inside the .NET Core’s IOC container. There are three ways to do that:
    • By calling services.AddSingleton will create the service the first time you request it and then every subsequent request is calling the same instance of the service. This means that all components are sharing the same service every time they need it. You are using the same instance always
    • By calling services.AddScoped will create the service once per request. That means whenever we send the HTTP request towards the application, a new instance of the service is created
    • By calling services.AddTransient will create the service each time the application request it. This means that if during one request towards our application, multiple components need the service, this service will be created again for every single component which needs it
    So, in the ServiceExtensions class we are going to add a new method:
    public static void ConfigureLoggerService(this IServiceCollection services)
    {
        services.AddSingleton<ILoggerManager, LoggerManager>();
    }
    
    Lastly, in the ConfigureServices method invoke this extension method, right above services.AddControllers():
    services.ConfigureLoggerService();
    Every time we want to use a logger service, all we need to do is to inject it into the constructor of the class that is going to use that service. .NET Core will serve that service from the IOC container and all of its features will be available to use. This type of injecting objects is called Dependency Injection.

    DI, IOC and Logger Service Testing

    What is exactly Dependency Injection(DI) and what is IOC (Inversion of Control)? Dependency injection is a technique for achieving loose coupling between objects and their dependencies. It means that rather than instantiating an object every time it is needed, we can instantiate it once and then serve it in a class, most often, through a constructor method. This specific approach we utilize is also known as the Constructor Injection. In a system that is designed to use DI, you may find many classes requesting their dependencies via their constructor. In that case, it is helpful to have a class that will provide all instances to classes through the constructor. These classes are referred to as containers or more specific Inversion of Control containers. An IOC container is essentially a factory that is responsible for providing instances of types that are requested from it. For the logger service testing purpose, we could use WeatherForecastController. We are going to find it in the main project in the Controllers folder. So, let's modify it:
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private readonly ILoggerManager _logger;
    
        public WeatherForecastController(ILoggerManager logger)
        {
            _logger = logger;
        }
    
        [HttpGet]
        public IEnumerable<string> Get()
        {
            _logger.LogInfo("Here is info message from the controller.");
            _logger.LogDebug("Here is debug message from the controller.");
            _logger.LogWarn("Here is warn message from the controller.");
            _logger.LogError("Here is error message from the controller.");
    
            return new string[] { "value1", "value2" };
        }
    }
    
    After all, start the application, and browse to http://localhost:5000/weatherforecast. As a result, you will see an array of two strings. Now go to the folder that you have specified in the nlog.config file, and check out the result. You should see four lines logged inside that file. logged file .net core logging

    Conclusion

    Now you have the working logger service, you will use through an entire process of our application development. By reading this post you've learned:
    • Why logging is important
    • How to create external service
    • How to use NLog library and how to configure it
    • What are DI and IOC
    • And how to use them for service injection
    Thank you for reading and see you soon in the next blog post where we will talk about repository pattern with entity framework core. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
    1164 0 0 0 114 0 0 115 114 0 116 115 0 130 0 0 132 130 0 313 0 0 316 313 0 405 0 0 626 custom file name, how can I do it? For example, when an error occurred, I produce a number for user then developer can figure out where the error occurred. I want to change my file name error to this number.]]> 0 0 636 public Startup(IConfiguration configuration) { LogManager.LoadConfiguration(String.Concat(Directory.GetCurrentDirectory(), "/nlog.config")); Configuration = configuration; } LogManager resides in NLog package if I'm not mistaken hence cannot be accessed from the Startup class I think since we added the NLog dependency to LoggerService and not AccountOwnerServer or am I missing something? Any sort of help is appreciated. Also could you elaborate as to why we need a Contracts project in between when we could directly use the interface in LoggerService?]]> 0 0 699 Projects > LoggerService. We need the Contracts project due to the interface reusability mainly. Now if you want to implement another logger with some other library, but you want to log the same level messages, all you have to do is to create another project and just reference the Contracts project. Then you would have access to the ILoggerManager interface. I hope this helps. All the best.]]> 636 0 701 626 0 893 https://www.ourtechroom.com 0 0 894 893 0 1051 0 0 1052 1051 0 1061 1052 0 1065 1061 0 1129 ConfigureServices() method in the Startup.cs, that adds services to the container (more specifically, the line services.ConfigureLoggerService();?) Or is it another place? (I actually had no clue at first, but while I was writing this question, I just went back to the text, then to the code, and now it all makes sense.. we put a Singleton class in that method into the services IOC.) Thanks ^_^]]> 0 0 1130 1129 0 1265 0 0 1267 1265 0 1269 1267 0 1470 0 0 1471 1470 0
    Module 1 https://code-maze.com/3886-2/ Sun, 08 Jul 2018 19:18:28 +0000 https://code-maze.com/?p=3886 Course Overview //TODO

    MODULE 1, Back To Basics in C#

    Overview

    Welcome to Module 1 - Back To Basics in C#. My name is Vladimir Pecanac, and I'm a web developer and really excited to be your host in this course. In this module, we're gonna talk about the basic concepts of the C# programming language. To make things easier to digest, we'll use these concepts in a series of practical examples. So we're gonna start off by talking about which development environment we should use and we'll clarify some key terms and concepts like data types, variables, operators, and linear structures. Now from there, we're going to move on to string manipulation, conditions, loops, access modifiers and we're going to learn what exceptions are and how to handle them. Next up, we're gonna step our game with methods and you'll see how you can do some pretty powerful things with recursion. And finally, at the end of the Module, we are going to tackle C# arrays and play with Files. This might seem like a lot of information at first, but we are slowly going to work our way through these concepts and apply them through practical examples which will help you understand them perfectly. You'll also have a chance to test your knowledge through quizzes and assignments so you'll know for sure if you've understood the material. So let's go ahead and get started by talking about what development environment is and how to set it up.

    Lesson 1 – Development Environment Setup

    Intro Slide

    Before we dive into C# concepts and start programming, we need to find the right tool to help us along the way. To develop an application in C# we can open Windows Notepad and start hacking. But since we want to do things the smart way, we don't want to develop our applications in Notepad because it would be too hard and too tedious. What we want instead is to use a dedicated environment to develop our applications in. And since C# is a programming language developed by Microsoft, the perfect solution for us is the Visual Studio IDE. So, let's learn what IDEs are, and more specifically what we can do with Visual Studio.

    Slide 1

    Ok, so what does IDE stand for? IDE stands for Integrated Development Environment. It’s basically a tool that consists of various features that help software developers create applications more easily. For example, an IDE might consist of a compiler, a debugger, a source code editor, a solution explorer and much much more. Our IDE of choice throughout this Module will be Visual Studio 2017 Community Edition. Visual Studio is the most popular and the most powerful tool to use with C# and .NET overall. Its utilities span far beyond the scope of this Module, and we are just going to focus of the most important parts for now. First, we are going to learn how to create a simple project, more specifically, a Hello World Console Application. Then, we are going to run the application and see what happens. And finally, we are going to learn to debug our application while it is running. Debugging practically means we are going to pause the application execution at some point and inspect the current state. So let’s dive right into it.

    Demo

    Ok, let’s get right into the project creation. The link to the Visual Studio download is right below the video. Make sure to check ASP.NET and web development checkbox before you install Visual Studio since it contains all the libraries we’ll need for the development of our applications. Assuming that you’ve already downloaded and installed it, we are going to begin from the scratch. To create a project, we need to go to the File, then Project, or simply press Ctrl+Shift+N as indicated right here. Once the dialogue opens we are going to select Visual C# on the left side, and that will show us a bunch of different project templates on the right side of the dialogue. For now, let’s choose a Console App (.NET Core) which will create a new Console Application template for us. If you are not familiar with the concepts of .NET Framework and .NET Core, don’t worry about it, for now, we’ll tackle that topic a bit later on. In the Name field, we can put anything we want, but for this example let’s put “HelloWorld” since that’s usually what the first application is called. In the Location field, you choose the place on the hard drive where your project will reside. Chose something appropriate and easy to find. Solution name is the same as the project name which is fine. Let’s click ok and proceed. There it is. This is the most basic C# application. Its only responsibility is to write “Hello world” in our console window once we run it. So let’s do that. We can click on the green triangle button right here or we can simply do it by pressing F5. You can do it however you like it, but we recommend that you get used to keyboard shortcuts as soon as possible since that will increase your speed and productivity. (Application run and Hello world popped up briefly) Ok, so what happened? Our application indeed runs and did its job perfectly. But since it finished what it had to do, it closed the window and shut down the process. Let’s make a simple tweak to make it stay on as long as we like. (Adding Console.ReadLine();) Now let’s try it one more time. Runs the application. And what do you know now we can clearly see the output in our console. The line we added means that application is waiting for our input to finish. We can press any key now to close the application. Closes it. Ok, and the last thing we want to try out before wrapping this module is to see how we can debug our application. There are two ways to do this. One way of doing this is by clicking right here next to the line we want to debug, and another is by clicking on the line and pressing F9. It’s totally up to you on how you prefer to do this. Let’s try it out. Puts a breakpoint on the first line. As you can see now out application execution is paused and we can see the current state of our application. If we try to press a key in our console right now, it won’t close the application since we paused it before the Console.ReadLine();. Right now we don’t have much to debug, but later on, this mechanism will come in handy. Another important thing to know is that there is a Watch window where we can see the state of our variables when we stop the application. We can enable it by going to Debug and then Windows, and then Watch and select Watch 1. After that, the watch window appears right below the code and amidst other windows that are opened by default. Ok, let’s continue our application by pressing the F5 button while Visual Studio is in focus, and then press any key to close the application while Console is in focus. That’s it, let’s summarize what we’ve learned.

    Summary Slide Lesson 1

    So let’s summarize what we’ve learned in this lesson. We’ve learned what IDE means and how to get Visual Studio. We’ve installed it on our machine and created our first project. After that, we’ve run our console application and learned how to use Visual Studio Debugger to pause and inspect our application. Now let's mover on right into some basic C# concepts, and learn what are the basic build blocks of our applications

    Lesson 2 - Data Types, Declarations, and Variable Definitions

    ]]>
    3886 0 0 0
    Consuming GitHub API (REST) With ServiceStack HTTP Utils https://code-maze.com/?p=5363 Mon, 03 Dec 2018 06:35:01 +0000 https://code-maze.com/?p=5363 A Few Great Ways to Consume RESTful API in C# we introduced a few different ways to consume a Restful API. This article is about going into details of Flurl library and giving a few examples of how to authenticate and consume a restful API such as GitHub's. The source code for this article is located here: ServiceStack Examples so be sure to fork it and follow along with this article to make most out of it. In this article you'll learn: So, let's drill down into it.

    What ServiceStack is and What HTTP Utils are

     

    How to Set Up a Project to Work With HTTP Utils

    Configuring HTTP Utils

    Two Ways to Authenticate with ServiceStack HTTP Utils

    How to Write Get, Post, Put/Patch, and Delete Requests

    Uploading Files

    Exception Handling

    Unit Testing

    Conclusion

    ]]>
    5363 0 0 0
    Handling OPTIONS and HEAD methods in ASP.NET Core Web API https://code-maze.com/?p=48867 Thu, 26 Sep 2019 13:31:33 +0000 https://code-maze.com/?p=48867 48867 0 0 0 Global error test video https://code-maze.com/global-error-test-video/ Sat, 30 Nov 2019 17:35:11 +0000 https://code-maze.com/?p=49669 try-catch block as well as the finally keyword to clean up our resources after the execution of the block. Even though there's nothing wrong with this approach, we can extract all the exception handling logic in a single centralized place. By doing that, we can make our code more readable and the exception handling logic more maintainable. In this video, we're gonna handle errors by using a try-catch block first and after that by using a built-in middleware and our custom middleware that handles our exceptions on a global level. We're doing this to demonstrate the benefits of handling exceptions globally. To get the source code for our starting project you can visit the Global error handling article on our site. The link to the article is in the description below. So let's start the application by opening the Values Controller in the starting project. In this controller, there's a single Get() method and an injected Logger service. It's a common practice to include the log messages while handling errors, and for that reason, we've created a LoggerManager service. It logs all the messages to the C drive, but you can set that by modifying the path in the nlog.config file. If you wanna read more on how to use Nlog in .NET Core, you can visit Logging with NLog article on our site. You can find a link to that article in the description, too. Now, let's start with action modification:
    using System;
    using LoggerService;
    using Microsoft.AspNetCore.Mvc;
    
    namespace GlobalErrorHandling.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class ValuesController : ControllerBase
        {
            private ILoggerManager _logger;
    
            public ValuesController(ILoggerManager logger)
            {
                _logger = logger;
            }
    
            [HttpGet]
            public IActionResult Get()
            {
                try
                {
                    _logger.LogInfo("Fetching all the Students from the storage");
    
                    var students = DataManager.GetAllStudents(); //simulation for the data base access
    
                    _logger.LogInfo($"Returning {students.Count} students.");
    
                    return Ok(students);
                }
                catch (Exception ex)
                {
                    _logger.LogError($"Something went wrong: {ex}");
                    return StatusCode(500, "Internal server error");
                }
            }
        }
    }
    
    The first thing we're gonna do is adding a try-catch block. Inside the try block, we log an info message and return students from the Data manager class. This class is just local storage to simulate the database. Finally, we log an additional info message with the student's count and return our data. Of course, we have to modify the catch block as well. Let's log an error message and return the 500 status code with the "Internal server error" message. Now we can start our application and test a result in Postman. Let's press the send button...... and... we get 200 OK status code and we can see our data. Basic request - Global Error Handling We've created the log files for this application, so, let's examine them: log basic request - Global Error Handling And from what we can see, everything works as expected. Now let’s get back to our code,  and force an exception right below the GetAllStudents() (stjudents)method call by throwing a new exception with a custom message:
    throw new Exception("Exception while fetching all the students from the storage.");
    With this in place, we're gonna start our application again =>... and send the same request: try catche error - Global Error Handling There we go, we can see the error message.  And Of course, let's inspect the log file: log try catch error There it is, we can see the error message logged here as well. So, this works just (mozes dz, ne moras dj) fine. But the downside of this approach is that we need to repeat our try-catch blocks in all the actions in which we want to catch unhandled exceptions. Well, there's a better approach to do that by using the UseExceptionHandler middleware. It is a built-in middleware we can use to handle exceptions. So, let’s dive into the code to see this middleware in action. First, we're gonna add a new class ErrorDetails in the Models folder:
    using Newtonsoft.Json;
    
    namespace GlobalErrorHandling.Models
    {
        public class ErrorDetails
        {
            public int StatusCode { get; set; }
            public string Message { get; set; }
    
    
            public override string ToString()
            {
                return JsonConvert.SerializeObject(this);
            }
        }
    }
    
    This class has two properties. The status code    <====>     the message     <====>   and the overridden ToString method which uses Json converter to serialize the object. We're gonna use this class for setting the error details. Next, let's create a new folder called Extensions and a new static class ExceptionMiddlewareExtensions.cs. which we're gonna modify:
    using GlobalErrorHandling.Models;
    using LoggerService;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Diagnostics;
    using Microsoft.AspNetCore.Http;
    using System.Net;
    
    namespace GlobalErrorHandling.Extensions
    {
        public static class ExceptionMiddlewareExtensions
        {
            public static void ConfigureExceptionHandler(this IApplicationBuilder app, ILoggerManager logger)
            {
                app.UseExceptionHandler(appError =>
                {
                    appError.Run(async context =>
                    {
                        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                        context.Response.ContentType = "application/json";
    
                        var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
                        if(contextFeature != null)
                        { 
                            logger.LogError($"Something went wrong: {contextFeature.Error}");
    
                            await context.Response.WriteAsync(new ErrorDetails()
                            {
                                StatusCode = context.Response.StatusCode,
                                Message = "Internal Server Error."
                            }.ToString());
                        }
                    });
                });
            }
        }
    }
    
    First, we create an extension ConfigureExceptionHandler method with the IApplicationBuilder and ILoggerManager parameters    <===> and with the registered UseExceptionHandler middleware.  By using the Run method from the appError object we register the context object   <===>   and populate the status code  <==> with Internal server error value from the HttpStatusCode enumeration    <===>   and finally populate the content type of our response. Then, we extract the exception handler feature into the contextFeature variable by using the IExceptionHandlerFeature interface. If it's not null    <===>   we log the error message by using contextFeature.Error property  <===>   and finally return the response with the custom created ErrorDetails object. Of course, we have to populate the StatusCode <==> and the Message properties. To be able to use this extension method, let’s modify the Configure method inside the Startup class:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerManager) 
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.ConfigureExceptionHandler(logger);
    
        app.UseHttpsRedirection();
    
        app.UseRouting();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    With all of these in place, we are safe to remove the try-catch block from our action:
    public IActionResult Get()
    {
        _logger.LogInfo("Fetching all the Students from the storage");
    
         var students = DataManager.GetAllStudents(); //simulation for the data base access
    
         throw new Exception("Exception while fetching all the students from the storage.");
    
         _logger.LogInfo($"Returning {students.Count} students.");
    
         return Ok(students);
    }
    And there we go. Our action method is much cleaner now and what’s more important we can reuse this functionality to write more readable actions in the future. Now, we can inspect the result by sending the same request from Postman: Global Handler Middleware There it is our status code... And if we check our log file: log global handler middleware we can see the error message there. So, we just saw the builtin middleware in action, but now, we're gonna use custom middleware to achieve the same thing. Let’s create a new folder named CustomExceptionMiddleware and a new ExceptionMiddlewareclass.
    public class ExceptionMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILoggerManager _logger;
    
        public ExceptionMiddleware(RequestDelegate next, ILoggerManager logger)
        {
            _logger = logger;
            _next = next;
        }
    
        public async Task InvokeAsync(HttpContext httpContext)
        {
            try
            {
                await _next(httpContext);
            }
            catch (Exception ex)
            {
                _logger.LogError($"Something went wrong: {ex}");
                await HandleExceptionAsync(httpContext, ex);
            }
        }
    
        private Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
    
            return context.Response.WriteAsync(new ErrorDetails()
            {
                StatusCode = context.Response.StatusCode,
                Message = "Internal Server Error from the custom middleware."
            }.ToString());
        }
    }
    
    
    So, we create two private readonly variables the RequestDelegate _next and  <===>  ILoggerManager logger. Then we register them through dependency injection. The _next parameter of the RequestDeleagate type is a function delegate that can process our HTTP requests. After that, we create the InvokeAsync() method with the HttpContext parameter. This method is essential for the RequestDelegate type because it can't process requests without it. If everything goes well, the _next delegate carries the request and the Get action in the controller generates a successful response. But if is it's not successful (and it's not gonna be because we are forcing an exception), our middleware will set off the catch block and call the HandleExceptionAsync method. So, let's create that method. Inside it,   <===>  we set up the response content type,   <===>the  status code with the well known enumeration value,    <===> and write our response in async manner, where we asign the value for the StatusCode property  <===> and the Message property as well. Now let’s modify our ExceptionMiddlewareExtensions class with another static method to register our ExceptionMiddleware:
    public static void ConfigureCustomExceptionMiddleware(this IApplicationBuilder app)
    {
        app.UseMiddleware<ExceptionMiddleware>();
    }
    
    Finally, let’s use it in the Configure method in the Startup class:
    //app.ConfigureExceptionHandler(logger);
    app.ConfigureCustomExceptionMiddleware();
    
    Great. Now let's inspect the result again: custom handler middleware There we go. We get the custom middleware message. So as you can see we implemented custom middleware in a couple of steps.

    Conclusion

    Let's sum everything up. We've learned how to handle errors in a more sophisticated and cleaner way. The code is much more readable and our exception handling logic is now reusable for the entire project. If you like this video, we would highly appreciate you hit those like and subscribe buttons down there. Of course, don't forget to visit the Code Maze blog to download the source code. If you like what you see you can subscribe to our mailing list to get notified about our new content and videos. So stay tuned and all the best. Zavrsna scena sa strelicom ka subscribe dugmetu i sa linkom ka nasem sajtu i ka clanku.]]>
    49669 0 0 0 ]]> ]]>
    OWASP Top 10 - Injection https://code-maze.com/?p=49977 https://code-maze.com/?p=49977 The injection attack is the most critical web application security threat as per OWASP Top 10 list. In this article, we are going to look at the Injection attack in detail. We have divided this article into the following sections:

    What is an Injection Attack

    Attackers can perform an injection attack in a web application by sending untrusted data to a code interpreter through a form input or some other mode of data submission.  For example, an attacker could enter SQL database script into a form that expects a plaintext. If we do not properly validate the form inputs, this would result in that SQL code being executed in the database. This is the most common type of injection attack and is known as an SQL injection attack.

    The Vulnerability

    So, when does an application become vulnerable to an injection attack? An application is vulnerable to an injection attack when it
    • do not validate or sanitize user-supplied data  
    • executes dynamic queries or non-parameterized statements directly in the database
    • uses user-supplied data directly or concatenate it in dynamic queries, commands, or stored procedures

    An Example Injection Attack Scenario

    Now, let’s take a look at how an injection attack can surface on a poorly designed application.

    Designing the Database

    For that, We are going to design an application that authenticates users against a database. First, let’s create a database table for storing Login details: table Then, let’s put some values in it: data

    Designing the Application

    After that, let’s create an ASP.NET Core Razor Page application.

    Creating the Login Model

    First, we need to create a Login model:
    public class Login
    {
        public int ID { get; set; }
    
        public string UserName { get; set; }
    
        public string Password { get; set; }
    
        public string Message { get; set; }
    }
    Then, we need to add two pages - Login & LoginSuccess

    Creating the Login Page

    We are going to create  Login page with two text inputs for Username and Password:
    @page
    @model OWASPTop10.Pages.LoginModel
    @{
        ViewData["Title"] = "Login";
    }
    
    <h1>Login</h1>
    
    <form method="post">
        @if (!string.IsNullOrEmpty(Model.Login?.Message))
        {
            <div class="alert-danger">
                @Model.Login?.Message
            </div>
        }
        <div>
            UserName:
            <input asp-for="Login.UserName" />
        </div>
        <div>
            Password:
            <input type=”password” asp-for="Login.Password" />
        </div>
        <input type="submit" />
    </form>
    In the page model class, we'll write the logic to check the user credentials against the database:
    public class LoginModel : PageModel
    {
        [BindProperty]
        public Login Login { get; set; }
    
        public void OnGet()
        {
        }
    
        public IActionResult OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }
    
            using (SqlConnection sqlConnection = new SqlConnection("Data Source=.;Initial Catalog=MvcBook;Integrated Security=True"))
            {
                string commandText = "SELECT [UserName] FROM dbo.[Login] WHERE [UserName] = '"
                    + Login.UserName
                    + "' AND [Password]='"
                    + Login.Password
                    + "' ";
                try
                {
                    using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
                    {
                        sqlConnection.Open();
                        if (sqlCommand.ExecuteScalar() == null)
                        {
                            // Invalid Login
                            Login.Message = "Invalid Login.";
                            return Page();
                        }
    
                        // Valid Login
                        string UserName = sqlCommand.ExecuteScalar().ToString();
                        sqlConnection.Close();
                        return RedirectToPage("./LoginSuccess", new { userName = UserName });
                    }
                }
    
                catch (Exception ex)
                {
                    Login.Message = ex.Message;
                    return Page();
                }
            }
        }
    }
    Here, we have built the SQL query by concatenating the user inputs. Then, this query is executed against the database.

    Creating the LoginSuccess Page

    Let’s add a LoginSuccess page so that users can be redirected to it after a successful login:
    @page "{userName}"
    @model OWASPTop10.Pages.LoginSuccessModel
    @{
        ViewData["Title"] = "LoginSuccess";
    }
    
    <h1>Login Success</h1>
    <div>Hello, <b>@Model.UserName</b> </div>
    We also need to add the page model :
    public class LoginSuccessModel : PageModel
    {
        public string UserName { get; set; }
    
        public void OnGet(string userName)
        {
            UserName = userName;
        }
    }
    Now, we are going to test the application.

    Testing the Application

    Let’s run the application and navigate to /Login: login page First, we are going to enter valid credentials and check the result: UserName: admin Password: admin@123 We are redirected to the login success page: login success The application builds the following query and executes at runtime:
    SELECT [UserName] 
    FROM   dbo.[Login] 
    WHERE  [UserName] = 'admin' 
           AND [Password] = 'admin@123'
    Now, let’s go back to the login page. When an attacker reaches our login page, the first thing he/she may try is entering some random credentials. Then, they’ll be presented with the Invalid login error: invalid login The next thing an attacker may do is check if the web application is vulnerable to injection attacks. They may do so by entering some special characters in the password field. For example, they may try: UserName: admin  Password: password' Then, the attacker is presented with the following error message: Unclosed quotation mark after the character string 'password' '. Incorrect syntax near 'password' ': invalid login with exception Let’s examine the actual query executed at runtime:
    SELECT [UserName] 
    FROM   dbo.[Login] 
    WHERE  [UserName] = 'admin' 
           AND [Password] = 'password''
    This is going to make the attacker very happy because he/she discovers that:
    • There are no validations against the user inputs
    • The application concatenates the user input with some database script in the back-end
    • It executes the concatenated string directly against the database
    Having learned the above facts, the next step the attacker may perform is to modify the input in such a way that the resulting database query always returns true. For example, they could try: UserName: admin Password:  ' OR 1=1 -- Then, the application successfully authenticates the attacker and takes him/her to the login success page: login success   So, how did this happen?

    Attack Explained

    What happened above is, when our input is concatenated with the SQL query, it always returns true and hence the application passes through the authentication phase. Additionally, it falsely identifies the user as the first user in the database, which unfortunately in most cases will be a user with administrative privileges. Let’s examine the actual query that the application executes:
    SELECT [UserName] 
    FROM   dbo.[Login] 
    WHERE  [UserName] = 'admin' 
           AND [Password] = '' 
            OR 1 = 1 --' 
    
    This always returns true and ignores any statements after this. Depending on how the application is designed, how the permission is managed and how the user inputs are used, the attack can get more severe. An unauthorized user can perform the following actions in the increasing order of severity:
    • Log in with administrative privileges
    • Fetch sensitive data from the database
    • Delete important data
    • Drop some key tables

    Prevention Steps

    In the previous section, We have looked at how an injection attack can happen. Now we’re going to see how we can prevent these types of attacks.

    Validation/Sanitization of User Inputs

    Validating/sanitizing the user inputs is the first line of defense against most types of attacks. The kind of validation that we need to perform depends on the application’s logic and the expected user inputs. In the application we discussed in the above section, let’s introduce a validation to restrict single quotes:
    <form method="post">
        @if (!string.IsNullOrEmpty(Model.Login?.Message))
        {
            <div class="alert-danger">
                @Model.Login?.Message
            </div>
        }
        <div>
            UserName:
            <input asp-for="Login.UserName" pattern="[^']*$" title="Cannot contain single quotes"/>
        </div>
        <div>
            Password:
            <input type="password" asp-for="Login.Password" pattern="[^']*$" title="Cannot contain single quotes"/>
        </div>
        <input type="submit" />
    </form>
    This restricts the user from entering single quotes: validation We should always remember that an attacker can easily bypass any validations implemented on the client-side. Therefore, we should implement equivalent validations on the server-side as well:
    public class Login
    {
        public int ID { get; set; }
    
        public string UserName { get; set; }
    
        [ShouldNotContainSingleQuotesValidation(ErrorMessage = "Cannot contain single quotes")]
        public string Password { get; set; }
    
        public string Message { get; set; }
    }
    
    public sealed class ShouldNotContainSingleQuotesValidation : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            return !value.ToString().Contains("'");
        }
    }
    The validation shown here just restricts the single quotes. But in the real world, we’ll have to implement more complex validations depending on our application’s context.

    Parameterized Queries/Stored Procedures/Use of ORMs

    As we explained in the previous section, validation is just a first line of defense and we cannot completely rely on just that for our application’s security.

    Parameterized Queries

    Parameterizing the queries will automatically prevent the injection attempts. Let’s modify the code that authenticates the user:
    public IActionResult OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
    
        using (SqlConnection sqlConnection = new SqlConnection("Data Source=.;Initial Catalog=MvcBook;Integrated Security=True"))
        {
            string commandText = "SELECT [UserName] FROM dbo.[Login] WHERE [UserName] = @userName AND [Password]= @password ";
            try
            {
                using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
                {
                    sqlCommand.Parameters.Add(new SqlParameter("userName", Login.UserName));
                    sqlCommand.Parameters.Add(new SqlParameter("password", Login.Password));
    
                    sqlConnection.Open();
                    if (sqlCommand.ExecuteScalar() == null)
                    {
                        // Invalid Login
                        Login.Message = "Invalid Login.";
                        return Page();
                    }
    
                    // Valid Login
                    string UserName = sqlCommand.ExecuteScalar().ToString();
                    sqlConnection.Close();
                    return RedirectToPage("./LoginSuccess", new { userName = UserName });
                }
            }
    
            catch (Exception ex)
            {
                Login.Message = ex.Message;
                return Page();
            }
        }
    }
    Now, let’s try to login with below credentials: UserName: admin Password: password` invalid login By parameterizing the user inputs, we can see that the injection attacks are taken care of by the ADO.Net.

    Stored Procedures

    We can improve this one step further by changing inline SQL queries with a stored procedure. Let’s create a stored procedure and encapsulate the logic for checking user credentials there:
    CREATE PROCEDURE [dbo].[CheckLogin]
    	@userName varchar(50),
    	@password varchar(50)
    AS
    	SELECT [UserName] 
    	FROM dbo.[Login] 
    	WHERE 
    		[UserName] = @userName 
    		AND [Password]= @password 
    RETURN 0
    We also need to make the following changes in code:
    public IActionResult OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
    
        using (SqlConnection sqlConnection = new SqlConnection("Data Source=.;Initial Catalog=MvcBook;Integrated Security=True"))
        {
            string commandText = "[dbo].[CheckLogin]";
    
            try
            {
                using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
                {
                    sqlCommand.CommandType = CommandType.StoredProcedure;
    
                    sqlCommand.Parameters.Add(new SqlParameter("@userName", Login.UserName));
                    sqlCommand.Parameters.Add(new SqlParameter("@password", Login.Password));
    
                    sqlConnection.Open();
                    if (sqlCommand.ExecuteScalar() == null)
                    {
                        // Invalid Login
                        Login.Message = "Invalid Login.";
                        return Page();
                    }
    
                    // Valid Login
                    string UserName = sqlCommand.ExecuteScalar().ToString();
                    sqlConnection.Close();
                    return RedirectToPage("./LoginSuccess", new { userName = UserName });
                }
            }
    
            catch (Exception ex)
            {
                Login.Message = ex.Message;
                return Page();
            }
        }
    }
    This will help us to prevent injection attacks and achieve better separation between the SQL scripts and user inputs.

    Use of ORMs

    Another better alternative would be to use Object Relational Mapping (ORM) frameworks to make the data access more seamless. Using ORM tools also means that developers rarely have to write SQL statements in their code and good ORM tools use parameterized statements under the hood.  Entity Framework (EF) Core is an example of a good ORM tool that works well with .NET Core. We have explained how to use EF Core with ASP.NET Core in the code-first and database-first articles. Using an ORM does not automatically make us completely immune to SQL injection as they still allow us to construct SQL statements if we intend to do so. Therefore, we should try to avoid that as much as possible and have to be extremely careful if we decide to do so.

    Apply the Principle of Least Privilege

    The Principle of Least Privilege states that we should operate every user or process within a system using the least amount of privilege necessary to undertake their job. That way, we can mitigate any risks if a component is compromised or an individual goes rogue. Usually, we do not expect an application to change the structure of the database at run-time. Typically we create, modify and drop database objects as part of the release process with temporarily elevated permissions. Therefore, it is a good practice to reduce the permissions of the application at runtime, so that it can at most edit data, but not change the database structures. In a SQL Server database, this means making sure our production accounts can only execute DML statements, not DDL statements. While designing complex databases, it is worth making these permissions even more fine-grained. We should allow data edits only through stored procedures that run on user accounts with elevated privileges. Furthermore, we should execute all data read/search process with read-only permissions. Sensibly designing database access permissions this way can provide a vital next line of defense. That way, even if the attacker gets access to our system, we can mitigate the type of damage they can cause.

    Password hashing

    The example attack that we performed earlier relied on the fact that the password was stored as plain-text in the database. In fact, storing unencrypted passwords is a major security flaw in itself. Applications should store user passwords as strong hashes, preferably salted. By doing so, we can mitigate the risk of a malicious user trying to steal credentials or impersonating other users.

    Using Industry Standard Third-Party Authentication

    Wherever possible, it's a good idea to consider outsourcing the authentication workflow of our application entirely. Google, Microsoft, LinkedIn, Facebook, Twitter, etc provide OAuth based authentication, that can be used to let users log in to our website using their existing accounts on those systems. As developers, this saves us the pain of implementing our own authentication mechanism and we can also avoid the risk of storing user credentials. As these providers implement industry-standard protocols, we can rest assured that these systems will have good security measures in place to prevent all common attack scenarios.

    Setting Limits to Data Exposure

    There are a few ways in which we can put limits on the result sets returned by our application. We can limit row counts processed or returned. By doing so, we can prevent reading or returning too much data. It is also possible to implement a date range limit to restrict the data to be returned from just a narrow range. Also, it's a good practice to always restrict blank searches. As a rule of thumb, always “return only what the user specifically asks for”. By implementing these restrictions, we can set a limit on the data exposure that can happen even if an attacker gets access to our application.

    Conclusion

    In this article, we have learned the following topics:
    • What is an Injection Attack
    • When does our application become vulnerable to Injection Attacks
    • An example Injection Attack scenario
    • The steps for preventing an Injection Attack
    ]]>
    49977 0 0 0
    How to Send an Email with Attachments in ASP.NET Core https://code-maze.com/?p=50048 Mon, 30 Dec 2019 09:32:04 +0000 https://code-maze.com/?p=50048 source code is available at our GitHub repo. So, let’s see the basic navigation for this article:

    Creating a Project and a Basic Configuration

    We are going to start with a new ASP.NET Core 3.1 Web API project. After the project creation, we are going to add the .NET Core Class Library project with the name EmailService. Of course, we have to add a reference to the main project: Project structure Let’s add the EmailConfiguration class inside that project:
    public class EmailConfiguration
    {
        public string From { get; set; }
        public string SmtpServer { get; set; }
        public int Port { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
    }
    This class contains properties needed to configure sending email messages from our application. We are going to use the appsettings.json file to populate these properties, and in order to do that, we have to modify that file:
    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      },
      "EmailConfiguration": {
        "From": "codemazetest@gmail.com",
        "SmtpServer": "smtp.gmail.com",
        "Port": 465,
        "Username": "codemazetest@gmail.com",
        "Password": "our test password"
      },
        "AllowedHosts": "*"
    }
    
    To finish with the configuration process, we are going to modify the ConfigureServices method in the Startup.cs class:
    public void ConfigureServices(IServiceCollection services)
    {
        var emailConfig = Configuration
            .GetSection("EmailConfiguration")
            .Get<EmailConfiguration>();
        services.AddSingleton(emailConfig);
    
        services.AddControllers();
    }
    
    So, we extract configuration values from the appsettings file and register EmailConfiguration as a singleton. And that’s all we need to configure our email service.

    How to Send Email in Synchronous Way?

    Before starting any other operation in our project, we have to add the NETCore.MailKit library to the EmailService project: MailKit This library is going to help us with the email sending process. Next, in the same project, we are going to create a Message class:
    public class Message
    {
        public List<MailboxAddress> To { get; set; }
        public string Subject { get; set; }
        public string Content { get; set; }
    
        public Message(IEnumerable<string> to, string subject, string content)
        {
            To = new List<MailboxAddress>();
    
            To.AddRange(to.Select(x => new MailboxAddress(x)));
            Subject = subject;
            Content = content;        
        }
    }
    
    We are going to use this class to set the data related to our email recipients, subject and content. Then, let’s create a new interface:
    public interface IEmailSender
    {
        void SendEmail(Message message);
    }
    
    And a class that implements this interface:
    public class EmailSender : IEmailSender
    {
        private readonly EmailConfiguration _emailConfig;
    
        public EmailSender(EmailConfiguration emailConfig)
        {
            _emailConfig = emailConfig;
        }
    
        public void SendEmail(Message message)
        {
            var emailMessage = CreateEmailMessage(message);
    
            Send(emailMessage);
        }
    }
    
    As you can see, we inject email configuration into EmailSender class and then we call two different methods to create an email message and to send the email respectively. Now, let’s implement those two missing methods:
    private MimeMessage CreateEmailMessage(Message message)
    {
        var emailMessage = new MimeMessage();
        emailMessage.From.Add(new MailboxAddress(_emailConfig.From));
        emailMessage.To.AddRange(message.To);
        emailMessage.Subject = message.Subject;
        emailMessage.Body = new TextPart(MimeKit.Text.TextFormat.Text) { Text = message.Content };
    
        return emailMessage;
    }
    
    private void Send(MimeMessage mailMessage)
    {
        using (var client = new SmtpClient())
        {
            try
            {
                client.Connect(_emailConfig.SmtpServer, _emailConfig.Port, true);
                client.AuthenticationMechanisms.Remove("XOAUTH2");
                client.Authenticate(_emailConfig.UserName, _emailConfig.Password);
    
                client.Send(mailMessage);
            }
            catch
            {
                //log an error message or throw an exception or both.
                throw;
            }
            finally
            {
                client.Disconnect(true);
                client.Dispose();
            }
        }
    }
    
    We use the first method to create an object of type MimeMessage and to configure the required properties. Then, we pass that object to the second method and use the SmtpClient class to connect to the email server, authenticate and send the email. Just pay attention that the SmtpClient class comes from the MailKit.Net.Smtp namespace. So, you should use that one instead of System.Net.Mail. We have one more step to do. Let’s modify the Get action in the WeatherForecastController:
    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        var rng = new Random();
    
        var message = new Message(new string[] { "codemazetest@mailinator.com" }, "Test email", "This is the content from our email.");
        _emailSender.SendEmail(message);
    
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
    
    That’s it. Let’s test this now: Postman request sync We get a 200 OK response. So, let’s check the source email server: Email server source And let’s check the destination server: Email server destination - Send Email Excellent. Everything works as expected. Just one note here. If you are using the Gmail server as a source server and you get an error about less secure apps, all you have to do is to sign in to your Gmail account, follow this link and turn on allow option.

    Using HTML in the Email Message Body

    In the previous example, we have been using the Plain Text as a body format. But we can use HTML as well with a few modifications to our code. So, all we have to do is to modify the CreateEmailMessage method:
    private MimeMessage CreateEmailMessage(Message message)
    {
        var emailMessage = new MimeMessage();
        emailMessage.From.Add(new MailboxAddress(_emailConfig.From));
        emailMessage.To.AddRange(message.To);
        emailMessage.Subject = message.Subject;
        emailMessage.Body = new TextPart(MimeKit.Text.TextFormat.Html) { Text = string.Format("<h2 style='color:red;'>{0}</h2>", message.Content) };
    
        return emailMessage;
    }
    
    And that’s all it takes. A simple change of the body text format and the content itself. We can try sending the same request one more time from Postman: Html body for the email message It doesn’t get any easier than that.

    Asynchronous Way of Sending Email Messages in ASP.NET Core

    If we want to send email messages asynchronously, we have to make some changes to our project. If you are not familiar with the asynchronous programming in ASP.NET Core Web API, we strongly recommend reading our article about Asynchronous Repository Pattern in ASP.NET Core Web API. Let’s start with the interface modification:
    public interface IEmailSender
    {
        void SendEmail(Message message);
        Task SendEmailAsync(Message message);
    }
    
    Next, let’s modify the EmalSender class:
    public async Task SendEmailAsync(Message message)
    {
        var mailMessage = CreateEmailMessage(message);
    
        await SendAsync(mailMessage);
    }
    
    As you can see, we use the same method to create an email message but a different method to send that email. So, we have to implement that one:
    private async Task SendAsync(MimeMessage mailMessage)
    {
        using (var client = new SmtpClient())
        {
            try
            {
                await client.ConnectAsync(_emailConfig.SmtpServer, _emailConfig.Port, true);
                client.AuthenticationMechanisms.Remove("XOAUTH2");
                await client.AuthenticateAsync(_emailConfig.UserName, _emailConfig.Password);
    
                await client.SendAsync(mailMessage);
            }
            catch
            {
                //log an error message or throw an exception, or both.
                throw;
            }
            finally
            {
                await client.DisconnectAsync(true);
                client.Dispose();
            }
        }
    }
    
    Finally, we have to modify the Get action:
    [HttpGet]
    public async Task<IEnumerable<WeatherForecast>> Get()
    {
        var rng = new Random();
    
        var message = new Message(new string[] { "codemazetest@mailinator.com" }, "Test email async", "This is the content from our async email.");
        await _emailSender.SendEmailAsync(message);
    
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
    
    Awesome. Now let’s try this out with the same request from Postman: Async email message

    Adding Attachments to the Email Message

    In order to include attachments to our email messages, we have to provide the way for our app to process the attached files. So, let’s start by adding a new POST action to our controller:
    [HttpPost]
    public async Task<IEnumerable<WeatherForecast>> Post()
    {
        var rng = new Random();
    
        var files = Request.Form.Files.Any() ? Request.Form.Files : new FormFileCollection();
    
        var message = new Message(new string[] { "codemazetest@mailinator.com" }, "Test mail with Attachments", "This is the content from our mail with attachments.", files);
        await _emailSender.SendEmailAsync(message);
    
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
    
    This action is almost the same as the previous one, but it has some important modifications. The first thing to notice is that this is a POST action. The second change is the part where we extract files from the request, if any, or return an empty file collection. The third change is related to the Message constructor, it now accepts an additional parameter. Of course, we have to modify the Message class:
    public class Message
    {
        public List<MailboxAddress> To { get; set; }
        public string Subject { get; set; }
        public string Content { get; set; }
    
        public IFormFileCollection Attachments { get; set; }
    
        public Message(IEnumerable<string> to, string subject, string content, IFormFileCollection attachments)
        {
            To = new List<MailboxAddress>();
    
            To.AddRange(to.Select(x => new MailboxAddress(x)));
            Subject = subject;
            Content = content;
            Attachments = attachments;
        }
    }
    
    As you can see, we’ve added the additional IFormCollection parameter. Now, let’s move on to the EmailSender class:
    private MimeMessage CreateEmailMessage(Message message)
    {
        var emailMessage = new MimeMessage();
        emailMessage.From.Add(new MailboxAddress(_emailConfig.From));
        emailMessage.To.AddRange(message.To);
        emailMessage.Subject = message.Subject;
    
        var bodyBuilder = new BodyBuilder { HtmlBody = string.Format("<h2 style='color:red;'>{0}</h2>", message.Content) };
    
        if (message.Attachments.Any())
        {
            byte[] fileBytes;
            foreach (var attachment in message.Attachments)
            {
                using (var ms = new MemoryStream())
                {
                    attachment.CopyTo(ms);
                    fileBytes = ms.ToArray();
                }
    
                bodyBuilder.Attachments.Add(attachment.FileName, fileBytes, ContentType.Parse(attachment.ContentType));
            }
        }
    
        emailMessage.Body = bodyBuilder.ToMessageBody();
        return emailMessage;
    }
    
    We now use the BodyBuilder class to create a body for the email message. Additionally, we check for the attachment files and if they exist, we convert each of them to the byte array and add it to the Attachments part from the bodyBuilder object. Finally, we convert the bodyBuilder object to the message body and return that message. Now, before we use Postman to send a request, we are going to add the FormOptions configuration to the ConfigureServices method:
    public void ConfigureServices(IServiceCollection services)
    {
        var emailConfig = Configuration
            .GetSection("EmailConfiguration")
            .Get<EmailConfiguration>();
        services.AddSingleton(emailConfig);
        services.AddScoped<IEmailSender, EmailSender>();
    
        services.Configure<FormOptions>(o => {
            o.ValueLengthLimit = int.MaxValue;
            o.MultipartBodyLengthLimit = int.MaxValue;
            o.MemoryBufferThreshold = int.MaxValue;
        });
    
        services.AddControllers();
    }
    
    With the FormOptions configuration, we set the limit for it’s different properties to the maximum value. To prepare a request in Postman, we have to choose the POST method option and to use the form-data options to send files with the request: Send Email with attachment Postman And let’s inspect our email: Attachments email Excellent work.

    Conclusion

    We did a great job here. Now we have a fully functional Web API service that we can use in our projects to send email messages. So to sum up, we have learned:
    • How to create a basic configuration
    • The way to send email messages with the plain text or html body
    • How to send email messages asynchronously
    • And how to include attachments in an email message
    [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
    50048 0 0 0
    Automapper video https://code-maze.com/automapper-video/ Mon, 30 Dec 2019 11:42:58 +0000 https://code-maze.com/?p=50064 In this video, we're gonna learn how to use AutoMapper in an ASP.NET Core application. We're gonna start by looking into what the AutoMapper is and what problem it solves. Then, we're gonna explain how to use AutoMapper with a couple of examples.  We’ll also take a look at how to flatten complex object models. So, what is AutoMapper? AutoMapper is a simple library that transforms one object type to another. It's a convention-based object-to-object mapper that doesn't require too much configuration. The object-to-object mapping works by transforming an input object of one type into an output object of a different type. To get the source code for this project, you can visit the AutoMapper article on our site. The link to the article is in the description below. Additionally, you can read more about use cases, when to use AutoMapper, when not, and the best practices by reading the same article.r Concerning this video, we're gonna focus on the coding part and code explanations. So, let’s have a look at how to add Automapper into our .NET Core application. The first step is to install the corresponding NuGet package:
    Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
    Once we install this package, it will automatically install the AutoMapper package for us. So, the next step is to configure the services. Let’s do that in the Startup.cs class by calling the AddAutoMapper method <==> and providing the Startup class as a type
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAutoMapper(typeof(Startup));
        services.AddControllersWithViews();
    }
    And that’s all it takes. AutoMapper is installed and configured in our project. Now, let’s see how to use it with our objects. We're gonna start with a new Userclass in the Models folder.  This class will have several properties: Id, FirstName, LastName, Email, and Address
    public class User
    {
        public int Id { get; set; }
    
        public string FirstName { get; set; }
    
        public string LastName { get; set; }
    
        public string Email { get; set; }
    
        public string Address { get; set; }
    }
    Then, let's create a new ViewModelfolder <==> and the UserViewModel class to display the user information. We're gonna add the FirstName property <==> LastName <==> and Email
    public class UserViewModel
    {
        public string FirstName { get; set; }
    
        public string LastName { get; set; }
    
        public string Email { get; set; }
    }
    Now, let’s see how to convert our domain object to a view model. A good way to organize our mapping configurations is with Profiles. That said, let's create a new UserProfile class. This class inherits from the Automapper's Profile class and we create a mapping configuration inside the constructor:
    public UserProfile()
    {
        CreateMap<User, UserViewModel>();
    }
    So, basically, we create the mapping from our User domain object to the UserViewModel. As soon as our application starts, AutoMapper will scan our application and look for classes that inherit from the Profile class and load their mapping configurations. Now, let’s define a new Controller <==> with the name UserController <==> and use the Auto-Mapping capabilities that we just added: The first thing we're gonna do is adding a new static method, that provides some demo data required for the mapping actions. All we do is return a new user object <===> with populated properties without an address for now
    private static User GetUserDetails()
            {
                return new User()
                {
                    Id = 1,
                    FirstName = "John",
                    LastName = "Smith",
                    Email = "John.Smith@gmail.com"
                };
            }
    Then...
    public class UserController : Controller
    {
        private readonly IMapper _mapper;
    
        public UserController(IMapper mapper)
        {
            _mapper = mapper;
        }  
    
        public IActionResult Index()
        {
            // Populate the user details from DB
            var user = GetUserDetails();
    
            UserViewModel userViewModel = _mapper.Map<UserViewModel>(user);   
    
            return View(userViewModel);
        }
    }
    let's add a private mapper variable <==> and inject it into the constructor.  <==> Then, we create the user object  <==>  and call the Map() method, which maps the User object to the UserViewModel object.  Finally, we pass the userViewModel to the Index view. Now, we're gonna create a View for the Index action method... Let's choose a Details template and the UserViewModel class After that, let's run the application  <==>   and just navigate to the User controller  <===>  run the app We can see our User domain object is properly mapped to the UserViewModel object. Well, that was quite simple, wasn’t it?  But what if we have different property names in our source and destination objects. Let’s take a look at how to do the mapping in these cases. Let’s modify the property names in the UserViewModel class. Instead of FirstName and LastName, we'll have FName and LName. Email stays the same
    public class UserViewModel
    {
        public string FName { get; set; }
    
        public string LName { get; set; }
    
        public string Email { get; set; }
    
    }
    So we have to map User.FirstName -> UserViewModel.FName User.LastName -> UserViewModel.LName User.EMail -> UserViewModel.Email For this to work, we have to change the mapping in the UserProfile class:
    public UserProfile() 
    {
        CreateMap<User, UserViewModel>()
            .ForMember(dest =>
                dest.FName,
                opt => opt.MapFrom(src => src.FirstName))
            .ForMember(dest =>
                dest.LName,
                opt => opt.MapFrom(src => src.LastName))
    }
    We use the CreateMap() method to create a mapping by providing the source and destination properties.  To customize the configuration for individual members, we can use the ForMember() method which has the destinationMember parameter,  of type Expression and memberOptions parameter, of type Action. So, let's use the ForMember method, and provide the destination property <==> and the option, where we map from the FirstName property to the FName destination property.  <==> We're gonna do the same thing for the LastName and LName properties. Additionally, we have to modify the Index view, by renaming the same properties After making these changes, let’s run the application once again.  <===> We can see that these properties are mapped correctly.

    So far, we have only looked at one-directional mapping, which means if we have two types Type A and Type B, then we only map Type A to Type B. But, by using Automapper’s Reverse mapping capability, it's possible to achieve bi-directional mapping. So, all we have to do is to add the ReverseMap method to our profile.
    public UserProfile()
    {
        CreateMap<User, UserViewModel>()
            .ForMember(dest =>
                dest.FName,
                opt => opt.MapFrom(src => src.FirstName))
            .ForMember(dest =>
                dest.LName,
                opt => opt.MapFrom(src => src.LastName))
            .ReverseMap();
    }
    Once Reverse Mapping is configured, we can map back from destination to source type:
    var mappedUser = _mapper.Map<User>(userViewModel);
    This way, we can easily achieve bi-directional mapping between types using AutoMapper’s Reverse Mapping capabilities. We can remove this code because we don't use it at the moment. If you wanna learn more about what's happening behind the scenes and how Automapper maps our objects, you can visit our Automapper article on the Code Maze blog. As we said, you can find the link in the description. So, let's move on with the project. AutoMapper supports flattening complex object models into DTO or another simple object model.  If we follow proper naming conventions for our object models, then there is no need to provide any additional configuration code. AutoMapper works with conventions and maps our object model from complex to flat/simple ones. You can read more about this in the mentioned article. Now, to see flattening in practice,   <==>    let’s modify our User object by modifying the Address property  <==> and adding a new GetFullName method:
    public class User
    {
        public int Id { get; set; }
    
        public string FirstName { get; set; }
    
        public string LastName { get; set; }
    
        public string Email { get; set; }
    
        public Address Address { get; set; }
    
        public string GetFullName()
        {
            return $"{this.LastName}, {this.FirstName}";
        }
    }
    Of course, we have to create the Address class. And add several properties: Id,  <==> Street  <==> City  <==> and Country
    public class Address
    {
        public int Id { get; set; }
    
        public string Street { get; set; }
    
        public string City { get; set; }
    
        public string Country { get; set; }
    }
    Let’s modify the UserViewModel class as well. We're gonna remove the FirstName and LastName properties, and add the FullName property with the Display attribute. Additionally, we're adding the AddresCountry property that will be displayed as Country in our view.
    public class UserViewModel
    {
        [Display(Name = "Full Name")]
        public string FullName { get; set; }
    
        [Display(Name = "Country")]
        public string AddressCountry { get; set; }
    
        public string Email { get; set; }
    }
    We have to populate this additional property in the GetUserDetails method:
     private static User GetUserDetails() 
            { 
                return new User() 
                { 
                    Id = 1, 
                    FirstName = "John", 
                    LastName = "Smith", 
                    Email = "John.Smith@gmail.com",
                    Address = new Address { Country = "USA" }
                }; 
            }
      And finally, let’s modify the profile class to use the default conventions:
    public UserProfile()
    {
        CreateMap<User, UserViewModel>();
    }
    Excellent. But we're not done with modifications. We have to modify our view  <=>   Let's change the FirstName property to the Fullname and <==>  The LastName property to the AddressCountry Now let's run the application once again   <===> and navigate to the required action: run the app with complex type mapping This time, we can see the GetFullName() method on the source object is correctly mapped to FullName property on the destination object. In a similar way, the Country property is automatically mapped to AddressCountry   These mappings are correctly handled by the AutoMapper using its default conventions. So, that's all on this topic. If you like this video, we would highly appreciate you hit those like and subscribe buttons down there. Of course, don't forget to visit the Code Maze blog to download the source code. If you like what you see you can subscribe to our mailing list to get notified about our new content and videos. So stay with us, and we see you again on our blog site or in another video. Until then, all the best. Zavrsna scena sa strelicom ka subscribe dugmetu i sa linkom ka nasem sajtu i ka clanku.]]>
    50064 0 0 0
    Top REST API Best Practices https://code-maze.com/top-rest-api-best-practices/ Mon, 29 Jan 2018 08:15:42 +0000 https://code-maze.com/?p=1127 REST is pretty flexible and compatible with HTTP that is the main protocol the internet is based upon. Since it is an architectural style and not the standard, it provides a lot of freedom to implement various design best practices. Did I mention it's language agnostic? [sc name="part_of_series" headline="Recommended Articles"] In this blog post, our goal will be to explain REST as clearly as possible so you can clearly understand when and how to use it, as well as what it is in essence. We'll go through some basics and definitions as well as show off some REST API best practices. This should give you all the knowledge you need to implement REST API in any language you prefer to code in. If you are not that familiar with HTTP, I recommend reading our HTTP series, or at least part 1 of it, so you can digest this material more easily. So, in this post we'll talk about: About REST: REST API best practices:

    So What Is REST Essentially?

    REST (Representational State Transfer) is an architectural style founded by Roy Fielding in his Ph.D. dissertation "Architectural Styles and the Design of Network-based Software Architectures" at UC Irvine. He developed it in parallel with HTTP 1.1 (no pressure). We use REST primarily as a way to communicate between computer systems on the World Wide Web.

    Is REST Bound to HTTP?

    By definition, it's not. Although you can use some other application protocol with REST, HTTP has remained undisputed champion among application protocols when it comes to the implementation of REST.

    REST and HATEOAS Support

    HATEOAS or Hypermedia As The Engine Of Application State is the important feature of every scalable and flexible REST API. The HATEOAS constraint proposes that the client and server communicate entirely utilizing the hypermedia. There are several advantages to using hypermedia:
    • Enables API designers rather than to include everything they can in each response, to provide one thing properly and hypermedia links to related endpoints and thus decouple the design
    • Helps an API evolve and mature more gracefully
    • Provides a user with the means to explore the API more deeply
    So it is clear that the HATEOAS was designed with durability in mind. Here is how GitHub does it:
    GET https://api.github.com/users/codemazeblog
    Response:
    {
      "login": "CodeMazeBlog",
      "id": 29179238,
      "avatar_url": "https://avatars0.githubusercontent.com/u/29179238?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/CodeMazeBlog",
      "html_url": "https://github.com/CodeMazeBlog",
      "followers_url": "https://api.github.com/users/CodeMazeBlog/followers",
      "following_url": "https://api.github.com/users/CodeMazeBlog/following{/other_user}",
      "gists_url": "https://api.github.com/users/CodeMazeBlog/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/CodeMazeBlog/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/CodeMazeBlog/subscriptions",
      "organizations_url": "https://api.github.com/users/CodeMazeBlog/orgs",
      "repos_url": "https://api.github.com/users/CodeMazeBlog/repos",
      "events_url": "https://api.github.com/users/CodeMazeBlog/events{/privacy}",
      "received_events_url": "https://api.github.com/users/CodeMazeBlog/received_events",
      "type": "User",
      "site_admin": false,
      "name": "Code Maze",
      "company": "Code Maze",
      "blog": "https://code-maze.com",
      "bio": "A practical programmers' resource.",
      ...
    }
    As you can see, besides the crucial information requested by the client, you can find a bunch of related hypermedia links in the response which lead you to other parts of the API you can freely explore.

    What Does RESTful API Mean?

    "RESTful", implies a few features:
    • Client-server architecture: The complete service is comprised of the "Client" which is the front end and the "Server" which is the backend part of the whole system
    • Stateless: The server should not save any states between different requests. The state of the session is exclusively left to the responsibility of the client. As per REST definition: All REST interactions are stateless. That is, each request contains all of the information necessary for a connector to understand the request, independent of any requests that may have preceded it. (Roy's dissertation ch. 5.2.2)
    • Cacheable: The client should be able to store responses in a cache for the greater performance
    So, the RESTful API is a service that follows these rules (hopefully) and uses HTTP methods to manipulate the set of resources. But why do we need or use RESTful APIs? Because they give us an easy, flexible and scalable way to make distributed applications that communicate over the internet.

    Can We Have Too Much REST?

    Yes, you guessed it. Yes, we can :) There is even a phrase for the people that follow the REST fanatically as defined by Mike Schinkel.
    RESTifarian is a zealous proponent of the REST software architectural style as defined by Roy T. Fielding in Chapter 5 of his PhD. dissertation at UCIrvine. You can find RESTifarians in the wild on the REST-discuss mailing list. But be careful, RESTifarians can be extremely meticulous when discussing the finer points of REST, as I learned recently while participating on the list. :)
    Too much of anything can be bad. We need a bit pragmatism to make good applications and services. A theory is important to know and understand, but the implementation of that theory is what differentiate bad vs good vs excellent applications. So be smart, have the end-user in mind. So let's go some important points that make the API "shine", and the lives of the users a whole lot easier.

    Abstract vs Concrete APIs

    When developing software we often use abstraction and polymorphism to get most of our applications. We want to reuse as much of the code as possible. So should we write our APIs that way too? Well, that is not exactly the case with APIs. For the REST APIs, the concrete is better than the abstract. Can you guess why? Let me show you a few examples: Let's look at two API versions. Is it better to have an API that has one /entities or an API that has /owners, /blogs and  /blogposts separately? Which one seems more descriptive to you as a developer? Which API would you rather use? I would always choose the second one.

    URI Formatting (Nouns, Not Verbs). Good URL vs Bad URL Examples

    Here is another one of the REST API best practices. How should you format your endpoints? If you use the software development approach you will end up with something like this: /getAllBlogPosts /updateBlogPost/12 /deleteBlogPost/12 /getAuthorById/3 /deleteAuthor/3 /updateAuthor/3 You get the point... There will be a ton of endpoints, each one doing something else. There is a better system to sort this mess out. Treat the resource like a noun, and HTTP method as a verb. If you do it like that, you'll end up with something like this: GET /blogposts - gets all the blog posts GET /blogposts/12 - gets the blog post with the id 12 POST /blogposts - adds a new blog post and returns the details DELETE /blogposts/12 - removes the blog post with the id 12 GET /authors/3/blogposts - gets all the blog posts of the author with id 3 This is a cleaner and more precise way to create an API. It is immediately clear to the end user, and there is a method to the madness. You can make it even cleaner by using singular instead of plural for the resource names. That one is up to you.

    Error Handling

    Another important aspect of the API building. There are a few good ways to handle errors. Let's see how the top dogs do it: Twitter:
    • Request: GET https://api.twitter.com/1.1/account/settings.json
    • Response: Status Code 400
      {"errors":[{"code":215,"message":"Bad Authentication data."}]}
    Twitter gives you the Status Code and Error Code with a short description of the nature of the error that occurred. They leave it up to you to look the codes up on their Response Codes page. Facebook:
    • Request: GET https://graph.facebook.com/me/photos
    • Response: Status Code 400
      {
         "error": {
            "message": "An active access token must be used to query information about the current user.",
            "type": "OAuthException",
            "code": 2500,
            "fbtrace_id": "DzkTMkgIA7V"
         }
      }
    Facebook gives you a more descriptive error message. Twilio:
    • Request: GET https://api.twilio.com/2010-04-01/Accounts/1234/IncomingPhoneNumbers/1234
    • Response: Status Code 404
      <?xml version='1.0' encoding='UTF-8'?>
      <TwilioResponse>
          <RestException>
              <Code>20404</Code>
              <Message>The requested resource /2010-04-01/Accounts/1234/IncomingPhoneNumbers/1234 was not found</Message>
              <MoreInfo>https://www.twilio.com/docs/errors/20404</MoreInfo>
              <Status>404</Status>
          </RestException>
      </TwilioResponse>
    Twilio gives you XML response by default and the link to the documentation where you can find the error details. As you can see, the approaches to error handling differ from the implementation to the implementation. The important thing is not to leave the user of the REST API “hanging”, not knowing what happened or aimlessly wandering through the wastes of StackOverflow searching for the explanation.

    Status Codes

    When designing a REST API, we communicate with the API user by utilizing HTTP Status Codes. There are a lot of status codes, describing multiple possible responses. But just how many should we use? Should we have a strict status code for every situation? As with many things in life, the KISS principle applies here too. There are over 70 status codes out there. Do you know them by heart? Will the potential API user know them all, or will it once again result in googling stuff? Most developers are familiar with the most common status codes:
    • 200 OK
    • 400 Bad Request
    • 500 Internal Server Error
    By starting with these three, you can cover most of the functionalities of your REST API. Other commonly seen codes include:
    • 201 Created
    • 204 No Content
    • 401 Unauthorized
    • 403 Forbidden
    • 404 Not Found
    We can use these to help the user figure out quickly what the result was. We should probably include some kind of message if you feel the status code is not descriptive enough like we discussed in the Error handling section. Once again, we need to be pragmatic, help the user by using a limited number of codes and descriptive messages. You can find the complete HTTP Status codes list, as well as other helpful HTTP stuff here, summarized on CodeMaze.

    Security

    There is not much to be said about REST API security because REST doesn’t deal with security. It relies upon standard HTTP mechanisms like basic or digest authentication. Every request should be made over HTTPS. There are many tricks to improve the security of your REST API, but you must be cautious when implementing them, because of the stateless nature of REST. Remembering the state of the last request goes out of the window, and the client is where the state should be stored and verified. Timestamping and logging requests can help a bit too. There is much more to be said on this topic, but it is out of the scope of this post. We have a nice post on HTTP Security here on CodeMaze if you want to learn more about that.

    REST API Versioning

    You’ve already written your REST API and it has been very successful and many people have used it and are happy with it. But you have that juicy new functionality that breaks other parts of the system. The breaking change. Never fear, there is a solution for that! Before we start making your API, we can version it by prefixing the endpoints with the API version: https://api.example.com/v1/authors/2/blogposts/13 This way we can always increment our API version number (eg. v2, v3…) whenever there are breaking changes in our API. This also signals the users something drastic has changed and they should be careful when using the new version.

    Importance of Documentation

    This one is the no-brainer. You could be the best API designer in the world, but without documentation, your API is as good as dead. Proper documentation is essential for every software product and web service alike. We can help the user by being consistent and using clear and descriptive syntax, sure. But there is no real replacement for the good ol’ documentation pages. Some of the great examples: https://www.twilio.com/docs/api/rest/ https://developers.facebook.com/docs/ https://developers.google.com/maps/documentation/ and many others… There are many tools that can help you document your API, but don’t forget to add the human touch, only one human can properly understand another. For now at least (looking at you AI). :)

    Conclusion

    We went through many concepts of the REST API building and covered some of the top REST API best practices. These might seem a bit strange or overwhelming when served at once, but try making your own REST API. And try to implement some the REST API best practices you learned here. Make the tiniest API possible and see how it looks. You’ll be surprised how well it can turn out by just following these few practices. We have an ASP.NET Core Web API series in which we demonstrate these practices. Also if you are a C# developer check out our article on how to consume RESTful APIs in a few different ways. And with this said, I REST my case… kmhmh… because you know, on the court… oh nevermind :/ [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
    1127 0 0 0 81 0 0 82 https://www.code-maze.com/ 81 0 111 0 0 323 0 0 325 https://code-maze.com/ 323 0 652 0 0 704 https://code-maze.com/ 652 0 852 0 0 1400 0 0 1402 https://code-maze.com/ 1400 0 1909 http://www.codecoder.top/dotnet/asp-net-core-web-api-best-practice-guide.html 0 0
    ASP.NET Core Web API - Repository Pattern https://code-maze.com/net-core-web-development-part4/ Mon, 05 Feb 2018 05:40:33 +0000 https://code-maze.com/?p=1250 What is a Repository pattern and why should we use it? With the Repository pattern, we create an abstraction layer between the data access and the business logic layer of an application. By using it, we are promoting a more loosely coupled approach to access our data from the database. Also, the code is cleaner and easier to maintain and reuse. Data access logic is in a separate class, or sets of classes called a repository, with the responsibility of persisting the application's business model. Implementing the repository pattern is our topic for this post. Additionally, this article has a strong relationship with EF Core, so we strongly recommend reading our EF Core tutorial to get familiar or just a better understanding of that topic. So let's start. [sc name="part_of_series" headline="This article is part of the series"] If you want to see all the basic instructions and complete navigation for this series, please follow the following link: Introduction page for this tutorial. For the previous part check out: Creating .NET Core WebApi project - Custom logging in .NET Core The source code is available for download at .NET Core, Angular and MySQL. Part 4 - Source Code This post is divided into several sections:

    Creating Models

    Let's begin by creating a new Class Library (.NET Core) project named Entities and inside it a new folder with the name Models, which will contain all the model classes. Model classes will represent the tables inside the database and will serve us to map the data from the database to the .NET Core. After that, we should reference this project to the main project. In the Models folder, we are going to create two classes and modify them:
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace Entities.Models
    {
        [Table("owner")]
        public class Owner
        {
            public Guid OwnerId { get; set; }
    
            [Required(ErrorMessage = "Name is required")]
            [StringLength(60, ErrorMessage = "Name can't be longer than 60 characters")]
            public string Name { get; set; }
    
            [Required(ErrorMessage = "Date of birth is required")]
            public DateTime DateOfBirth { get; set; }
    
            [Required(ErrorMessage = "Address is required")]
            [StringLength(100, ErrorMessage = "Address cannot be loner then 100 characters")]
            public string Address { get; set; }
    
            public ICollection<Account> Accounts { get; set; }
        }
    }
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace Entities.Models
    {
        [Table("account")]
        public class Account
        {
            public Guid AccountId { get; set; }
    
            [Required(ErrorMessage = "Date created is required")]
            public DateTime DateCreated { get; set; }
    
            [Required(ErrorMessage = "Account type is required")]
            public string AccountType { get; set; }
    
            [Required(ErrorMessage = "Owner Id is required")]
            public Guid OwnerId { get; set; }
    
           [ForeignKey(nameof(Owner))]
            public Guid OwnerId { get; set; }
            public Owner Owner { get; set; }
        }
    }
    As you can see, there are two models decorated with the attribute Table(“tableName”). This attribute will configure the corresponding table name in the database. All the mandatory fields have the attribute [Required] and if we want to constrain the strings, we can use the[StringLength]attribute In the Owner class, we have the Accounts property which suggests that one Owner is related to multiple Accounts. Additionally, we add the OwnerId and the Owner properties decorated with the [ForeignKey] attribute to state that one Account is related to only one Owner. If you want to learn more about the EF Core configuration, and we strongly suggest you do, visit the Configuring Nonrelational Properties in EF Core.

    Context Class and the Database Connection

    Now, let us create the context class, which will be a middleware component for the communication with the database. It has DbSet properties that contain the table data from the database. For a better understanding of the Context class and DbSet properties and how they work with EF Core overall, you can read Getting Started with EF Core article. In the root of the Entities project, we are going to create the RepositoryContext class and modify it:
    using Entities.Models;
    using Microsoft.EntityFrameworkCore;
    
    namespace Entities
    {
        public class RepositoryContext: DbContext
        {
            public RepositoryContext(DbContextOptions options)
                :base(options)
            {
            }
    
            public DbSet<Owner> Owners { get; set; }
            public DbSet<Account> Accounts { get; set; }
        }
    }
    Pay attention that you have to install Microsoft.EntityFrameworkCore package. To enable communication between the .NET core part and the MySQL database, we have to install a third-party library named Pomelo.EntityFrameworkCore.MySql. In the main project, we can install it with the NuGet package manager or Package manager console (It is important to Include prerelease because it is still in the prerelease state). After the installation, let's open the appsettings.json file and add DB connection settings inside:
    {
      "Logging": {
        "LogLevel": {
          "Default": "Warning"
        }
      },
      "mysqlconnection": {
        "connectionString": "server=localhost;userid=root;password=yourpass;database=accountowner;"
      },
      "AllowedHosts": "*"
    }
    In the ServiceExtensions class, we are going to write the code for configuring the MySQL context. First, let's add the using directives and then add the method ConfigureMySqlContext:
    using Microsoft.Extensions.Configuration;
    using Microsoft.EntityFrameworkCore;
    
    public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration config)
    {
        var connectionString = config["mysqlconnection:connectionString"];
        services.AddDbContext<RepositoryContext>(o => o.UseMySql(connectionString));
    }
    
    With the help of the IConfiguration config parameter, we can access the appsettings.json file and take all the data we need from it. Afterward, in the Startup class in the ConfigureServices method, add the context service to the IOC right above the services.AddControllers():
    services.ConfigureMySqlContext(Configuration);

    Repository Pattern Logic

    After establishing a connection with the database, it is time to create the generic repository that will serve us all the CRUD methods. As a result, all the methods can be called upon any repository class in our project. Furthermore, creating the generic repository and repository classes that use that generic repository is not going to be the final step. We will go a  step further and create a wrapper around repository classes and inject it as a service. Consequently,  we can instantiate this wrapper once and then call any repository class we need inside any of our controllers. You will understand the advantages of this wrapper when we use it in the project. First, let’s create an interface for the repository inside the Contracts project:
    namespace Contracts
    {
        public interface IRepositoryBase<T>
        {
            IQueryable<T> FindAll();
            IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression);
            void Create(T entity);
            void Update(T entity);
            void Delete(T entity);
        }
    }
    Right after the interface creation, we are going to create a new Class Library (.NET Core) project with the name Repository (add the reference from Contracts and Entities to this project), and inside the Repository project create the abstract class RepositoryBase which will implement the interface IRepositoryBase. Reference this project to the main project too.  Let's add the following code to the RepositoryBase class:
    using Contracts;
    using Entities;
    using Microsoft.EntityFrameworkCore;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    
    namespace Repository
    {
        public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class
        {
            protected RepositoryContext RepositoryContext { get; set; }
    
            public RepositoryBase(RepositoryContext repositoryContext)
            {
                this.RepositoryContext = repositoryContext;
            }
    
            public IQueryable<T> FindAll()
            {
                return this.RepositoryContext.Set<T>().AsNoTracking();
            }
    
            public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
            {
                return this.RepositoryContext.Set<T>().Where(expression).AsNoTracking();
            }
    
            public void Create(T entity)
            {
                this.RepositoryContext.Set<T>().Add(entity);
            }
    
            public void Update(T entity)
            {
                this.RepositoryContext.Set<T>().Update(entity);
            }
    
            public void Delete(T entity)
            {
                this.RepositoryContext.Set<T>().Remove(entity);
            }
        }
    }
    This abstract class, as well as IRepositoryBase interface, uses generic type T to work with. This type T gives even more reusability to the RepositoryBase class. That means we don’t have to specify the exact model (class) right now for the RepositoryBase to work with, we are going to do that later on.

    Repository User Classes

    Now that we have the RepositoryBase class, let's create the user classes that will inherit this abstract class. Every user class will have its own interface, for additional model-specific methods. Furthermore, by inheriting from the RepositoryBase class they will have access to all the methods from the RepositoryBase. This way, we are separating the logic, that is common for all our repository user classes and also specific for every user class itself. Let’s create interfaces in the Contracts project for our Owner and Account classes. Don't forget to add a reference from the Entities project. As soon as we do this, we can delete the Entities reference from the main project because it is now provided through the Repository project that already has the Contracts project referenced, with the Entities reference inside.
    using Entities.Models;
    
    namespace Contracts
    {
        public interface IOwnerRepository : IRepositoryBase<Owner>
        {
        }
    }
    
    using Entities.Models;
    
    namespace Contracts
    {
        public interface IAccountRepository : IRepositoryBase<Account>
        {
        }
    }
    Now, let's  create a repository user classes in the Repository project:
    using Contracts;
    using Entities;
    using Entities.Models;
    
    namespace Repository
    {
        public class OwnerRepository : RepositoryBase<Owner>, IOwnerRepository
        {
            public OwnerRepository(RepositoryContext repositoryContext)
                :base(repositoryContext)
            {
            }
        }
    }
    
    using Contracts;
    using Entities;
    using Entities.Models;
    
    namespace Repository
    {
        public class AccountRepository : RepositoryBase<Account>, IAccountRepository
        {
            public AccountRepository(RepositoryContext repositoryContext)
                :base(repositoryContext)
            {
            }
        }
    }
    After these steps, we are finished with creating the repository and repository user classes. But there are still more things to be done.

    Creating a Repository Wrapper

    Let's imagine if inside a controller we need to collect all the Owners and to collect only the certain Accounts (for example Domestic ones). We would need to instantiate OwnerRepository and AccountRepository classes and then call the FindAll and FindByCondition methods. Maybe it's not a problem when we have only two classes, but what if we need logic from 5 different classes or even more. Having that in mind, let's create a wrapper around our repository user classes. Then place it into the IOC and finally inject it inside the controller’s constructor. Now, with that wrappers instance, we may call any repository class we need. Let's start by creating a new interface in the Contract project:
    namespace Contracts
    {
        public interface IRepositoryWrapper
        {
            IOwnerRepository Owner { get; }
            IAccountRepository Account { get; }
            void Save();
        }
    }
    After that, we are going to add a new class to the Repository project:
    using Contracts;
    using Entities;
    
    namespace Repository
    {
        public class RepositoryWrapper : IRepositoryWrapper
        {
            private RepositoryContext _repoContext;
            private IOwnerRepository _owner;
            private IAccountRepository _account;
    
            public IOwnerRepository Owner {
                get {
                    if(_owner == null)
                    {
                        _owner = new OwnerRepository(_repoContext);
                    }
    
                    return _owner;
                }
            }
    
            public IAccountRepository Account {
                get {
                    if(_account == null)
                    {
                        _account = new AccountRepository(_repoContext);
                    }
    
                    return _account;
                }
            }
    
            public RepositoryWrapper(RepositoryContext repositoryContext)
            {
                _repoContext = repositoryContext;
            }
    
            public void Save()
            {
                _repoContext.SaveChanges();
            }
        }
    }
    As you can see, we are creating properties that will expose the concrete repositories and also we have the Save() method to be used after all the modifications are finished on a certain object. This is a good practice because now we can, for example, add two owners, modify two accounts and delete one owner, all in one method, and then just call the Save method once. All changes will be applied or if something fails, all changes will be reverted:
    _repository.Owner.Create(owner);
    _repository.Owner.Create(anotheOwner);
    _repository.Account.Update(account);
    _repository.Account.Update(anotherAccount);
    _repository.Owner.Delete(oldOwner);
    
    _repository.Save();
    In the ServiceExtensions class, we are going to add this code:
    public static void ConfigureRepositoryWrapper(this IServiceCollection services)
    {
        services.AddScoped<IRepositoryWrapper, RepositoryWrapper>();
    }
    And in the Startup class inside the ConfigureServices method, above the services.AddControllers() line, add this code:
    services.ConfigureRepositoryWrapper();
    Excellent.

    Testing

    All we have to do is to test this code the same way we did with our custom logger in part3 of this series. Inject the RepositoryWrapper service inside the WeatherForecast controller and call any method from the RepositoryBase class:
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        private IRepositoryWrapper _repoWrapper;
    
        public ValuesController(IRepositoryWrapper repoWrapper)
        {
            _repoWrapper = repoWrapper;
        }
        // GET api/values
        [HttpGet]
        public IEnumerable<string> Get()
        {
            var domesticAccounts = _repoWrapper.Account.FindByCondition(x => x.AccountType.Equals("Domestic"));
            var owners = _repoWrapper.Owner.FindAll();
    
            return new string[] { "value1", "value2" };
        }
    }
    Place the breakpoint inside the Get() method and you'll see the data returned from the database. In the next part, we are going to show you how to restrict access to the RepositoryBase methods from the controller, if you don't want them to be exposed here. We have created our Repository Pattern synchronously but it could be done asynchronously as well. If you want to learn how to do that you can visit Implementing Async Repository in .NET Core. Although we strongly recommend finishing all the parts from this series for an easier understanding of the project's business logic.

    Conclusion

    The Repository pattern increases the level of abstraction in your code. This may make the code more difficult to understand for developers who are unfamiliar with the pattern. But once you are familiar with it, it will reduce the amount of redundant code and make the logic much easier to maintain. In this post you have learned:
    • What is repository pattern
    • How to create models and model attributes
    • How to create context class and database connection
    • The right way to create repository logic
    • And the way to create a wrapper around your repository classes
    Thank you all for reading this post and I hope you read some useful information in it. See you soon in the next article, where we will use repository logic to create HTTP requests. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
    1250 0 0 0 96 0 0 98 96 0 100 98 0 101 100 0 1082 If the primary key value is not set then it will be tracked in the Microsoft.EntityFrameworkCore.EntityState.Added state. In other words If on the Map() method I pass everything needed apart from the primary key and let the GetOwnerById() (which is called before Map()) function do it's job. Then Update is enough for me.]]> 1081 0 183 0 0 187 183 0 236 0 0 238 236 0 243 0 0 244 243 0 245 244 0 246 245 0 252 246 0 310 0 0 311 310 0 338 0 0 339 own.Name).ToList(); Or if you want to keep only IEnumerable than just don't call ToList() method. So as you can see, this is just a simple LINQ query, nothing more than that. And I want to tell you, there is no silly questions and I have none to forgive you. I write this articles so I could help readers and answer the comments for the same point. If this reply helps you, than I am satisfied for sure :D So just keep on working and asking questions all over the internet :D]]> 338 0 341 FindAll() { return this.RepositoryContext.Set(); } And public void Save() { this.RepositoryContext.SaveChanges(); } Error CS1061 'RepositoryContext' does not contain a definition for 'Set' and no accessible extension method 'Set' accepting a first argument of type 'RepositoryContext' has been located (a using directive or an assembly reference is- it's missing?) Repository D: code WebAppProthese Repository RepositoryBase.cs And Error CS1061 'RepositoryContext' does not contain a definition for 'SaveChanges' and no 'SaveChanges' accessible extension method that accepts a first argument of type 'RepositoryContext' has been located (a using directive or an assembly reference is- it's missing?) Repository D: code WebAppProthese Repository RepositoryBase.cs Thnx]]> 0 0 343 341 0 345 339 0 346 343 0 347 345 0 348 346 0 350 348 0 352 350 0 356 347 0 388 352 0 407 ();) 2) Why don't you instance the context inside the RepositoryWrapper? (you created outside and pass it as constructor parameter) 3) If I need to query multiple databases, how can I pass connection string to RepositoryContext? Thanks again!]]> 0 0 408 (o => o.UseMySql(connectionString)); 2) You don't have to instantiate RepositoryContext class. With the code above, you have registered it inside the IOC and now you can include it in any class by providing its object in the constructor. That's the main advantage of the IOC and DI, not only in .NET Core but in .NET overall if you implement IOC support (just in .NET Core it is implemented by default). 3) If you need multiple databases it is best to create separate Context classes for each of them and than just to include the one you need inside any constructor you want. But if you want just to split your connection strings between development and production environment, you can have one context class but you must use environment object in .NET Core to provide information about your current environment: public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { //register context with development connection } else { //register context with different connection string. } } Hope my answers helped you. All the best.]]> 407 0 409 408 0 416 0 0 417 416 0 542 0 0 543 542 0 612 0 0 618 0 0 621 0 0 640 0 0 642 640 0 643 642 0 648 0 0 651 648 0 680 0 0 687 public interface IRepositoryBase< T>. Your error is not related to the EF Core.]]> 612 0 691 621 0 693 618 0 706 651 0 733 693 0 775 691 0 776 0 0 777 776 0 778 777 0 781 https://www.facebook.com/app_scoped_user_id/10209163592063048 0 0 782 778 0 783 781 0 1083 1082 0 838 0 0 839 838 0 882 0 0 883 882 0 884 883 0 913 0 0 915 913 0 916 915 0 917 916 0 918 916 0 936 918 0 937 936 0 949 0 0 950 949 0 953 0 0 954 953 0 955 954 0 965 https://www.facebook.com/app_scoped_user_id/10209163592063048 0 0 967 965 0 1072 0 0 1073 1072 0 1080 AddRange/UpdateRange method which I believe its the below solution. Is it good practice to use inside a new CreateRange/UpdateRange method in order to save collection of models or is there a better way. Also do you have any Idea what is happening to AddOrUpdate method from EF Core. I need to create an Upsert method to the User Repositories.]]> 0 0 1081 1080 0 1084 1083 0 1085 1084 0 1087 0 0 1088 0 0 1089 1088 0 1092 1089 0 1121 0 0 1122 1121 0 1128 1122 0 1183 0 0 1185 1183 0 1186 1185 0 1189 0 0 1190 1189 0 1198 p.Id == test.Id).AsQueryable() .Include(p => p.CandidateLanguages) .SingleOrDefault(); // Update parent RepositoryContext.Entry(existingParent).CurrentValues.SetValues(test); // Delete children foreach (var existingChild in existingParent.CandidateLanguages.ToList()) { if (!test.CandidateLanguages.Any(c => c.Id == existingChild.Id)) RepositoryContext.CandidateLanguages.Remove(existingChild); } // Update and Insert children foreach (var childModel in test.CandidateLanguages) { var existingChild = existingParent.CandidateLanguages .Where(c => c.Id == childModel.Id) .SingleOrDefault(); if (existingChild != null) // Update child RepositoryContext.Entry(existingChild).CurrentValues.SetValues(childModel); else { // Insert child var newChild = new CandidateLanguage { LangId = childModel.LangId, Mothertongue = childModel.Mothertongue }; existingParent.CandidateLanguages.Add(newChild); } } Update(test); RepositoryContext.SaveChanges(); } Or can you please can suggest a better way of updating the child collection before or after the parent update. Got the sample from https://stackoverflow.com/questions/27176014/how-to-add-update-child-entities-when-updating-a-parent-entity-in-ef]]> 0 0 1199 !test.CandidateLanguages.Select(t => t.Id).Contains(e.Id)); _context.CandidateLanguages.RemoveRange(forDelete); this would find all the collection objects from db that don't exists in the Test.Candidates collection and set the state of all these objects to Deleted. Finally you can do the other way around to find all those that exists in Test.CandidateLanguages and don't exist in existingParent.CandidateLanguages and for all these objects to call AddRange to set their state to Added. After that you call SaveChanges(). Again I haven't test this or anything, but I believe it should work and it is exactly how things should be done.]]> 1198 0 1206 1190 0 1207 0 0 1415 0 0 1255 1207 0 1256 var owners= context.Owners .FromSql("EXECUTE dbo.GetSomeOwners") .ToList(); You can use this statement in the owner repository class. About the master detail, I can't see any problem with that. Every entity has its own repository user class which inherits from the RepositoryBase class, so the specific logic for specific entities you need to place into the specific repository classes.]]> 1255 0 1289 0 0 1290 1289 0 1302 1290 0 1416 1415 0 1417 1416 0 1429 Context property in my RepositoryBase class. The error happens at the call of the 'FindAll()' method. My context class is never instantiated and always null when it's being passed through all the constructors. Any idea what may be causing this? I'm using Sql local db, the data is there. I used migrations in the Entities project to seed the database. I configure StartUp class services like this: services.AddScoped (); services.AddDbContext(opts => opts.UseSqlServer(Configuration["ConnectionString:RepoDB"])); Thanks, hope you have enough information. I do have been doing the tutorial with different models, but the logic is all the same.]]> 0 0 1430 1429 0 1431 1429 0 1476 System.ComponentModel.Win32Exception (53): The network path was not found]]> 0 0 1478 "Verify that the instance name is correct and that SQL Server is configured to allow remote connections". You can google it and try to find the solution. But for now you can check your connection string and whether your server allows remote connections. It would take too long for me to explain how to do that here, so that's why I said to google it a bit.]]> 1476 0 1479 1478 0 1582 The program '[21432] iisexpress.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'. I tried injecting the context class directly into the controller and using it to query the database y everything works fine. So i belive the issue came when using the repository pattern architecture. I realy need a hand on this!]]> 0 0 1583 1582 0 1911 Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Contracts.IRepositoryWrapper Lifetime: Scoped ImplementationType: Repository.RepositoryWrapper': Unable to resolve service for type 'Entities.RepositoryContext' while attempting to activate 'Repository.RepositoryWrapper'.) Exception 1: InvalidOperationException: Error while validating the service descriptor 'ServiceType: Contracts.IRepositoryWrapper Lifetime: Scoped ImplementationType: Repository.RepositoryWrapper': Unable to resolve service for type 'Entities.RepositoryContext' while attempting to activate 'Repository.RepositoryWrapper'. Exception 2: InvalidOperationException: Unable to resolve service for type 'Entities.RepositoryContext' while attempting to activate 'Repository.RepositoryWrapper'. Been trying to fix it the last couple of hours, but i'm at a loss of how to fix this. Anyone experience anything similar? Any help would be highly appreciated!]]> 0 0 1912 1911 0 1914 1912 0 1916 1914 0 1917 1916 0 1918 1917 0 1919 1918 0 1920 1919 0
    How to Implement Content Negotiation in ASP.NET Core Web API https://code-maze.com/content-negotiation-dotnet-core/ Wed, 14 Feb 2018 08:56:03 +0000 https://code-maze.com/?p=1377 Top REST API best practices article. Content negotiation is an HTTP feature that has been around for a while, but for one reason or another, it is, maybe, a bit underused. In short, content negotiation lets you choose or rather "negotiate" the content you want in to get in response to the REST API request. If you want to learn how content negotiation works behind the scenes, you can download our Complete Guide to HTTP Book for free and look it up in the advanced features section. Today, we are going through the content negotiation implementation in ASP.NET Core. We are going to talk about: So let's get down to it.

    What Do You Get out of the Box?

    By default, ASP.NET Core Web API returns a JSON formatted result. Let's make a default Web API project and remove the default ValuesController. Instead, we are going to make our own controller (with blackjack and hookers), BlogController with only one method:
    [Route("api/[controller]")]
    public class BlogController : Controller
    {
    	public IActionResult Get()
    	{
    		var blogs = new List<Blog>();
    		var blogPosts = new List<BlogPost>();
    		
    		blogPosts.Add(new BlogPost
    		{
    			Title = "Content negotiation in .NET Core",
    			MetaDescription = "Content negotiation is one of those quality-of-life improvements you can add to your REST API to make it more user-friendly and flexible. And when we design the API, isn't that what we want to achieve in the first place?",
    			Published = true
    		});
    
    		blogs.Add(new Blog()
    		{
    			Name = "Code Maze",
    			Description = "A practical programmers resource",
    			BlogPosts = blogPosts
    		});
    
    		return Ok(blogs);
    	}
    }
    Things to note about this simple example:
    • We are using two classes: Blog and BlogPosts to create an object to return as a response object
    • We are utilizing the IActionResult interface provided by ASP.NET Core as a generic return type for different types of responses our methods might have
    • The object creation logic is in the controller. You should not implement your controllers like this; this is just for the sake of simplicity
    • We are returning the result with the Ok helper method which returns the object and the status code 200 OK

    How to Use Postman to Test Your API

    Postman is a nice little tool you can use to test your APIs easily. Now, let's try calling the method using Postman and see what we get as a response. content negotiation json You can clearly see that the default result when calling GET on /api/blog returns our JSON result. Those of you with sharp eyes might have even noticed that we used the Accept header to try forcing the server to return other media types like plain text and XML. But that doesn't work. Why? Because we need to configure server formatters to format a response the way we want it. Let's see how to do that.

    Changing the Default Configuration of Our Project

    A server does not explicitly specify where it formats a response to JSON. But you can override it by changing configuration options through the AddMvc method options. By default, it looks like this:
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }
    We can add the following options to enable the server to format the XML response when the client tries negotiating for it.
    public void ConfigureServices(IServiceCollection services)
    {
    	services.AddMvc(config =>
    	{
    		// Add XML Content Negotiation
    		config.RespectBrowserAcceptHeader = true;
    		config.InputFormatters.Add(new XmlSerializerInputFormatter());
    		config.OutputFormatters.Add(new XmlSerializerOutputFormatter());
    	});
    }
    First things first, we must tell a server to respect the Accept header. After that, we can add XML formatters to enable the XML formatting. XmlSerializerInputFormatter and XmlSerializerOutputFormatter are both part of the Microsoft.AspNetCore.Mvc.Formatters, so we need to add a reference to that library. Now that we have our server configured let's test the content negotiation once more.

    Testing Content Negotiation

    Let's see what happens now if we fire the same request through Postman. content negotiation xml There is our XML response. That was easy, wasn't it? Now by changing the Accept header from text/xml to text/json, we can get differently formatted responses which is awesome, wouldn't you agree? Ok, that was nice and easy. But what if despite all this flexibility a client requests a media type that a server doesn't know how to format?

    Restricting Media Types

    Currently, it will default to a JSON type. But you can restrict this behavior by adding one line to the configuration.
    public void ConfigureServices(IServiceCollection services)
    {
    	services.AddMvc(config =>
    	{
    		config.RespectBrowserAcceptHeader = true;
    		config.ReturnHttpNotAcceptable = true;
    
    		config.InputFormatters.Add(new XmlSerializerInputFormatter());
    		config.OutputFormatters.Add(new XmlSerializerOutputFormatter());
    	});
    }
    We added the ReturnHttpNotAcceptable = true option, which tells the server that if the client tries to negotiate for the media type the server doesn't support, it should return the 406 Not Acceptable status code. This will make your application more restrictive and force the API consumer to request only the types the server supports. The 406 status code is created for this purpose. You can find more details about that in our Complete Guide to HTTP book, or if you want to go even deeper you can check out the RFC2616. Now, let's try fetching the text/css media type using Postman to see what happens. content negotiation request not acceptable And as expected, there is no response body, and all we get is a nice 406 Not Acceptable status code. So far so good.

    More About Formatters

    Let's imagine you are making a public REST API and it needs to support content negotiation for a type that is not "in the box". Rare as it might occur, you need to have a mechanism to do this. So, how can you do that? ASP.NET Core supports the creation of custom formatters. Their purpose is to give you the flexibility to create your own formatter for any media types you need to support. We can make the custom formatter using the following method:
    • Create an output formatter class that inherits the TextOutputFormatter class
    • Create an input formatter class that inherits the TextInputformatter class
    • Add input and output classes to InputFormatters and OutputFormatters collections the same way as we did for the XML formatter
    Now let's have some fun and implement a custom CSV formatter for our example.

    Implementing a Custom Formatter

    Since we are only interested in formatting responses in this article, we need to implement only an output formatter. We would need an input formatter only if a request body contained a corresponding type. The idea is to format a response to return the list of blogs and their corresponding list of blog posts in a CSV format. Let's add a CsvOutputFormatter class to our project.
    public class CsvOutputFormatter : TextOutputFormatter
    {
    	public CsvOutputFormatter()
    	{
    		SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv"));
    		SupportedEncodings.Add(Encoding.UTF8);
    		SupportedEncodings.Add(Encoding.Unicode);
    	}
    
    	protected override bool CanWriteType(Type type)
    	{
    		if (typeof(Blog).IsAssignableFrom(type) || typeof(IEnumerable<Blog>).IsAssignableFrom(type))
    		{
    			return base.CanWriteType(type);
    		}
    
    		return false;
    	}
    
    	public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
    	{
    		var response = context.HttpContext.Response;
    		var buffer = new StringBuilder();
    
    		if (context.Object is IEnumerable<Blog>)
    		{
    			foreach (var Blog in (IEnumerable<Blog>)context.Object)
    			{
    				FormatCsv(buffer, Blog);
    			}
    		}
    		else
    		{
    			FormatCsv(buffer, (Blog)context.Object);
    		}
    
    		using (var writer = context.WriterFactory(response.Body, selectedEncoding))
    		{
    			return writer.WriteAsync(buffer.ToString());
    		}
    	}
    
    	private static void FormatCsv(StringBuilder buffer, Blog blog)
    	{
    		foreach (var blogPost in blog.BlogPosts)
    		{
    			buffer.AppendLine($"{blog.Name},\"{blog.Description},\"{blogPost.Title},\"{blogPost.Published}\"");
    		}
    	}
    }
    There are a few things to note here:
    • In the constructor, we define which media type this formatter should parse as well as encodings
    • The CanWriteType method is overridden, and it indicates whether or not the Blog type can be written by this serializer.
    • The WriteResponseBodyAsync method that constructs the response
    • And finally, we have the FormatCsv method that formats a response the way we want it.
    The class is pretty straightforward to implement, and the main thing that you should focus on is the FormatCsv method logic. Now, we just need to add the newly made formatter to the list of OutputFormatters in the AddMvcOptions.
    public void ConfigureServices(IServiceCollection services)
    {
    	services.AddMvc(config =>
    	{
    		config.RespectBrowserAcceptHeader = true;
    		config.ReturnHttpNotAcceptable = true;
    
    		config.InputFormatters.Add(new XmlSerializerInputFormatter());
    		config.OutputFormatters.Add(new XmlSerializerOutputFormatter());
    		config.OutputFormatters.Add(new CsvOutputFormatter());
    	});
    }
    Now let's run this and see if it actually works. This time we will put the text/csv as the value for the Accept header. content negotiation csv Well, what do you know, it works! Since we only have one blog and one blog post in our example, there is only one line in the response. You can play around with source code to see what happens when you add more blogs and blog posts. There is a great page about custom formatters in ASP.NET Core if you want to learn more about them. You can also check out the implementation of the input and output formatters for the vcard content type if you need more examples.

    A Note About .NET Core 2.2

    If you are using .NET Core 2.2 or latest XmlSerializerInputFormatter should be marked as deprecated. A better way would be to implement XML serializers like this:
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(config =>
        {
            config.RespectBrowserAcceptHeader = true;
            config.ReturnHttpNotAcceptable = true;
    
            config.OutputFormatters.Add(new CsvOutputFormatter());
        }).AddXmlSerializerFormatters().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }
    Instead of explicitly defining XML serializers as we did before, in the .NET Core 2.2 we can add them simply by calling AddXmlSerializerFormatters() method which will do the job now. This is the preferred way of defining XML serializers since .NET Core 2.2 because otherwise, we could get serialization problems in some cases. You can read more on this issue if you want to see what the potential consequences are (besides deprecation in future versions of .NET Core). Modified source code for the .NET Core 2.2 can be found on the Content Negotiation examples 2.2 branch on GitHub.

    Consuming API Programmatically

    Up until now, we have used Postman to play around with the example. But, I feel you need to try out to consume some REST APIs using content negotiation we described here by making some requests programmatically instead of using the third party tool. For that purpose, we have laid out a few great ways to consume RESTful API. You can find some of the best tools that .NET provides to consume any REST API. Be sure to check it out and try consuming some APIs.

    Conclusion

    In this blog post, we went through a concrete implementation of the content negotiation mechanism in an ASP.NET Core project. We have learned about formatters and how to make a custom one, and how to set them up in your project configuration as well. We have also learned how to restrict an application only to certain content types, and not accept any others. You should be able both to design and consume REST APIs using content negotiation now. It really is a great mechanism, and we have great tools to implement it in our projects, easily. So, there are no excuses! If you want to play around with the source code, you can find it here: Download source code from GitHub. Thanks for reading and please leave a comment in the comment section. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
    1377 0 0 0 354 0 0 355 https://code-maze.com/ 354 0 611 services.AddMvc(config=>{ ... stuff here ...}).AddXmlSerializerFormatters().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);]]> 0 0 692 https://code-maze.com/ 611 0
    ASP.NET Core Web API - How to Handle Get Request https://code-maze.com/net-core-web-development-part5/ Mon, 12 Feb 2018 07:17:19 +0000 https://code-maze.com/?p=1406 In the previous post, we have created a repository pattern for collecting the data from the database. Now, it is time to use that repository for the business logic. We are going to keep all the database logic inside the repository classes. Controllers will be responsible for handling requests, model validation and returning responses to the frontend part of the application. By doing so, our controllers won't be overwhelmed with the code thus making the code easier to read and maintain as well. [sc name="part_of_series" headline="This article is part of the series"] If you want to see all the basic instructions and complete navigation for this series, please follow the following link: Introduction page for this tutorial. For the previous part check out: Creating .NET Core WebApi project - Repository pattern in .NET Core The source code is available for download at .NET Core, Angular and MySQL. Part 5 - Source Code This post is divided into several sections:

    Controllers and Routing in WEB API

    To create a controller, right-click on the Controllers folder inside the main project and Add/Controller. Then from the menu choose API Controller - Empty and name it OwnerController.cs: Adding owner controller Our controller should look like this:
    using Microsoft.AspNetCore.Mvc;
    
    namespace AccountOwnerServer.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class OwnerController : ControllerBase
        {       
        }
    }
    
    Every web API controller class inherits from the ControllerBase abstract class that provides all the necessary behavior for the derived class. Also, above the controller class we can see this part of code:
    [Route("api/[controller]")]
    This represents the routing and we will talk a little bit about the routing inside Web APIs. Web API routing routes the incoming HTTP requests to the particular action method inside the Web API controller. There are two types of routings:
    1. Convention based routing and
    2. Attribute routing
    Convention based routing is called that way because it establishes a convention for the URL paths. The first part makes the mapping for the controller name, the second part makes the mapping for the action method and the third part is used for the optional parameter. We can configure this type of routing in the Startup class in the Configure method: convention based routing http get requests Attribute routing uses the attributes to map the routes directly to the action methods inside the controller. Usually, we place the base rout above the controller class, as you can notice in our Web API controller class. Similarly, for the specific action methods, we create their routes right above them.

    GetAllOwners GET Request in .NET Core

    So let’s start. First, let's change the base route from: [Route("api/[controller]")] to: [Route("api/owner")]. Even though the first rout will work just fine, with the second example we are more specific to show that this routing should point to the OwnerController. Now it is time to create the first action method to return all the owners from the database. In the IOwnerRepository interface create a definition for theGetAllOwners method :
    public interface IOwnerRepository
    {
        IEnumerable<Owner> GetAllOwners();
    }
    
    Then implement that interface inside the OwnerRepository class:
    namespace Repository
    {
        public class OwnerRepository : RepositoryBase<Owner>, IOwnerRepository
        {
            public OwnerRepository(RepositoryContext repositoryContext)
                :base(repositoryContext)
            {
            }
    
            public IEnumerable<Owner> GetAllOwners()
            {
                return FindAll()
                    .OrderBy(ow => ow.Name)
                    .ToList();
            }
        }
    }
    
    Finally, we need to return all the owners by using the GetAllOwners method inside the Web API action. The purpose of the action methods, inside Web API controllers, is not only to return the results. It is the main purpose, but not the only one. You need to pay attention to the status codes of your Web API responses as well. Additionally, you’ll have to decorate your actions with the HTTP attributes which will mark the type of the HTTP request to that action. You can read more on HTTP and find some HTTP request examples in part 1 of our HTTP series. Finally, let's modify the OwnerController:
    using Contracts;
    using Microsoft.AspNetCore.Mvc;
    using System;
    
    namespace AccountOwnerServer.Controllers
    {
        [Route("api/owner")]
        [ApiController]
        public class OwnerController : ControllerBase
        {
            private ILoggerManager _logger;
            private IRepositoryWrapper _repository;
    
            public OwnerController(ILoggerManager logger, IRepositoryWrapper repository)
            {
                _logger = logger;
                _repository = repository;
            }
    
            [HttpGet]
            public IActionResult GetAllOwners()
            {
                try
                {
                    var owners = _repository.Owner.GetAllOwners();
    
                    _logger.LogInfo($"Returned all owners from database.");
    
                    return Ok(owners);
                }
                catch (Exception ex)
                {
                    _logger.LogError($"Something went wrong inside GetAllOwners action: {ex.Message}");
                    return StatusCode(500, "Internal server error");
                }
            }
        }
    }
    Let us explain this code a bit. First of all, we inject the logger and repository services inside the constructor. Then by decorating the GetAllOwners action with [HttpGet] attribute, we are mapping this action to the GET request. Finally, we use both injected parameters to log the messages and to get the data from the repository class. The IActionResult interface supports using a variety of methods, which return not only the result but the status codes as well. In this situation, the OK method returns all the owners and also the status code 200 which stands for OK. If an exception occurs, we will return the internal server error with the status code 500. You can read more about status codes by reading The HTTP series - References Because there is no route attribute right above the action, the route for the action GetAllOwners will be api/owner (http://localhost:5000/api/owner).

    Code Permissions and Testing the Result

    We would like to point out one more thing inside GetAllOwners action. Right now, if you look at the repository structure, its classes inherit from the abstract RepositoryBase<T> class and also from its own interface which then inherits from the IRepositoryBase<T> interface. With this hierarchy in place, by typing _repository.Owner. you are able to call the custom method from the OwnerRepository class and also all of the methods from the abstract RepositoryBase<T> class. If you want to avoid that type of behavior and to allow actions inside the controller to call only methods from the repository user classes, all you need to do is to remove IRepositoryBase<T> inheritance from IOwnerRepository. Consequently, only repository user classes will be able to call generic methods from RepositoryBase<T> class. Likewise, action methods communicate only with repository user classes. It is all up to you, how you want to organize your code and permissions. To check the result, we are going to use the Postman tool to send requests towards the application. Also, you can learn more about how to consume web API programmatically using C# by reading A few great ways to consume restful api in c#. Let's start the application, start the Postman and create a request: get all owners http get requests Excellent, everything is working as planned. Before we continue, we would like to show you one more thing. If you look at the model classes, you'll notice that all properties have the same name as the columns they are mapped to. But you can have the property with a different name than the column it points to, and still to map each other. To achieve that you need to use attribute [Column] So, let's do something like that. We are going to change the property names from AccountId and OwnerId to just Id in the Owner and Account classes. Also, we are going to add the [Column] property which will map the Id property to the right column in the database:
    [Table("Account")]
    public class Account
    {
        [Column("AccountId")]
        public Guid Id { get; set; }
    
        [Required(ErrorMessage = "Date created is required")]
        public DateTime DateCreated { get; set; }
    
        [Required(ErrorMessage = "Account type is required")]
        public string AccountType { get; set; }
    
        [Required(ErrorMessage = "Owner Id is required")]
        public Guid OwnerId { get; set; }
        
        public ICollection<Account> Accounts { get; set; }
    }
    [Table("Owner")]
    public class Owner
    {
        [Column("OwnerId")]
        public Guid Id { get; set; }
    
        [Required(ErrorMessage = "Name is required")]
        [StringLength(60, ErrorMessage = "Name can't be longer than 60 characters")]
        public string Name { get; set; }
    
        [Required(ErrorMessage = "Date of birth is required")]
        public DateTime DateOfBirth { get; set; }
    
        [Required(ErrorMessage = "Address is required")]
        [StringLength(100, ErrorMessage = "Address can not be loner then 100 characters")]
        public string Address { get; set; }
    
        [ForeignKey(nameof(Owner))]
        public Guid OwnerId { get; set; }
        public Owner Owner { get; set; }
    }
    Now let’s continue.

    Using DTO and AutoMapper

    DTO or Data Transfer Object serves the purpose to transfer data from the server to the client. That is exactly what are we going to use it for. If we take a look at the GetAllOwners action, we can see that we use the model Owner class to fetch the data from the database (_repository.Owner.GetAllOwners()returns a list of  Owner objects) and also to return that result to the client. And that is not a good practice. A much better practice is to have a model class to fetch the data from the database and to have a DTO class to return that result to the client. The DTO object could be exactly the same as the model object but still, it is much better to use DTO objects because if something changes in the database the model class must change but that doesn't mean that the client wants changed results. Thus the DTO object will not change. Having that said, let's create a new folder DataTransferObjects in the Entities project and let's create OwnerDto class inside:
    public class OwnerDto
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string Address { get; set; }
    }
    As you can see, we don't have the Accounts property, because we don't want to show that information to the client right now. Now, all we would have to do is to map a returned list of owners from the database to the list of ownerDto. But, doing that manually is a boring job and if we have twenty or even more properties in our DTO class, it would be time-consuming as well. Luckily for us, there is a great tool that could help us a lot in the mapping process. Yes, it is AutoMapper.

    Working with AutoMapper

    AutoMapper is a library that helps us mapping different objects. To install it, we have to type this command in the Package Manager Console window: PM> Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection After the installation, we have to register it in the ConfigureServices method in the Startup class: services.AddAutoMapper(typeof(Startup)); Now, we have to create a mapping profile class to tell AutoMapper how to execute mapping actions. So, let's create a new class MappingProfile in the main project and modify it:
    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            CreateMap<Owner, OwnerDto>();
        }
    }
    Finally, we can modify the OwnerController:
    public class OwnerController : ControllerBase 
    { 
        private ILoggerManager _logger; 
        private IRepositoryWrapper _repository;
        private IMapper _mapper;
            
        public OwnerController(ILoggerManager logger, IRepositoryWrapper repository, IMapper mapper) 
        { 
            _logger = logger; 
            _repository = repository;
            _mapper = mapper;
        }
            
        [HttpGet] 
        public IActionResult GetAllOwners() 
        { 
            try 
            { 
                var owners = _repository.Owner.GetAllOwners(); 
                _logger.LogInfo($"Returned all owners from database.");
    
                var ownersResult = _mapper.Map<IEnumerable<OwnerDto>>(owners);
                return Ok(ownersResult); 
            } 
            catch (Exception ex) 
            { 
                _logger.LogError($"Something went wrong inside GetAllOwners action: {ex.Message}"); 
                return StatusCode(500, "Internal server error"); 
            } 
        } 
    }
    We can send the same request from Postman and we are going to get the same result (without accounts), but now, with much better implementation. AutoMapper has great capabilities and you can learn more by reading Getting Started With AutoMapper in ASP.NET Core.

    GetOwnerById GET Request in .NET Core

    To continue, let's modify the IOwnerRepository interface:
    public interface IOwnerRepository
    {
        IEnumerable<Owner> GetAllOwners();
        Owner GetOwnerById(Guid ownerId);
    }
    
    Then, let's implement the interface in the OwnerRepository.cs:
    public Owner GetOwnerById(Guid ownerId)
    {
        return FindByCondition(owner => owner.Id.Equals(ownerId))
                .FirstOrDefault();
    }
    
    Finally, let's change the OwnerController:
    [HttpGet("{id}")]
    public IActionResult GetOwnerById(Guid id)
    {
        try
        {
            var owner = _repository.Owner.GetOwnerById(id);
    
            if (owner == null)
            {
                _logger.LogError($"Owner with id: {id}, hasn't been found in db.");
                return NotFound();
            }
            else
            {
               _logger.LogInfo($"Returned owner with id: {id}");
    
               var ownerResult = _mapper.Map<OwnerDto>(owner);
               return Ok(ownerResult); 
            }
       }
       catch (Exception ex)
       {
            _logger.LogError($"Something went wrong inside GetOwnerById action: {ex.Message}");
            return StatusCode(500, "Internal server error");
       }
    }
    We are going to use the Postman to send valid and invalid requests to check the results: valid postman request http get requests Invalid request: invalid request http get requests

    Owner Details Request

    Let’s continue by creating a logic to return the owner object with its account details. First, we need to create the AccountDto class:
    public class AccountDto
    {
        public Guid Id { get; set; }
        public DateTime DateCreated { get; set; }
        public string AccountType { get; set; }
    }
    Then, we have to modify our OwnerDto class that will help us return the owner with all related accounts to it. If you want you can create additional DTO class with name OwnerWithAccountsDto, but for the sake of simplicity, we are going to modify the existing DTO class:
    public class OwnerDto
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string Address { get; set; }
    
        public IEnumerable<AccountDto> Accounts { get; set; }
    }
    Notice the property Accounts which will bind all the accounts related to the certain owner.  Let’s modify the interface accordingly:
    public interface IOwnerRepository
    {
        IEnumerable<Owner> GetAllOwners();
        Owner GetOwnerById(Guid ownerId);
        Owner GetOwnerWithDetails(Guid ownerId);
    }
    
    Also, let's modify the repository class:
    public Owner GetOwnerWithDetails(Guid ownerId)
    {
        return FindByCondition(owner => owner.Id.Equals(ownerId))
            .Include(ac => ac.Accounts)
            .FirstOrDefault();
    }
    We are using the Include method to include all the accounts related to the current owner. We have to add two additional mapping rules in the MappingProfile class:
    public MappingProfile()
    {
        CreateMap<Owner, OwnerDto>();
    
        CreateMap<Account, AccountDto>();
    
        CreateMap<Owner, OwnerDto>();
    }
    Finally, let's modify the controller:
    [HttpGet("{id}/account")]
    public IActionResult GetOwnerWithDetails(Guid id)
    {
        try
        {
            var owner = _repository.Owner.GetOwnerWithDetails(id);
    
            if (owner == null)
            {
                _logger.LogError($"Owner with id: {id}, hasn't been found in db.");
                return NotFound();
            }
            else
            {
                _logger.LogInfo($"Returned owner with details for id: {id}");
                
                var ownerResult = _mapper.Map<OwnerDto>(owner);
                return Ok(ownerResult);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError($"Something went wrong inside GetOwnerWithDetails action: {ex.Message}");
            return StatusCode(500, "Internal server error");
        }
    }
    Result: ownerdetails http get request We have created these actions that use Repository Pattern logic synchronously but it could be done asynchronously as well. If you want to learn how to do that you can visit Implementing Async Repository in .NET Core. Although we strongly recommend finishing all the parts from this series for an easier understanding of the project's business logic.

    Conclusion

    Requests using GET should only retrieve the data from the database, and all the actions inside the OwnerController class are written like that. By reading this post you've  learned:
    • How to work with a controller class
    • What is routing and how to use it
    • How to handle GET requests in a web API
    • The way to use DTO's while handling requests
    Thank you all for reading and I hope you found something useful in it. In the next article, where we are going to apply the same principles learned here to support the POST, PUT and DELETE requests in our application. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
    1406 0 0 0 104 0 0 107 104 0 134 0 0 319 0 0 320 319 0 324 _signInManager; private readonly UserManager _userManager; private readonly IConfiguration _configuration; public AccountController( UserManager userManager, SignInManager signInManager, IConfiguration configuration ) { _userManager = userManager; _signInManager = signInManager; _configuration = configuration; } how ever I don't want to expose this class inside my controller, I have to provide the repository wrapper over this classes. How can provide repository wrapper over these classes. Can you guide my for this. So That one repository class will work for my both MVC and WebApi Application. Thanks in advance. Omkar.]]> 0 0 327 324 0 436 to it. public async Task GetOwnerWithDetailsAsync(int ownerId) { var owner = await GetOwnerByIdAsync(ownerId); return new OwnerExtended(owner) { Accounts = await RepositoryContext.Accounts .Where(a => a.OwnerId == ownerId).ToListAsync() // Add Nested List to the Accounts Here }; } Thanks again. Adam]]> 0 0 437 as navigation property inside the Accounts class then you could use Include() as well (something EF Core provides for you) or you can create AccountsExtended DTO and fill it up as we did with the OwnerExtended class. All the best.]]> 436 0 847 0 0 848 847 0 850 848 0 851 850 0 873 0 0 874 873 0 875 874 0 876 875 0 877 876 0 878 877 0 880 878 0 907 return new OwnerExtended(GetOwnerById(ownerId)) { Accounts = RepositoryContext.Accounts .Where(a => a.OwnerId == ownerId) }; This will result in n+1 queries, where n is the number of owners. So, if you want to get all owners with accounts you will get n + 1 queries. I'm trying to accomplish something similar: var jobsCategories = this.RepositoryContext.JobCategories.Select(c => new JobCategory(c) { OpenJobsCount = c.Jobs.Where(j => j.IsActive == true).Count(j => j.IsActive) }); So, if I have 10 categories, I get 11 queries 1st query: SELECT c.id, c.name FROM job_categories AS c And 1 query for each category (10 in total): SELECT COUNT(*)::INT FROM jobs AS j0 WHERE ((j0.is_active = TRUE) AND (j0.is_active = TRUE)) AND (@_outer_Id = j0.category_id) While in Sql one query will do the job: select c.id, c.name, (select count(*) from jobs as j where j.category_id = c.id AND j.is_active = true) as openJobs from job_categories as c; So, how to accomplish this in EF Core?]]> 0 0 908 907 0 1045 0 0 972 FemaleNames, MaleNames, ChildNames (Exact same DbSets properties). Now I want to expose the URL: GET names/{gender} and from that function it should be passing the string {gender}, then the repoWrapper should return the right I{Gender}Repository property and then use the function FindByCondition. I have implemented a factory method that uses the {gender} string and withing a case returns a type of . But FindByCondition is not in the scope. What am I doing wrong? Is the idea wrong in the first place? I am pretty sure you have met this issue before. Also note that I need the three diffrent I{Gender}Repositories in order to attach new specific functions. Example to understand better: [Route("name/{gender}")]public async Task GetGenderName(string gender){ code... response = await _repoWrapper.GenderFactory(gender).FindByConditionAsync(expression goes here); code... } ]]> 0 0 973 972 0 976 return FindAll().OrderBy(em => em.Name); Exception: entity type has composite primary key defined with data annotations]]> 0 0 978 976 0 980 Repository would solve that and would be a great example as well on the series]]> 973 0 981 980 0 986 0 0 987 978 0 988 986 0 989 988 0 1014 0 0 1015 1014 0 1019 1015 0 1049 1045 0 1053 1049 0 1054 1053 0 1250 0 0 1273 0 0 1274 1273 0 1301 0 0 1303 1301 0 1304 1303 0 1305 1304 0 1334 [HttpGet("{id}")] public IActionResult GetOwnerById(Guid id) { .... } and you can request it through `/api/owner/[guid id]` Inside my project I have to specify `[FromRoute]` in the method signature in the params list, otherwise I get a 404 response Am I missing some set ups?]]> 0 0 1335 1334 0 1336 // GET: api/addresses/{addressID} [HttpGet("{addressID}")] public async Task GetAddress([FromRoute] int addressID) { try { var address = await _Repository.GetAddressAsync(addressID); if (address != null) { return Ok(address); } return NotFound(new ApiError { Message = $"No address was found with this ID: {addressID}" }); } catch (Exception ex) { return BadRequest(new ApiError { Message = ex.Message }); } } I think it should be something to do with app.UseMvc() in my startup.cs I am Sorry for the bad indentation but it's pretty hard to share code through this "editor"]]> 1335 0 1337 1336 0 1340 1337 0 1342 1340 0 1349 /api/addresses/{addressID} */api/addresses/GetEmployeeFromAddress I know, these are not following REST principles but I needed the second URI for a specific purpose. The problem was that this type of URI : /api/addresses/5 was recognized as the same of : /api/addresses/GetEmployeeFromAddress because I did a wrong set up inside the [Http*] attributes, but specifying [FromRoute] did the work. Said that, I was sure that this was the framework's "default" behavior, and I kept adding [FromRoute] in the new Controllers, but actually If I remove them it works perfectly. Of course this is valid only where there is no routing double match I hope this help someone else too! Thanks Marinko]]> 1342 0 1353 1349 0 1362 public OwnerExtended GetOwnerWithDetails(int id) // this Id = Owner ID { return new OwnerExtended(GetOwnerById(id)) { Account = RepositoryContext.AccountTbl .Where(a => a.OwnerId == id) }; } How can I map Account to OwnerId == id if in the accountTbl doesnt have OwnerId in it. The relationship between OwnerTbl and AccountTbl is by using FK AccountType and PK AccountTypeId @AccountTbl?]]> 1305 0 1364 1362 0 1365 Fetch the owner object by Id and then find the account that matches the type inside that owner object Can you explain a bit more please?]]> 1364 0 1366 var owner = _context.Owners.SingleOrDefault(x => x.Id == 1); With this you have the owner object with Id 1 and Type SomeType. Now you can write something like this: var account = _context.Accounts.SingleOrDefault(a => a.Type == owner.AccountType); That way you can connect those two objects. Finally if you have a DTO object, as I suggested in one of previous comments, you could populate that object in a same manner for sure.]]> 1365 0 1373 public OwnerExtended GetOwnerWithDetails(int id) { var owner = RepositoryContext.OwnerTbl.SingleOrDefault(x => x.OwnerId == id); var accountDetails = RepositoryContext.AccountTbl.SingleOrDefault(p => p.AccountId == Owner.Account); return new OwnerExtended(GetOwnerById(id)) { Account = RepositoryContext.AccountTbl .Where(p => p.AccountId == accountDetails.AccountId) }; } ----- I want to go up one more level in this situation. Link/chain reference( Correct me if i'm wrong about naming) WHat If account table has references from different tables. For example, OwnerTbl reference from AccountTbl, AccountTbl reference from CreatedByTbl ( I made up a scenario, that we record who created what account type). And CreatedBy reference from EmployeeId which has EmpId to show who created that account type for the Owner.) What do you think in this case.]]> 1366 0 1376 1373 0 1382 1376 0 1384 1382 0 1493 0 0 1494 dotnet AccountOwnerServer.dll it started my app and I was able to send a request from a browser. https://uploads.disquscdn.com/images/74a8f6c9b340cc4cc924173b100101ed56919d58ebea6aadd40674a1b0a48c40.png]]> 1493 0 1496 1494 0 1497 1494 0 1498 1494 0
    ASP.NET Core Web API - Post, Put, Delete https://code-maze.com/net-core-web-development-part6/ Mon, 19 Feb 2018 06:33:40 +0000 https://code-maze.com/?p=1552
  • Creating a Database for Our Project
  • Basic Code Preparations
  • Logging with NLog in ASP.NET Core
  • Repository Pattern with Entity Framework Core
  • How to Handle Get Request
  • Using Repository for POST, PUT and DELETE Requests (Current article)
  • [sc name="part_of_series" headline="This article is part of the series"] If you want to see all the basic instructions and complete navigation for this series, please follow this link: Introduction page for this tutorial.
      The source code is available for download at .NET Core, Angular and MySQL. Part 6 - Source Code This post is divided into several sections: Let's get right into it.

      Handling POST Request

      Firstly, let's modify the decoration attribute for the action method GetOwnerById in the Owner controller:
      [HttpGet("{id}", Name = "OwnerById")]
      With this modification, we are setting the name for the action. This name will come in handy in the action method for creating a new owner. Before we continue, we should create another DTO class. As we said in the previous part, we use the model class just to fetch the data from the database, to return the result we need a DTO. It is the same for the create action. So, let's create the OwnerForCreationDto class in the Entities/DataTransferObjects folder:
      public class OwnerForCreationDto
      {
          [Required(ErrorMessage = "Name is required")]
          [StringLength(60, ErrorMessage = "Name can't be longer than 60 characters")]
          public string Name { get; set; }
      
          [Required(ErrorMessage = "Date of birth is required")]
          public DateTime DateOfBirth { get; set; }
      
          [Required(ErrorMessage = "Address is required")]
          [StringLength(100, ErrorMessage = "Address cannot be loner then 100 characters")]
          public string Address { get; set; }
      }
      As you can see, we don't have the Id and Accounts properties. We are going to continue with the interface modification:
      public interface IOwnerRepository : IRepositoryBase<Owner>
      {
          IEnumerable<Owner> GetAllOwners();
          Owner GetOwnerById(Guid ownerId);
          Owner GetOwnerWithDetails(Guid ownerId);
          void CreateOwner(Owner owner);
      }
      After the interface modification, we are going to implement that interface:
      public void CreateOwner(Owner owner)
      {
          Create(owner);
      }
      Before we modify the OwnerController, we have to create an additional mapping rule: CreateMap<OwnerForCreationDto, Owner>(); Lastly, let's modify the controller:
      [HttpPost]
      public IActionResult CreateOwner([FromBody]OwnerForCreationDto owner)
      {
          try
          {
              if (owner == null)
              {
                  _logger.LogError("Owner object sent from client is null.");
                  return BadRequest("Owner object is null");
              }
      
              if (!ModelState.IsValid)
              {
                  _logger.LogError("Invalid owner object sent from client.");
                  return BadRequest("Invalid model object");
              }
      
              var ownerEntity = _mapper.Map<Owner>(owner);
      
              _repository.Owner.CreateOwner(ownerEntity);
              _repository.Save();
      
              var createdOwner = _mapper.Map<OwnerDto>(ownerEntity);
      
              return CreatedAtRoute("OwnerById", new { id = createdOwner.Id }, createdOwner);
          }
          catch (Exception ex)
          {
              _logger.LogError($"Something went wrong inside CreateOwner action: {ex.Message}");
              return StatusCode(500, "Internal server error");
          }
      }
      Right now is a good time to test this code by sending the POST request by using Postman. Let's examine the result: POST HTTP request in .NET Core

      Code Explanation

      Let’s talk a little bit about this code. The interface and the repository parts are pretty clear so we won’t talk about that. But the code in the controller contains several things worth mentioning. The CreateOwner method has its own [HttpPost] decoration attribute, which restricts it to the POST requests. Furthermore, notice the owner parameter which comes from the client. We are not collecting it from the Uri but from the request body. Thus the usage of the [FromBody] attribute. Also, the owner object is a complex type and because of that, we have to use [FromBody]. If we wanted to, we could explicitly mark the action to take this parameter from the Uri by decorating it with the [FromUri] attribute, though I wouldn’t recommend that at all due to the security reasons and complexity of the request. Since the owner parameter comes from the client, it could happen that the client doesn't send that parameter at all. As a result, we have to validate it against the reference type's default value, which is null. Further down the code, you can notice this type of validation: if(!ModelState.IsValid). If you look at the owner model properties: Name, Address, and DateOfBirth, you will notice that all of them are decorated with the Validation Attributes. If for some reason validation fails, the ModelState.IsValid will return false as a result, signaling that something is wrong with the creation DTO object. Otherwise, it will return true which means that values in all the properties are valid. We have two map actions as well. The first one is from the OwnerForCreationDto type to the Owner type because we accept the OwnerForCreationDto object from the client and we have to use the Owner object for the create action. The second map action is from the Owner type to the OwnerDto type, which is a type we return as a result. The last thing to mention is this part of the code:
      CreatedAtRoute("OwnerById", new { id = owner.Id}, owner);
      CreatedAtRoute will return a status code 201, which stands for Created as explained in our post: The HTTP Reference. Also, it will populate the body of the response with the new owner object as well as the Location attribute within the response header with the address to retrieve that owner. We need to provide the name of the action, where we can retrieve the created entity: Post action header If we copy this address and paste it in Postman, once we send the GET request, we are going to get a newly created owner object.

      Handling PUT Request

      Excellent. Let’s continue with the PUT request, to update the owner entity. First, we are going to add an additional DTO class:
      public class OwnerForUpdateDto
      {
          [Required(ErrorMessage = "Name is required")]
          [StringLength(60, ErrorMessage = "Name can't be longer than 60 characters")]
          public string Name { get; set; }
      
          [Required(ErrorMessage = "Date of birth is required")]
          public DateTime DateOfBirth { get; set; }
      
          [Required(ErrorMessage = "Address is required")]
          [StringLength(100, ErrorMessage = "Address cannot be loner then 100 characters")]
          public string Address { get; set; }
      }
      We did the same thing as with the OwnerForCreationDto class. Even though this class looks the same as the OwnerForCreationDto, they are not the same. First of all, they have a semantical difference, this one is for update action and the previous one is for creation. Additionally, the validation rules that apply for the creation DTO doesn't have to be the same for the update DTO. Therefore, it is always a good practice to separate those. One more thing, if you want to remove the code duplication from the OwnerForCreationDto and OwnerForUpdateDto, you can create an additional abstract class, extract properties to it and then just force these classes to inherit from the abstract class. Due to the sake of simplicity, we won't do that now. After that, we have to create a new map rule: CreateMap<OwnerForUpdateDto, Owner>(); Then, let's change the interface:
      public interface IOwnerRepository : IRepositoryBase<Owner>
      {
          IEnumerable<Owner> GetAllOwners();
          Owner GetOwnerById(Guid ownerId);
          Owner GetOwnerWithDetails(Guid ownerId);
          void CreateOwner(Owner owner);
          void UpdateOwner(Owner owner);
      }
      Of course, we have to modify the OwnerRepository.cs:
      public void UpdateOwner(Owner owner)
      {
          Update(dbOwner);
      }
      
      Finally, alter the OwnerController:
      [HttpPut("{id}")]
      public IActionResult UpdateOwner(Guid id, [FromBody]OwnerForUpdateDto owner)
      {
          try
          {
              if (owner == null)
              {
                  _logger.LogError("Owner object sent from client is null.");
                  return BadRequest("Owner object is null");
              }
      
              if (!ModelState.IsValid)
              {
                  _logger.LogError("Invalid owner object sent from client.");
                  return BadRequest("Invalid model object");
              }
      
              var ownerEntity = _repository.Owner.GetOwnerById(id);
              if (ownerEntity == null)
              {
                  _logger.LogError($"Owner with id: {id}, hasn't been found in db.");
                  return NotFound();
              }
      
              _mapper.Map(owner, ownerEntity);
      
              _repository.Owner.UpdateOwner(ownerEntity);
              _repository.Save();
      
              return NoContent();
          }
          catch (Exception ex)
          {
              _logger.LogError($"Something went wrong inside UpdateOwner action: {ex.Message}");
              return StatusCode(500, "Internal server error");
          }
      }
      As you may have noticed, the action method is decorated with the [HttpPut] attribute. Furthermore, it receives two parameters: id of the entity we want to update and the entity with the updated fields, taken from the request body. The rest of the code is pretty simple. After the validation, we are pulling the owner from the database and executing the update of that owner. Finally, we return NoContent which stands for the status code 204: PUT HTTP request in .NET Core You can read more about Update actions in ASP.NET Core with EF Core to get a better picture of how things are done behind the scene. It could be very useful to upgrade quality of the update actions.

      Handling DELETE Request

      For the Delete request, we should just follow these steps: Interface:
      public interface IOwnerRepository : IRepositoryBase<Owner>
      {
          IEnumerable<Owner> GetAllOwners();
          Owner GetOwnerById(Guid ownerId);
          Owner GetOwnerWithDetails(Guid ownerId);
          void CreateOwner(Owner owner);
          void UpdateOwner(Owner owner);
          void DeleteOwner(Owner owner);
      }
      OwnerRepository:
      public void DeleteOwner(Owner owner)
      {
          Delete(owner);
      }
      OwnerController:
      [HttpDelete("{id}")]
      public IActionResult DeleteOwner(Guid id)
      {
      	try
      	{
      		var owner = _repository.Owner.GetOwnerById(id);
      		if(owner.IsEmptyObject())
      		{
      			_logger.LogError($"Owner with id: {id}, hasn't been found in db.");
      			return NotFound();
      		}
      
      		_repository.Owner.DeleteOwner(owner);
                      _repository.Save();
      
      		return NoContent();
      	}
      	catch (Exception ex)
      	{
      		_logger.LogError($"Something went wrong inside DeleteOwner action: {ex.Message}");
      		return StatusCode(500, "Internal server error");
      	}
      }
      Let's handle one more thing. If you try to delete the owner that has accounts, you are going to get 500 internal error because we didn't allow cascade delete in our database configuration. What we want is to return a BadRequest. So, to do that let's make a couple of modifications. Modify the IAccountRepository interface:
      using Entities.Models;
      using System;
      using System.Collections.Generic;
      
      namespace Contracts
      {
          public interface IAccountRepository
          {
              IEnumerable<Account> AccountsByOwner(Guid ownerId);
          }
      }
      Then modify the AccountRepository file by adding one new method:
      public IEnumerable<Account> AccountsByOwner(Guid ownerId)
      {
          return FindByCondition(a => a.OwnerId.Equals(ownerId)).ToList();
      }
      Finally, modify the DeleteOwner action in the OwnerController by adding one more validation before deleting the owner:
      if(_repository.Account.AccountsByOwner(id).Any())
      {
          _logger.LogError($"Cannot delete owner with id: {id}. It has related accounts. Delete those accounts first");
          return BadRequest("Cannot delete owner. It has related accounts. Delete those accounts first");
      }
      So, that is it. Send the Delete request from Postman and see the result. The owner object should be deleted from the database. We have created these actions that use Repository Pattern logic synchronously but it could be done asynchronously as well. If you want to learn how to do that you can visit Implementing Async Repository in .NET Core. Although we strongly recommend finishing all the parts from this series for an easier understanding of the project's business logic.

      Conclusion

      Now that you know all of this, try to repeat all the actions but for the Account entity. Because nothing beats the practice, doesn't it? ;) With all this code in place, we have a working web API that covers all the features for handling the CRUD operations. By reading this post you've learned:
      • The way to handle the POST request
      • How to handle PUT request
      • How to write better and more reusable code
      • And the way to handle DELETE request
      Thank you for reading and I hope you found something useful in it. When you are ready, continue to Part 7 which is the part of the series where we introduce Angular. I will show you how to create an angular project and set up your first component. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      1552 0 0 0 94 0 0 95 94 0 135 0 0 136 135 0 502 0 0 503 public class Owner: IEntity. Our extension methods are extending the IEntity type, so in order to have them implemented you must modify your model classes. When your Owner class implements IEntity interface, that means that the Owner is IEntity. So, extension upon the IEntity type will work for the Owner type as well, because it is IEntity. From what you wrote, I believe this is a problem, but if it is not, please share your code, and I will help you further more.]]> 502 0 508 503 0 649 0 0 654 with one Owner Owner = RepositoryContext.Owners .Where(o => o.Id == account.OwnerId) }; }]]> 0 0 656 Accounts and your Account should have Owner owner. After we have this knowledge, we can fix you AccountExtended into something like this: public AccountExtended GetAccountWithDetails(Guid accountId) { var account = GetAccountById(accountId); return new AccountExtended(account) { Owner = RepositoryContext.Owners.SingleOrDefault(o => o.Id.Equeals(account.OwnerId)) }; } So you are transforming your account object into account with a single Owner. Hope this will help you. All the best mate.]]> 654 0 707 656 0 711 649 0 885 0 0 886 885 0 887 886 0 888 887 0 970 0 0 971 970 0 1011 0 0 1100 0 0 1101 1100 0 1111 0 0 1112 1111 0 1113 1112 0 1114 1113 0 1115 1114 0 1133 0 0 1134 Students. Once you have models like that, you can use something like this: _context.Standard.Include(s => s.Students). This will create a JOIN between those two tables and return all the Standards with all the Students per Standard.]]> 1133 0 1143 1134 0 1144 0 0 1145 1144 0 1152 0 0 1154 GetAllWithSP(); Then in the OwnerRepository class implement this method: public IEnumerable GetAllWithSP() => RepositoryContext.Owners.FromSql("Call GetAllOwners()").ToList(); Pay attention that you must have this package installed alongside the EF Core pacakge: Microsoft.EntityFrameworkCore.Relational (You can do that via NuGet package manager) Now, I will modify GetAllOwners action inside the Owners controller, to collect all the owners but from the stored procedure: public IActionResult GetAllOwners() { try { var owners = _repository.Owner.GetAllWithSP(); _logger.LogInfo($"Returned all owners from database."); return Ok(owners); } catch (Exception ex) { _logger.LogError($"Something went wrong inside GetAllOwners action: {ex.Message}"); return StatusCode(500, "Internal server error"); } } And that is it. Call this action with postman and you will get your result: https://uploads.disquscdn.com/images/e34e92243e761054e5db4f06f548d64a549f2eb08cea9520aa2880b2e5e6ee76.png]]> 1152 0 1155 1154 0 1182 0 0 1184 public class ProfileClass : Profile { public ProfileClass() { CreateMap(); //could be the same types as well } } After that inject it into your controller as you did with the Repository and logger. Finally, use the mapper: var result = _mapper.Map(originalObject); The result object will have mapped values so it should be the only object passed to the Update method. Hope this helps you.]]> 1182 0 1191 1184 0 1192 0 0 1193 1192 0 1194 1191 0 1195 1193 0 1196 1195 0 1197 1196 0 1594 leaving us a review, it means us a lot. Best regards.]]> 1591 0 1363 0 0 1374 1363 0 1377 owner.Id = Guid.NewGuid(); Your object (that you send to create) will have a zero as a value for the PK property (zero is a default value for the value types in C#) and the SQL will do the rest for you. I hope this made it clearer.]]> 1374 0 1389 0 0 1390 1389 0 1391 _repository.Owner.UpdateOwner(dbOwner, entity); it's call UpdateOwner and passing dbOwner and entity ( which is obj I send from Postman). Then, It calls the functioin Map : public static void Map( this OwnerTbl dbOwner, OwnerTbl entity). I'm not sure, but dbOwner.OwnerId = entity.OwnerId are not the same value. ( which I think it should be the same) For example, I'm sending ownerId = 1000 , then dbOwner.OwnerId = 1000, but entity.OwnerId = 0. Other properties are renaming the same. Not sure it's the way EFCore should do or not. I think its correct because I'm not warp OwnerId inside body. The ex error is : {"The property 'OwnerId' on entity type 'OwnerTbl' has a temporary value while attempting to change the entity's state to 'Deleted'. Either set a permanent value explicitly or ensure that the database is configured to generate values for this property."} [HttpPut("{id}")] public IActionResult UpdateOwner(int id, [FromBody]OwnerTbl owner) { try { if (owner.IsObjectNull()) { _logger.LogError("Owner object sent from client is null."); return BadRequest("Owner object is null"); } if (!ModelState.IsValid) { _logger.LogError("Invalid Owner object sent from client."); return BadRequest("Invalid model object"); } var dbOwner = _repository.Owner.GetOwnerById(id); if (dbOwner.IsEmptyObject()) { _logger.LogError($"Owner with Id: {id}, hasn't been found in db."); return NotFound(); } _repository.Owner.UpdateOwner(dbOwner, Owner); _repository.Save(); return NoContent(); } catch (Exception ex) { _logger.LogError($"Something went wrong inside UpdateOwner action: {ex.Message}"); return StatusCode(500, "Internal server error@updateOwner"); } } public void UpdateOwner(OwnerTbl dbOwner, OwnerTbl owner) { dbOwner.Map(owner); Update(dbOwner); } Basically same code as in your blog. Here is my database: OwnerTbl has OwnerId(PK) , OwnerDriverLicenceId, AccountId(FK to AccountTbl), AccountType(FK to AccountTypeTbl) AccountTbl has AccountID ( PK) , DateOpen...etc AccountType has TypeID ( PK), TYpeType...]]> 1390 0 1394 1391 0 1397 CreateOwner ?]]> 1394 0 1398 1397 0 1399 o.owner.ownderId)) but seem like not correct. Any advice?]]> 1398 0 1401 o.Id == Id).AccountNumber) ... If you don't have that number in the Owner table, than you have to fetch the owner by id first, and then to fetch the account with the FK inside the owner object you just retreived. Than you can check if AccountNumber exists in the retreived account object.]]> 1399 0 1404 1401 0 1405 1404 0 1406 1405 0 1420 1406 0 1499 0 0 1500 1499 0 1505 [HttpPost("LiveDevices")] public ActionResult LiveDevices([FromBody] LiveDeviceTransform transform) { Console.WriteLine("Transform: " + transform); } And from the client I'm sending a string I created using a JSON utility from a class that looks exactly like LiveDeviceTransform using a POST request. I tried Content-Type "application/json" and "raw". From the server, I'm getting this: info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] Executing endpoint 'ShowWrapper.Controllers.ShowController.LiveDevices (ShowWrapper)' info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3] Route matched with {action = "LiveDevices", controller = "Show"}. Executing controller action with signature Microsoft.AspNetCore.Mvc.ActionResult LiveDevices(System .Object) on controller ShowWrapper.Controllers.ShowController (ShowWrapper). info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1] Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.ProblemDetails'. info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Executed action ShowWrapper.Controllers.ShowController.LiveDevices (ShowWrapper) in 404.7825ms info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] Executed endpoint 'ShowWrapper.Controllers.ShowController.LiveDevices (ShowWrapper)' info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 598.5863ms 415 application/problem+json; charset=utf-8 info: Microsoft.AspNetCore.Server.Kestrel[32] Connection id "0HLQ2G1H2OEU3", Request id "0HLQ2G1H2OEU3:00000001": the application completed without reading the entire request body. Which, I think, means it's not finding the right route to match or that it does but reading the content doesn't work. And, indeed, when I removed the parameters from the method, it entered correctly. So, something about my definitions or my formatting is wrong. Can you help?]]> 0 0 1508 1505 0 1510 1508 0 1511 1510 0 1512 1511 0 1513 1512 0 1524 { "AccountType": "Saving", "DateCreated": "1900-01-51", "OwnerId": "261e1685-cf26-494c-b17c-3546e65f5620" } I'm getting "Account object is null" return from the request. If i change the "1900-01-51" to "1900-01-01", it return Status OK(200) and data inserted into db. but i follow the above tutorial for owner POST request, using below body { "Name": "Mark Marton", "DateOfBirth": "1900-01-51", "Address": "Sunny street 60" } i'm getting JSON { "DateOfBirth": [ "Could not convert string to DateTime: 1900-01-51. Path 'DateOfBirth', line 3, position 27." ] } Why i getting different result from the same method? could anyone help me about this? seem like i'm missing something. Thanks in advance :) My code for Account: AccountController.cs [HttpPost] public IActionResult CreateAccount([FromBody]Account account) { try { if (account.IsObjectNull()) { _logger.LogError("Account object sent from client is null."); return BadRequest("Account object is null"); } if (!ModelState.IsValid) { _logger.LogError("Invalid account object sent from client."); return BadRequest("Invalid model object"); } _repository.Account.CreateAccount(account); _repository.Save(); return CreatedAtRoute("AccountById", new { id = account.Id }, account); } catch (Exception ex) { _logger.LogError($"Something went wrong inside CreateAccount action: {ex.Message} : {ex.InnerException}"); return StatusCode(500, "Internal server error"); } } Contracts.IAccountRepository.cs public interface IAccountRepository : IRepositoryBase { ... void CreateAccount(Account account); } Repository.AccountRepository.cs public class AccountRepository : RepositoryBase, IAccountRepository { public AccountRepository(RepositoryContext repositoryContext) : base(repositoryContext) { } .... .... public void CreateAccount(Account account) { account.Id = Guid.NewGuid(); Create(account); } } Entities.Models.Account.cs [Table("account")] public class Account : IEntity { [Key] [Column("AccountId")] public Guid Id { get; set; } [Required(ErrorMessage = "Date created is required")] public DateTime DateCreated { get; set; } [Required(ErrorMessage = "Account type is required")] [StringLength(10, ErrorMessage = "Account type cannot be longer then 10 characters")] public string AccountType { get; set; } [Required(ErrorMessage = "Owner Id is required")] public Guid OwnerId { get; set; } } ]]> 0 0 1525 1524 0 1527 1525 0 1529 1527 0 1586 0 0 1587 EF Core tutorial (series of articles). In there you will understand how EF Core works and how to utilize that knowledge for all your requirements. Based on your comment, this series will help you a lot. Best regards.]]> 1586 0 1589 1587 0 1590 1589 0 1591 1590 0 1592 1590 0
      Angular - Angular Project Preparation https://code-maze.com/net-core-web-development-part7/ Mon, 26 Feb 2018 06:16:45 +0000 https://code-maze.com/?p=1627 the following link: Introduction of the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. The source code is available at GitHub .NET Core, Angular 4 and MySQL. Part 7 - Source Code This post is divided into several sections:

      Installation of the Angular CLI and Starting a New Project

      First, we are going to install the Angular CLI (Angular Command Line Interface) which will help us a lot with the Angular project preparation and Angular project development overall. To install Angular CLI, type the following command at the command prompt:
      npm install -g @angular/cli
      If you already have the Angular CLI installed, verify that you have the latest version. If not, please update it before starting the project. You can find all the instructions in here: https://github.com/angular/angular-cli. After the installation completes, we are going to create a new project. Open the Visual Studio Code and in a terminal window (CTRL+~) navigate to the path you want your project in and execute the command:
      ng new AccountOwnerClient
      In Angular version 7, as soon as we type this command, two questions will appear. The first one is whether we want our project to have routing created, and the second one is about what kind of styling we want in our project. We are going to answer NO (N) for the first question, and (CSS - just hit enter) for the second one. It will take some time to create the project. After the creation process is over, just open the project folder inside your editor: New angular project Angular components - Angular project preparation

      Third-Party Libraries as Part Of Angular Project Preparation

      We are going to use the bootstrap library for the styling, so let's install it with the command:
      npm install --save bootstrap@3
      It will install the library but we also need to import its path into the angular-cli.json (In Angular 6 it is angular.json) file. Place it right above the styles.css:   installed-bootstrap-angular6 - Angular project preparation After the bootstrap library installation, we are going to install the type definitions for it. For the installation, type this command:
      npm install --save @types/bootstrap
      Right after that, let's import that type definition inside the tsconfig.app.json file: type definitions Angular projects To install the JQuery library, type this command:
      npm install --save jquery
      Type definitions are already installed with the bootstrap types. If they are not, just execute:
      npm install --save @types/jquery
      This is how the scripts array should look like:
      "scripts": [
      	"./node_modules/jquery/dist/jquery.min.js",
      	"./node_modules/bootstrap/dist/js/bootstrap.min.js"
      ]
      
      And modify the imports for the types:
      "types": [
        "jquery",
        "bootstrap"
      ]
      
      For the JQueryUI installation, execute:
      npm install --save jqueryui
      and for the types execute:
      npm install --save @types/jqueryui
      This is how the styles and the scripts array should look like:
      "styles": [
      	"./node_modules/bootstrap/dist/css/bootstrap.min.css",
      	"./node_modules/jqueryui/jquery-ui.min.css",
      	"src/styles.css"
        ],
      "scripts": [
      	"./node_modules/jquery/dist/jquery.min.js",
      	"./node_modules/bootstrap/dist/js/bootstrap.min.js",
      	"./node_modules/jqueryui/jquery-ui.min.js"
        ],
      UPDATE: The above style and script paths are going to work with the Angular 6+ project (which you are working on if you have updated the Angular CLI as we have mentioned at the beginning of this post). But if you are working on an older project version, than your paths must start with two dots instead of one: ../node_modules/bootstrap... Imports for the types should look like this:
      "types": [
        "jquery",
        "bootstrap",
        "jqueryui"
      ]
      
      That wraps up the installation of dependencies. Now we have all the libraries installed and imported into the right files. The next step is adding our components to the project.

      Angular Components

      Let’s take some time to talk a bit about Angular. Angular is a framework for building SPA (Single Page Application) applications. Therefore, we are going to generate all of our pages inside one page. That is why we only have the index.html page. In the index.html page all content is going to be generated inside <app-root></app-root> selector which comes from the app.component.ts file. Take a look inside the app.component.ts file:
      import { Component } from '@angular/core';
      
      @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
      })
      export class AppComponent {
        title = 'app';
      }
      Every component must import Component from the @angular/core package. We will import more things when we need them. Also, you might have noticed the @Component decorator inside the code. This is the place where we create our selector (it is the same as the app-root tag in the index.html file). Also, we are binding the HTML template for this component with the templateUrl and the CSS files with this component by using styleUrls. StyleUrls is an array of strings, comma-separated. In the end, we are creating our class for the component. Now if we look in the app.module.ts file, which is quite important for the Angular project preparation and for development at all, we are going to notice this code:
      import { BrowserModule } from '@angular/platform-browser';
      import { NgModule } from '@angular/core';
      
      import { AppComponent } from './app.component';
      
      @NgModule({
        declarations: [
          AppComponent
        ],
        imports: [
          BrowserModule
        ],
        providers: [],
        bootstrap: [AppComponent]
      })
      export class AppModule { }
      
      In this file, we are going to import the necessary modules, components, and services. We are going to use the declarations array to import our components, and the imports array to import our modules. Also, we are going to use providers array for registering our services.

      Creating a New Component

      To create a new component with the name Home, first, let's create the folder home inside the app folder. Then inside that home folder, let’s create the home.component.ts, home.component.css, and home.component.html files. We will do the following actions manually just once for the sake of practice, but after that, I am going to show you how to automate the process. Empty files won’t do the trick, so let’s add some code to the Home component:
      import { Component, OnInit } from '@angular/core';
      
      @Component({
        selector: 'app-home',
        templateUrl: './home.component.html',
        styleUrls: ['./home.component.css']
      })
      export class HomeComponent implements OnInit {
      
        constructor() { }
      
        ngOnInit() {
        }
      
      }
      In here we import OnInit interface which defines the function ngOnInit. This function will execute any logic inside it as soon as the component initializes. Notice the constructor as well. The constructor is intended only for injection of the service into the component. For any action that needs to be executed upon component initialization, use the ngOnInit method.

      About App.Module

      In the end, we are going to include our component into the app.module.ts file:
      import { BrowserModule } from '@angular/platform-browser';
      import { NgModule } from '@angular/core';
      
      import { AppComponent } from './app.component';
      import { HomeComponent } from './home/home.component';
      
      @NgModule({
        declarations: [
          AppComponent,
          HomeComponent
        ],
        imports: [
          BrowserModule
        ],
        providers: [],
        bootstrap: [AppComponent]
      })
      export class AppModule { }
      Angular Cli provides a better and easier way to instantiate components, all you need to do is to type the command:
      ng g component home
      Angular CLI will create all of this for you. Also, it will create the fourth file, home.component.spec.ts which you may use for testing purpose. Even though one module is enough for the entire application, you still want to create more modules. Why? Because it is easier to maintain the modules and also more modules give you the advantage of the lazy content loading. That means that your application will load only content related to that specific module you are pointing to, and not the entire application. So let’s continue.

      Additional Content in the Home Component

      Modify the home component file:
      export class HomeComponent implements OnInit {
      
        public homeText: string;
      
        constructor() { }
      
        ngOnInit() {
          this.homeText = "WELCOME TO ACCOUNT-OWNER APPLICATION";
        }
      Then, add a new class to the home.component.css file:
      .homeText{
          font-size: 35px;
          color: red;
          text-align: center;
          position: relative;
          top:30px;
          text-shadow: 2px 2px 2px gray;
      }
      Continue with changing the home.component.html file:
      <div class="col-md-12">
          <p class="homeText">{{homeText}}</p>
      </div>
      Finally, modify the app.component.html file, just to test if this works:
      <div class="container container-fluid">
          <div class="row">
              <app-home></app-home>
          </div>
      </div>
      
      Now in the terminal type ng serve and wait for the application to compile. Right after that start your browser and navigate to: localhost:4200. You should see the welcome message on the screen from the Home component.

      Conclusion

      Right now we have a working component and an Angular application that you can run in your browser. But it is just a beginning. We have a long way ahead of us because there are still a lot of important Angular features to introduce to the project. By reading this post you've learned:
      • The way to set up third-party libraries
      • The overview of the angular components
      • How to create components
      • And some facts about modules in angular
      Thank you for reading and I hope you found something useful in it. In the next part of the series, I am going to show you how to create navigation in the project and also how to use routing. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      1627 0 0 0 110 0 0 137 0 0 138 137 0 164 0 0 165 ../node_modules, the project doesn't build as soon as I change to ./node_modules, it builds like a charm. But pay attention to this, if I use this old project and try to reference with ./node_modules, the build brakes in the same way :D :D so in earlier projects we need to use ../node_modules. It is so weird and I must admit a little bit of frustrating. Thank you very very much for your comment, I haven't noticed that this could be the problem, and you helped us a lot. You helped the readers as well, if some of them stumble upon the same problem. I am going to update the article. One more time, thank you very much. All the best.]]> 164 0 188 0 0 190 188 0 1231 0 0 1232 1231 0 1233 0 0
      Angular - Navigation and the Angular Routing https://code-maze.com/net-core-web-development-part8/ Mon, 05 Mar 2018 05:27:28 +0000 https://code-maze.com/?p=1701 the following link: Introduction of the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. The source code is available at GitHub .NET Core, Angular and MySQL. Part 8 - Source Code This post is divided into several sections:

      Creating a Navigation Menu

      In the app folder, create a new folder and name it "menu". After that create these files:
      • menu.component.ts
      • menu.component.html
      • and menu.component.css
      Then, import the menu component inside the app.module.ts file. Of course, if you want you may always use the Angular CLI command : ng g component menu We are going to use Bootstrap classes to implement the navigation menu within the menu.component.html file:
      <nav class="navbar navbar-inverse">
        <div class="container-fluid">
          <div class="navbar-header">
            <a class="navbar-brand" href="#">Account-Owner Home</a>
          </div>
          <ul class="nav navbar-nav">
            <li><a href="#">Owner Actions</a></li>
            <li><a href="#">Account Actions</a></li>
          </ul>
        </div>
      </nav>
      This is how the menu.component.ts file should look like:
      import { Component, OnInit } from '@angular/core';
      
      @Component({
        selector: 'app-menu',
        templateUrl: './menu.component.html',
        styleUrls: ['./menu.component.css']
      })
      export class MenuComponent implements OnInit {
      
        constructor() { }
      
        ngOnInit() {
        }
      }
      
      And we are going to change our app.component.html file:
      <div class="container container-fluid">
          <div class="row">
              <app-menu></app-menu>
          </div>
      </div>
      Start your angular project by typing ng serve and navigate tohttp://localhost:4200. You should be able to see our menu on the screen: Menu Angular routing

      Configuring Angular Routing

      The page with the welcome message is not available anymore because we have changed the app.component.html file. To enable the navigation between all the pages inside this project, we need to configure the Angular routing. Let's open the app.module.ts file and modify it:
      import { BrowserModule } from '@angular/platform-browser';
      import { NgModule } from '@angular/core';
      import { RouterModule } from '@angular/router'
      
      import { AppComponent } from './app.component';
      import { HomeComponent } from './home/home.component';
      import { MenuComponent } from './menu/menu.component';
      
      @NgModule({
        declarations: [
          AppComponent,
          HomeComponent,
          MenuComponent
        ],
        imports: [
          BrowserModule,
          RouterModule.forRoot([
            { path: 'home', component: HomeComponent },
            { path: '', redirectTo: '/home', pathMatch: 'full' }
          ])
        ],
        providers: [],
        bootstrap: [AppComponent]
      })
      export class AppModule { }
      In this file, we may notice a MenuComponent inside declarations array. It was imported earlier during the creation of the navigation component. Also, check out the RouterModule, which is the module for the Angular routing. We are going to use RouterModule.forRoot to define routes for our application. When we create more than one module inside the application, we can use the forRoot() function provided by the RouterModule, only in the main(root) module. In this project, it is the app.module file. In all other modules, we must use the forChild() function. The forRoot() function accepts an array of objects as a parameter. Every element of that array consists of the path and the target component for that path. So, the path: ‘home’ means that on the address http://localhost:4200/home we are going to serve the HomeComponent. The other line inside the forRoot function is the default redirection to the home page. To show the home page, modify app.component.html:
      <div class="container container-fluid">
          <div class="row">
              <app-menu></app-menu>
          </div>
          <div class="row">
              <router-outlet></router-outlet>
          </div>
      </div>
      The router-outlet is a container for the routing content. So basically, all the content that exists on the address you are routing to is going to be presented inside that container. Now if you navigate to the localhost:4200 you should be able to see the following result: Home page Angular routing

      Styling links

      If you want to style your active link in the menu, change your <a> tag:
      <a class="navbar-brand" [routerLink]="['/home']" routerLinkActive="active" 
                      [routerLinkActiveOptions]="{exact: true}">Account-Owner Home</a>
      
      With the routerLinkActive, we are setting up the CSS class name we want to use to style the active link. Furthermore, the routerLinkActiveOptions is going to allow us to add a class only if there is an exact match of the link and the URL. Now in the menu.component.css file add the .active class styles:
      .active{
          font-weight: bold;
          font-style: italic;
          color: #fff;
      }

      Creating the Not-Found Component

      Excellent. Now we have the working navigation. To complete the Angular routing part of this post, we are going to create a component with the name not-found. The application is going to redirect a user to this component when he types a none existing route in the URL. Let's start by creating the new folder inside the app folder and name it error-pages. Inside the error-pages folder, we are going to create a folder and name it not-foundInside that folder create all the component files you need. Be sure not to forget to import this component into our app.module.ts file. This is the folder structure: not-found folder structure Angular routing We can also create this folder structure automatically by typing the command:
      ng g component error-pages/not-found
      Let's modify the not-found.component.ts file:
      import { Component, OnInit } from '@angular/core';
      
      @Component({
        selector: 'app-not-found',
        templateUrl: './not-found.component.html',
        styleUrls: ['./not-found.component.css']
      })
      export class NotFoundComponent implements OnInit {
      
        public notFoundText: string = `404 SORRY COULDN'T FIND IT!!!`
      
        constructor() { }
      
        ngOnInit() {
        }
      }
      Pay attention to the string value of the variable notFoundText. We are not using apostrophes but backticks (`). All the content inside the backticks will be considered as a string, even the apostrophe sign in the string. Modify the not-found.component.html file:
      <p>
        {{notFoundText}}
      </p>
      Modify not-found.component.css file:
      p{
          font-weight: bold;
          font-size: 50px;
          text-align: center;
          color: #f10b0b;
      }
      Finally, modify the forRoot function in the app.module.ts file:
      imports: [
          BrowserModule,
          RouterModule.forRoot([
            { path: 'home', component: HomeComponent },
            { path: '404', component : NotFoundComponent},
            { path: '', redirectTo: '/home', pathMatch: 'full' },
            { path: '**', redirectTo: '/404', pathMatch: 'full'}
          ])
        ],
      You may notice two changes. With the first change, we are declaring the path 404 and assigning the component NotFoundComponent to that path. Now our component is going to be visible on the localhost:4200/404. The second change means that whenever you search for any route that doesn’t match any of our defined routes, the application redirects you to the 404 page. Note that the best practice for large projects is to keep router array in the separate file, but for the sake of the simplicity of this example, we are keeping it inside the forRoot function. If you still want to create a separate routing module, you can learn more about it here: Angular Material Navigation and Routing. Typing localhost:4200/whatever should return the following result: not-found page Angular component

      Conclusion

      As you might have noticed, creating the menu and using the routing in the angular project is pretty straightforward. Although we are not creating a large project, it is quite big enough to demonstrate the usage, configuration, and routing of all the pages we currently have. Of course, we are going to create routes for all new pages that we introduce to our project. By reading this post you've learned:
      • How to create a navigation menu
      • How to create and configure the routing
      • The way to style the route links
      Thank you for reading the post, hopefully, it was helpful to you. In the next part of the series, I am going to show you how to fetch the data and consume the API with the HTTP and Observables. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      1701 0 0 0 105 0 0 106 105 0 189 0 0 191 189 0 192 191 0 1504 0 0 1507 1504 0 1509 1507 0
      Angular - HttpClient, Services and Environment Files https://code-maze.com/net-core-web-development-part9/ Mon, 12 Mar 2018 06:06:17 +0000 https://code-maze.com/?p=1782 the following link: Introduction of the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. The source code is available at GitHub .NET Core, Angular and MySQL. Part 9 - Source Code This post is divided into several sections:

      About HttpClientModule and Angular HttpClient

      The thing that matters is that we are going to use the new HttpClientModule introduced in Angular 4.3 and not the old HttpModule. The new module gives us some advantages with processing responses and handling errors as well. With the old HttpModule, we would have to map our response and convert it to the JSON. Consequently, we would have to use the RxJS library because this library enables usage of the map function. With the new HttpClientModule and Angular HttpClient, JSON response is set by default. Thus our project requires no result conversion at all. The one way to check out our angular version is to start the application and to open developer tools. We will see the version in <app-root> tag: check the angular version Angular HTTP

      Working With Environment Files

      While this project is in development mode the server's endpoint address is the http://localhost:5000. With the application in the production environment, the endpoint is different, something like www.accountowner.com. What we want is that Angular takes care of that. So in the development mode, it should send requests towards localhost:5000, and in the production environment to some other address. Let’s implement that. In the side-bar of the visual studio code, search for the environments folder. Inside that folder, you are going to find two files, the environment.prod.ts, and the environment.ts. We are going to use the first one for configuring the production and the second one for configuring the development environment. It is time for us to modify these files. environment.prod.ts:
      export const environment = {
        production: true,
        urlAddress: 'http://www.accountowner.com'
      };
      environment.ts:
      export const environment = {
        production: false,
        urlAddress: 'http://localhost:5000'
      };
      Now we are going to create the service which will provide us with the valid environment urlAddress. Inside the app folder, create a new folder and name it shared. Then in that folder create a new one and name it services. Finally, create the new file environment-url.service.ts: folder structure Angular HTTP We could do this using the AngularCLI command as well:
      ng g service shared/services/environment-url

      About the Angular Services

      Services are just classes, which provide us with some business logic relevant to our components. These services must be injected into a component using constructor injection. Furthermore, our code becomes more maintainable and readable. When we want to use a service, we need to inject it into a component's constructor. Therefore, it is always decorated with the @Injectable decorator. Use services whenever you have some code that you can reuse in other components, or extract part of the code from your components.  To make a service work, we must import it into our app.module.ts file. From Angular version 6, there is a modification regarding a service providing. Now when we use Angular CLI command to create a service, the created service will have a different @Injectable decorator:
      @Injectable({
        providedIn: 'root',
      })
      What this means is that we don't need to register it inside the app.module file anymore, it is already provided for the entire application. But if we need it, we may provide it in an old fashion way as well. Let’s continue our work with the environment files by modifying the environment-url.service.ts file:
      import { Injectable } from '@angular/core';
      import { environment } from './../../../environments/environment';
      
      @Injectable()
      export class EnvironmentUrlService {
      
        public urlAddress: string = environment.urlAddress;
      
        constructor() { }
      
      }
      The urlAddress property accepts the value of the urlAddress defined in the environment file. Angular knows if it is production or development environment (because of "production" property values inside both environment files) and is going to supply us with a valid value of that urlAddress. Now we need to import this service into our app.module.ts file:
      import { EnvironmentUrlService } from './shared/services/environment-url.service';
      Place it inside providers array as well:
      providers: [
          EnvironmentUrlService
        ],

      Creating Angular Repository File

      Let’s configure the HttpClientModule and create the repository service. First, we need to import the HttpClientModule inside the app.module.ts file:
      import { HttpClientModule } from '@angular/common/http';
      Then let’s place it inside imports array:
      imports: [
          BrowserModule,
          HttpClientModule,
          RouterModule.forRoot([
            { path: 'home', component: HomeComponent },
            { path: '404', component : NotFoundComponent},
            { path: '', redirectTo: '/home', pathMatch: 'full' },
            { path: '**', redirectTo: '/404', pathMatch: 'full'}
          ])
        ]
      Now create the service file and name it repository.service.ts. Place it in the same folder in which environment service resides. In this service, we are going to create get, post, put and delete requests. Don't forget to include this service inside the app.module.ts file and to place it inside the providers array. So let’s modify repository service:
      import { Injectable } from '@angular/core';
      import { HttpClient, HttpHeaders } from '@angular/common/http';
      import { EnvironmentUrlService } from './environment-url.service';
      
      @Injectable()
      export class RepositoryService {
      
        constructor(private http: HttpClient, private envUrl: EnvironmentUrlService) { }
      
        public getData(route: string) {
          return this.http.get(this.createCompleteRoute(route, this.envUrl.urlAddress));
        }
      
        public create(route: string, body) {
          return this.http.post(this.createCompleteRoute(route, this.envUrl.urlAddress), body, this.generateHeaders());
        }
      
        public update(route: string, body){
          return this.http.put(this.createCompleteRoute(route, this.envUrl.urlAddress), body, this.generateHeaders());
        }
      
        public delete(route: string){
          return this.http.delete(this.createCompleteRoute(route, this.envUrl.urlAddress));
        }
      
        private createCompleteRoute(route: string, envAddress: string) {
          return `${envAddress}/${route}`;
        }
      
        private generateHeaders() {
          return {
            headers: new HttpHeaders({'Content-Type': 'application/json'})
          }
        }
      }

      Repository Code Explanation

      Excellent. Let’s explain this code. First, we are injecting the Angular HttpClient and the environment variable into the constructor. Then we are creating functions which are going to wrap our requests. Function getData is a wrapper for all GET requests. It accepts the route parameter of type string (api/owner or api/owner/id …) and then combines it with the environment variable (localhost or www…). After all of that, we are going to have the route like http://localhost:5000/api/owner if it is a development environment, which perfectly fits our requirements on the server side. The second function, create, is a wrapper for POST requests. It also generates a route, but additionally receives a body (an entity which we are creating) and generate headers. For this example, we are just creating the Content-Type inside the header. But if we need additional values inside the header, we could just add another key-value pair inside the HttpHeaders object. Function update is pretty much the same as the create function, except it sends PUT request. Lastly, delete function is a wrapper for the DELETE request which accepts the route like (api/owner/id).

      Subscription on the HTTP calls

      These wrapper functions need a subscription in order to work. In this post, we are only creating a repository with the HTTP calls. But as soon as we start creating our pages, we are going to use subscription. For now, I am just going to show you one example of a subscription:
      public result: any;
      
      constructor(private repo: RepositoryService) { }
      
      public consumeGetFromRepository() {
          this.repo.getData('api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906')
            .subscribe(res => {
              this.result = res;
            },
            (error) => {
              this.handleErrors(error);
            })
        }
      
      As you may notice, we are calling the getDatafunction from the repository, but that function won't be executed until we call the subscribe function. By calling the subscribe function, we are going to execute the getData function. The result from the response is going to be stored in the parameter res.

      Conclusion

      Excellent, now we have our repository prepared and we are ready for creating components, which are going to use this repository functions to show results in a browser. By reading this post you've learned:
      • What the HttpClientModule is
      • How to work with environment files in Angular
      • What the Angular services are
      • How to use HTTP requests
      • The way to subscribe to HTTP requests
      Thank you for reading the post, hopefully, it was helpful to you. In the next part of the series, I am going to show you how to use lazy content loading in the Angular. Moreover, we are going to show the result data on a page. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      1782 0 0 0 108 0 0 109 108 0 375 374 0 374 0 0
      Angular - Angular Lazy Loading https://code-maze.com/net-core-web-development-part10/ Mon, 19 Mar 2018 06:02:28 +0000 https://code-maze.com/?p=1848  you how to use subscription), we are now going to implement that subscription to our HTTP requests in order to display the data on the page. Furthermore, we are going to use the advantage of  Angular Lazy Loading, by using another module in our application – the owner module. If you want to see all the basic instructions and complete navigation for the .NET Core series, check out the following link: Introduction of the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. The source code is available at GitHub .NET Core, Angular and MySQL. Part 10 - Source Code This post is divided into several sections:

      Creating a New Module

      So let’s start. Create a new folder and name it owner. Inside create a new file and name itowner.module.ts. As you might have noticed we are creating a new module inside the application which is going to be responsible for all the owner operations. Let’s modify the owner module:
      import { NgModule } from '@angular/core';
      import { CommonModule } from '@angular/common';
      
      @NgModule({
        imports: [
          CommonModule
        ],
        declarations: []
      })
      export class OwnerModule { }
      There are two small differences between this module file and the app module file. The first difference is that in the app module file we have an import statement for the BrowserModule, and in the owner module file, we have an import statement for the CommonModule. This is because the BrowserModule is only related to the root module in the application. The second difference is that we don’t have providers array inside the owner module file. That’s because we should register all the services in the root module. That way components will inject the same instance of the service only once and you can keep the state in your service. Of course, if you really want to register a service inside any child module, you could just add providers array. But, by doing so you cannot keep the state inside your service because every time we create a new instance of that component a new instance of a service is created.

      Owner Component and Angular Lazy Loading

      Let’s start with the creation of the owner component files. Execute the AngularCLI command for creating a new component:
      ng g component owner/owner-list
      This command is going to create the required folder structure and it is going to import this component inside the owner.module.ts file as well: owner-list folder structure angular lazy loading What we want now is, when we click on the “Owner-Actions” menu, to show the content from this component's HTML file. So first, just for the testing purposes, modify the owner.component.html file by adding one paragraph (<p> tag):
      <p>This is owner-list component page.</p>
      Now modify the app.module.ts file:
      RouterModule.forRoot([
            { path: 'home', component: HomeComponent },
            { path: 'owner', loadChildren: "./owner/owner.module#OwnerModule" },
            { path: '404', component : NotFoundComponent},
            { path: '', redirectTo: '/home', pathMatch: 'full' },
            { path: '**', redirectTo: '/404', pathMatch: 'full'}
          ])
      With the modified part of the code, we are configuring the app.module to load the owner module whenever someone searches for the http://localhost:4200/owner endpoint. As you might have noticed, we are using the loadChildren property which means, that the owner module with its components won’t be loaded until we explicitly ask for them. By doing this, we are configuring Angular lazy loading from the owner module content. Now if you navigate to the Home page, you will get only resources from the root module, not from the owner module. And only by navigating to the owner-actions menu, you will load the owner module resources into the application. From the previous statement, we can see why is Angular lazy loading important for Angular applications.

      Routing for the Owner Module

      Let’s modify the owner.module.ts:
      import { NgModule } from '@angular/core';
      import { CommonModule } from '@angular/common';
      import { RouterModule } from '@angular/router';
      
      import { OwnerListComponent } from './owner-list/owner-list.component';
      
      @NgModule({
        imports: [
          CommonModule,
          RouterModule.forChild([
            { path: 'list', component: OwnerListComponent }
          ])
        ],
        declarations: [
          OwnerListComponent
        ]
      })
      export class OwnerModule { }
      With this setup, we are exposing our OwnerListComponent on the http://localhost:4200/owner/list endpoint. Moreover, we are using the RouterModule.forChild function and not the forRoot function. This is the case because we should use the forRoot function only in the root module of the application. Now we have to modify the menu.component.html file:
      <ul class="nav navbar-nav">
            <li><a [routerLink]="['/owner/list']" routerLinkActive="active" 
                      [routerLinkActiveOptions]="{exact: true}">Owner Actions</a></li>
            <li><a href="#">Account Actions</a></li>
      </ul>
      Excellent. Now we know how to set up the routing for the child module, and for the component inside that module as well. child component navigation angular lazy lodaing

      Interface, Subscription and Data Display

      Let’s continue on. When we navigate to the Owner Actions menu, we want to show all of the owners to the user. So that means when owner component loads, the app automatically gets all the owners from the server. To accomplish that, let’s create a new folder, inside app folder and name it _interfaces. Inside create a new file owner.model.ts. Modify that file:
      export interface Owner{
          id: string;
          name: string;
          dateOfBirth: Date;
          address: string;
      }
      Modify owner-list.component.ts:
      import { Component, OnInit } from '@angular/core';
      import { RepositoryService } from './../../shared/services/repository.service';
      import { Owner } from './../../_interfaces/owner.model';
      
      @Component({
        selector: 'app-owner-list',
        templateUrl: './owner-list.component.html',
        styleUrls: ['./owner-list.component.css']
      })
      export class OwnerListComponent implements OnInit {
        public owners: Owner[];
      
        constructor(private repository: RepositoryService) { }
      
        ngOnInit() {
          this.getAllOwners();
        }
      
        public getAllOwners(){
          let apiAddress: string = "api/owner";
          this.repository.getData(apiAddress)
          .subscribe(res => {
            this.owners = res as Owner[];
          })
        }
      }
      As you might have noticed we have a property with the name owners and it is of type Owner array. Following we execute the subscribe function, which is going to populate that property with all the owners of the server. Using "owners" property to create our owner HTML page is what we aim for. To accomplish that modify HTML component:
      <div class="row"> 
        <div class="col-md-offset-10 col-md-2">
          <a href="#">Create owner</a>
        </div>
      </div>
      <br>
      <div class="row">
        <div class="col-md-12">
          <div class="table-responsive">
            <table class="table table-striped">
              <thead>
                <tr>
                  <th>Owner name</th>
                  <th>Owner address</th>
                  <th>Date of birth</th>
                  <th>Details</th>
                  <th>Update</th>
                  <th>Delete</th>
                </tr>
              </thead>
              <tbody>
                <tr *ngFor="let owner of owners">
                  <td>{{owner.name}}</td>
                  <td>{{owner.address}}</td>
                  <td>{{owner.dateOfBirth | date: 'dd/MM/yyyy'}}</td>
                  <td><button type="button" id="details" class="btn btn-default">Details</button></td>
                  <td><button type="button" id="update" class="btn btn-success">Update</button></td>
                  <td><button type="button" id="delete" class="btn btn-danger">Delete</button></td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </div>
      We could split this html file into two components (the parent component: OwnerList and the child component Owner), but because I didn't explain how to use child components, and I am going to in the next posts, we are going to leave it like this for now. We are using some basic Bootstrap classes to create a table showing the owner's data. Inside that table, we are looping  (with *ngFor) through all the owners. Then by using interpolation {{}}, we are showing owner properties on the page. For the dateOfBirth property, we are using just the Date pipe to format it the way we want to see it on a screen. In our application, we are going to use date format as MM/dd/yyyy, but in here we are going to use dd/MM/yyyy just to demonstrate the way to change the format with pipes without too much effort. owner-list angular lazy loading

      Conclusion

      By reading this post you have learned:
      • How to create a new module and what imports to use
      • The way to configure Angular lazy loading and how it can help your application
      • How to execute HTTP request with a subscription and to display result data on the page
      • The way to reformat your date type when displaying it
      Thank you for reading the post, hopefully, it was helpful to you. In the next part of the series, I am going to show you my way of error handling while sending HTTP requests. Moreover, we are going to create a Details page for the single owner. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      1848 0 0 0 117 0 0 118 117 0 123 0 0 124 123 0 125 124 0 126 125 0 140 0 0 141 140 0 193 0 0 194 193 0 647 0 0 666 140 0 671 0 0 675 671 0 714 647 0 1009 0 0 1010 1009 0 1443 0 0 1444 1443 0
      How to Prepare an ASP.NET Core Application for Dockerization https://code-maze.com/how-to-prepare-aspnetcore-app-dockerization/ Wed, 25 Apr 2018 05:06:38 +0000 https://code-maze.com/?p=1857
    • .NET Core 3.1 SDK
    • Windows 10
    • Git
    • IDE of your choice (we use Visual Studio 2019)
    • Docker Desktop (at least 2.1.0.5)
    • These are not necessary but to achieve the best results we recommend this setup. [sc name="part_of_series" headline="This article is part of the series"] For the purposes of this article, we are going to re-use the application from our .NET Core Series, more concretely part 6 of the series. The main reason for this decision is that if you are not familiar with the ASP.NET Core, you can go through the series and learn how to make ASP.NET Core WebApi application from scratch. Since we are going to make some changes to the code, if you want to follow along with the steps of this course, you can find the starting point on the master branch docker-series If you want to skip the preparation phase and jump right into Docker goodness, you can switch to the docker-series-app-prepared branch of the docker-series repo and go quickly through this section. It might be beneficial for you to follow along if you are not familiar with some of the changes we are going to implement. These are the changes we're going to make to the original project: There is a lot to cover, so let's get down to business.

      Switching to an In-Memory Database

      First things first. To be able to dockerize our application and make it work without relying on external resources (database) we are going to make some changes to the configuration. We are going to re-introduce MySQL later in the series, but for now, we are going to rely on an in-memory database to store our data. So, let's modify our ServiceExtensions.cs class:
      public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration config)
      {
      	services.AddDbContext<RepositoryContext>(o => o.UseInMemoryDatabase("accountowner"));
      }
      The UseInMemoryDatabase method is part of the Microsoft.EntityFrameworkCore.InMemory NuGet package, so make sure to import it to the AccountOwnerServer project! That's it! We successfully removed the reference to the MySQL database and added an in-memory database in a single line of code. Don't worry about the hanging connection string in the appsettings.json file, we don't need to remove it since we are going to use it in the next part.

      Introducing AccountController

      To make our API functional on its own, we are going to modify and rename existing WeatherForecastController. So, first, we are going to rename it to AccountController, and then add some new code to it. We are going to add GetAllAccounts method and the CreateAccount method that will give us more options to play around with our API.
      [ApiController]
      	[Route("api/[controller]")]
      	public class AccountController : Controller
      	{
      		private ILoggerManager _logger;
      		private IRepositoryWrapper _repository;
      		private IMapper _mapper;
      
      		public AccountController(ILoggerManager logger, IRepositoryWrapper repository, IMapper mapper)
      		{
      			_logger = logger;
      			_repository = repository;
      			_mapper = mapper;
      		}
      
      		[HttpGet]
      		public IActionResult GetAllAccounts()
      		{
      			try
      			{
      				var accounts = _repository.Account.GetAllAccounts();
      
      				_logger.LogInfo($"Returned all accounts from database.");
      
      				var accountDtos = _mapper.Map<IEnumerable<AccountDto>>(accounts);
      				return Ok(accountDtos);
      			}
      			catch (Exception ex)
      			{
      				_logger.LogError($"Something went wrong inside GetAllAccounts action: {ex}");
      				return StatusCode(500, "Internal server error");
      			}
      		}
      
      		[HttpPost]
      		public IActionResult CreateAccount([FromBody]AccountForCreationDto account)
      		{
      			try
      			{
      				if (account == null)
      				{
      					_logger.LogError("Object sent from client is null.");
      					return BadRequest("Object is null");
      				}
      
      				if (!ModelState.IsValid)
      				{
      					_logger.LogError("Invalid object sent from client.");
      					return BadRequest("Invalid model object");
      				}
      
      				var accountEntity = _mapper.Map<Account>(account);
      
      				_repository.Account.CreateAccount(accountEntity);
      				_repository.Save();
      
      				var createdAccount = _mapper.Map<AccountDto>(accountEntity);
      
      				return CreatedAtRoute("AccountById", new { id = createdAccount.Id }, createdAccount);
      			}
      			catch (Exception ex)
      			{
      				_logger.LogError($"Something went wrong inside CreateAccount action: {ex}");
      				return StatusCode(500, "Internal server error");
      			}
      		}
      	}
      Of course, the compiler won't let us build this since we need to implement GetAllAccounts and CreateAccount methods in our AccountRepository. So let's do exactly that. Add the method declarations to the IAccountRepository:
      public interface IAccountRepository
      {
      	IEnumerable<Account> GetAllAccounts();
      	void CreateAccount(Account account);
      }
      And the implementation of the AccountRepository:
      public IEnumerable<Account> GetAllAccounts()
      {
      	return FindAll()
      		.OrderBy(ac => ac.DateCreated);
      }
      
      public void CreateAccount(Account account)
      {
      	account.Id = Guid.NewGuid();
      	Create(account);
      	Save();
      }
      Now run the dotnet build on the entire solution, and the project should compile. Great, now we have a working AccountController.

      Introducing Swagger

      Swagger is one of the most popular and versatile frameworks for tooling your APIs. Here is how we are going to add it to our project. Navigate to the /AccountOwnerServer folder. After that execute the command:
      dotnet add package Swashbuckle.AspNetCore --version 5.0.0-rc5
      This will add the reference to the Swashbuckle NuGet package for ASP.NET Core to our project (5.0.0-rc5 is the current version, for now, you can use the latest one). In short, Swashbuckle will help us integrate Swagger into our application. After that, we have to enable Swagger by changing our Startup.cs file a bit. To the ConfigureServices method, just before the service.AddControllers() line, we are going to add:
      services.AddSwaggerGen(c =>
      {
      	c.SwaggerDoc("v1", new OpenApiInfo
      	{
      		Title = "AccountOwner API",
      		Version = "v1"
      	});
      });
      
      services.AddControllers();
      And to the Configure method just after the app.UseEndpoints() line:
      app.UseSwagger();
      
      app.UseSwaggerUI(c =>
      {
      	c.SwaggerEndpoint("/swagger/v1/swagger.json", "AccountOwner API V1");
      });
      Great. But, what have we gained by doing all this? If you run the application now and go navigate to http://localhost:5000/swagger/v1/swagger.json to see the information about your API presented in JSON format. This information is the power behind the more interesting part of Swagger, the interactive documentation which you can get by navigating to http://localhost:5000/swagger. If you did everything right you should see the documentation page like this one: Swagger UI This is a great way to document and explore an API. Since our application has no frontend attached, you can test the API directly using this documentation page, without the need to use external tools like Postman or Fiddler. To learn more about configuring Swagger in-depth, we suggest reading this article: Configuring and Using Swagger UI in ASP.NET Core Web API. Let's move on.

      Modifying the launchSettings.json File

      We are going to change the applicationUrl property from http://localhost:5000/ to http://0.0.0.0:5000/ because otherwise, we are going to have problems with exposing our application to the local environment.
      {
        "iisSettings": {
          "windowsAuthentication": false,
          "anonymousAuthentication": true,
          "iisExpress": {
            "applicationUrl": "http://0.0.0.0:5000/",
            "sslPort": 0
          }
        },
        "profiles": {
          "IIS Express": {
            "commandName": "IISExpress",
            "launchBrowser": false,
            "environmentVariables": {
              "ASPNETCORE_ENVIRONMENT": "Development"
            }
          },
          "AccountOwnerServer": {
            "commandName": "Project",
            "launchBrowser": false,
            "environmentVariables": {
              "ASPNETCORE_ENVIRONMENT": "Development"
            },
            "applicationUrl": "http://0.0.0.0:5000/"
          }
        }
      }

      Adding the Test Project and a Unit Test

      For testing purposes, we're going to use the xUnit tool and Moq for mocking our data. The easiest way to add a testing project in ASP.NET Core is with the dotnet CLI. Navigate to the root of the solution and type:
      dotnet new xunit -o Tests
      This will create the project using the xUnit template and restore the needed packages. After that we can add the project to the solution:
      dotnet sln AccountOwnerServer.sln add .\Tests\Tests.csproj
      And because we need to use the Moq library, we are going to navigate to Tests project and add it too:
      cd Tests
      dotnet add package Moq
      dotnet restore
      And finally, reference the projects we need in order to write unit tests:
      dotnet add reference ..\Contracts\Contracts.csproj
      That's it for the setup part. Now we can easily add our first unit test to the project. First, rename the default UnitTests1.cs file to something more appropriate like OwnerRepositoryTests.cs in which we can test our owner repository logic. Now, let's add our unit test:
      [Fact]
      public void GetAllOwners_ReturnsListOfOwners_WithSingleOwner()
      {
      	// Arrange
      	var mockRepo = new Mock<IOwnerRepository>();
      	mockRepo.Setup(repo => (repo.GetAllOwners())).Returns(GetOwners());
      
      	// Act
      	var result = mockRepo.Object.GetAllOwners().ToList();
      
      	// Assert
      	Assert.IsType<List<Owner>>(result);
      	Assert.Single(result);
      }
      
      public List<Owner> GetOwners()
      {
      	return new List<Owner>
      	{
      		new Owner
      		{
      			Id = Guid.NewGuid(),
      			Name = "John Keen",
      			DateOfBirth = new DateTime(1980, 12, 05),
      			Address = "61 Wellfield Road"
      		}
      	};
      }
      This is a simple demo test to check if the GetAllOwners method returns a list of Owner objects and if it returns exactly one owner as we defined in the GetOwners helper method. Now if you run dotnet test you should get something like this: Test Results Excellent! We have completed the preparation part of the project. We have a working ASP.NET Core application, together with interactive documentation and some unit tests. Everything is set for the next step, which is why we did all this preparation. Now it's time to dockerize our ASP.NET Core application. To check if everything works alright, run dotnet build and dotnet test. This is how we do it usually during the development phase. This time around we'll use docker images and containers to do the same thing and bring our software development skills to another level.

      Conclusion

      That wraps it up for this part of the series. Although we haven't even touched upon Docker yet, the changes we made here are important for the parts to come. These changes will help us soften the learning curve by introducing the concepts one by one. In the next part of the series, we are going to learn more about Docker, the reasons for using it, and we're going to execute some basic Docker CLI commands. You can find all the changes up to this point by checking out the docker-series-app-prepared branch of the docker-series repository. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      1857 0 0 0 follow along with this part you need .NET Core 2.0, Git, an IDE of your choice, and Docker. [sc name="part_of_series" headline="This article is part of the series"] For the purposes of this article, we are going to re-use the application from our .NET Core Series, more concretely part 6 of the series. The main reason for this decision is that if you are not familiar with the ASP.NET Core, you can go through the series and learn how to make ASP.NET Core WebApi application from scratch. Since we are going to make some changes to the code, if you want to follow along with the steps of this course, you can find the starting point on the master branch docker-series If you want to skip the preparation phase and jump right into Docker goodness, you can switch to the docker-series-app-prepared branch of the docker-series-app-prepared repo and go quickly through this section. It might be beneficial for you to follow along if you are not familiar with some of the changes we are going to implement. This is the first article of our Docker Series. You can find the other parts on our Docker Series page. These are the changes we're going to make to the project: There is a lot to cover, so let's get down to business.

      Switching to an In-Memory Database

      First things first. To be able to dockerize our app and make it work without relying on external resources (database) we are going to make some changes to the configuration. We are going to re-introduce MySQL later in the series, but for now, we are going to rely on an in-memory database to store our data into. So, let's modify our ServiceExtensions.cs class:
      public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration config)
      {
      	services.AddDbContext(o => o.UseInMemoryDatabase("accountowner"));
      }
      That's it! We successfully removed the reference to the MySQL database and added an in-memory database in a single line of code. Isn't that just great? Don't worry about the hanging connection string in the appsettings.json file, we don't need to remove it since we are going to use it in the next part.

      Introducing AccountController

      To make our API functional on its own, we are going to modify and rename existing ValuesController. So, first, we are going to rename it to AccountController, and then add some new code to it. We are going to add GetAllAccounts method and CreateAccount method which will give us more freedom to play with our API.
      [Route("api/[controller]")]
      public class AccountController : Controller
      {
      	private ILoggerManager _logger;
      	private IRepositoryWrapper _repository;
      
      	public AccountController(ILoggerManager logger, IRepositoryWrapper repository)
      	{
      		_logger = logger;
      		_repository = repository;
      	}
      
      	[HttpGet]
      	public IActionResult GetAllAccounts()
      	{
      		try
      		{
      			var accounts = _repository.Account.GetAllAccounts();
      
      			_logger.LogInfo($"Returned all accounts from database.");
      
      			return Ok(accounts);
      		}
      		catch (Exception ex)
      		{
      			_logger.LogError($"Something went wrong inside GetAllAccounts action: {ex}");
      			return StatusCode(500, "Internal server error");
      		}
      	}
      
      	[HttpPost]
      	public IActionResult CreateOwner([FromBody]Account account)
      	{
      		try
      		{
      			if (account.IsObjectNull())
      			{
      				_logger.LogError("Object sent from client is null.");
      				return BadRequest("Object is null");
      			}
      
      			if (!ModelState.IsValid)
      			{
      				_logger.LogError("Invalid object sent from client.");
      				return BadRequest("Invalid model object");
      			}
      
      			_repository.Account.CreateAccount(account);
      
      			return CreatedAtRoute("AccountById", new { id = account.Id }, account);
      		}
      		catch (Exception ex)
      		{
      			_logger.LogError($"Something went wrong inside CreateAccount action: {ex}");
      			return StatusCode(500, "Internal server error");
      		}
      	}
      }
      Of course, the compiler won't let us build this since we need to implement GetAllAccounts and CreateAccount methods in our AccountRepository. So let's do exactly that. Add the method declarations to the IAccountRepository:
      public interface IAccountRepository
      {
      	IEnumerable GetAllAccounts();
      	void CreateAccount(Account account);
      }
      And the implementation the AccountRepository:
      public IEnumerable GetAllAccounts()
      {
      	return FindAll()
      		.OrderBy(ac => ac.DateCreated);
      }
      
      public void CreateAccount(Account account)
      {
      	account.Id = Guid.NewGuid();
      	Create(account);
      	Save();
      }
      Now run the dotnet build on the entire solution, and the project should compile. And just like that, we have a working AccountController.

      Introducing Swagger

      Swagger is one of the most popular and versatile frameworks for tooling your APIs. Here is how we are going to add it to our project. Navigate to the /AccountOwnerServer folder. After that execute the command:
      dotnet add package Swashbuckle.AspNetCore --version 2.3.0
      This will add the reference to Swashbuckle package for ASP.NET Core to our project (2.3.0 is the current version, for now). In short, Swashbuckle will help us integrate Swagger into our application. After that, we have to enable Swagger by changing our Startup.cs file a bit. To the ConfigureServices method, just after the service.AddMvc() line, we are going to add:
      services.AddSwaggerGen(c =>
      {
      	c.SwaggerDoc("v1", new Info { Title = "AccountOwner API", Version = "v1" });
      });
      And to the Configure method just after the app.UseMvc() line:
      app.UseSwagger();
      
      app.UseSwaggerUI(c =>
      {
      	c.SwaggerEndpoint("/swagger/v1/swagger.json", "AccountOwner API V1");
      });
      Great. That wasn't too hard, was it? But, what have we gained by doing all this? If you run the application now and go navigate to http://localhost:5000/swagger/v1/swagger.json to see the information about your API presented in JSON format. This information is the power behind the more interesting part of Swagger, the interactive documentation which you can get by navigating to http://localhost:5000/swagger. If you did everything right you should see the documentation page like this one: Swagger UI This is a great way to document and explore an API. Since our application has no frontend attached, you can test the API directly using this documentation page, without the need to use external tools like Postman or Fiddler. Let's move on.

      Modifying the launchSettings.json File

      We are going to change the http://localhost:5000/ to http://[::]:5000/ because otherwise, we are going to have problems with exposing our application to the local environment. http://0.0.0.0:5000/ works too.
      {
        "iisSettings": {
          "windowsAuthentication": false,
          "anonymousAuthentication": true,
          "iisExpress": {
            "applicationUrl": "http://[::]:5000/",
            "sslPort": 0
          }
        },
        "profiles": {
          "IIS Express": {
            "commandName": "IISExpress",
            "launchBrowser": false,
            "environmentVariables": {
              "ASPNETCORE_ENVIRONMENT": "Development"
            }
          },
          "AccountOwnerServer": {
            "commandName": "Project",
            "launchBrowser": false,
            "environmentVariables": {
              "ASPNETCORE_ENVIRONMENT": "Development"
            },
            "applicationUrl": "http://[::]:5000/"
          }
        }
      }

      Adding the Test Project and a Unit Test

      For the testing purposes, we're going to use xUnit tool and Moq for mocking our data. The easiest way to add a testing project in ASP.NET Core is with the dotnet CLI. Navigate to the root of the solution and type:
      dotnet new xunit -o Tests
      This will create the project using the xUnit template and restore the needed packages. After that we can add the project to the solution:
      dotnet sln AccountOwnerServer.sln add .TestsTests.csproj
      And because we need to use Moq library, we are going to navigate to Tests project and add it too:
      cd Tests
      dotnet add package Moq
      dotnet restore
      And finally, reference the projects we need in order to write unit tests:
      dotnet add reference ..ContractsContracts.csproj
      That's it for the setup part. Now we can easily add our first unit test to the project. First, rename the default UnitTests1.cs file to something more appropriate like OwnerRepositoryTests.cs in which we can test our owner repository logic. Now, let's add our unit test:
      [Fact]
      public void GetAllOwners_ReturnsListOfOwners_WithSingleOwner()
      {
      	// Arrange
      	var mockRepo = new Mock();
      	mockRepo.Setup(repo => (repo.GetAllOwners())).Returns(GetOwners());
      
      	// Act
      	var result = mockRepo.Object.GetAllOwners().ToList();
      
      	// Assert
      	Assert.IsType>(result);
      	Assert.Single(result);
      }
      
      public List GetOwners()
      {
      	return new List
      	{
      		new Owner
      		{
      			Id = Guid.NewGuid(),
      			Name = "John Keen",
      			DateOfBirth = new DateTime(1980, 12, 05),
      			Address = "61 Wellfield Road"
      		}
      	};
      }
      This is a simple demo test to check if the GetAllOwners method returns a list of Owner objects and if it returns exactly one owner like we defined in GetOwners helper method. Now if you run dotnet test you should get something like this: Test Results Great! We have completed the preparation part of the project. We have a working ASP.NET Core application, together with interactive documentation and some unit tests. Everything is set for the next step, which is why we did all this preparation for and that's the Dockerization of our ASP.NET Core application. To check if everything works alright, run dotnet build and dotnet test. This is how we do it usually during the development phase. But, to lift this up a level, we're going to try it out by using Docker images and containers in this series.

      Conclusion

      That wraps it up for this part of the series. Although we haven't touched Docker yet, the changes we made here are important for the parts to come. These changes will help us soften the learning curve by introducing the concepts one by one. In the next part, we are going to learn more about Docker, the reasons for using it, and we're going to execute some basic Docker CLI commands. You can find all the changes up to this point by checking out the docker-series-app-prepared branch of the docker-series repository. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      255 0 0 256 https://code-maze.com/ 255 0 921 0 0 922 https://code-maze.com/ 921 0 924 922 0 925 https://code-maze.com/ dotnet --info command. If that's the case, check out this page and download the same SDK as you have on the Windows machine. https://dotnet.microsoft.com/download/dotnet-core/2.2]]> 924 0 938 .csproj file contained a reference to a library (IdentityTokens) on disk and Ubuntu's SDK was failing to publish. After changing reference from disk to nuget everything goes smoothly and publishes normally on Linux.]]> 925 0 939 https://code-maze.com/ 938 0 946 939 0 947 https://code-maze.com/ 946 0 948 947 0
      Angular - How To Handle Errors with Angular Error Handling https://code-maze.com/net-core-web-development-part11/ Mon, 26 Mar 2018 05:04:54 +0000 https://code-maze.com/?p=1936 the following link: Introduction of the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. The source code is available at GitHub .NET Core, Angular and MySQL. Part 11 - Source Code This post is divided into several sections:

      Creating the 500 (Internal Server Error) Component

      In the folder error-pages create a complete file and folder structure by typing the AngularCLI command:
      ng g component error-pages/internal-server
      Let’s modify the internal-server.component.ts:
      import { Component, OnInit } from '@angular/core';
      
      @Component({
        selector: 'app-internal-server',
        templateUrl: './internal-server.component.html',
        styleUrls: ['./internal-server.component.css']
      })
      export class InternalServerComponent implements OnInit {
        public errorMessage: string = "500 SERVER ERROR, CONTACT ADMINISTRATOR!!!!";
      
        constructor() { }
      
        ngOnInit() {
        }
      
      }
      Then modify the internal-server.component.html:
      <p>
        {{errorMessage}}
      </p>
      
      Modify the internal-server.component.css as well:
      p{
          font-weight: bold;
          font-size: 50px;
          text-align: center;
          color: #c72d2d;
      }
      
      Finally, modify the app.module.ts:
      RouterModule.forRoot([
            { path: 'home', component: HomeComponent },
            { path: 'owner', loadChildren: "./owner/owner.module#OwnerModule" },
            { path: '404', component: NotFoundComponent},
            { path: '500', component: InternalServerComponent },
            { path: '', redirectTo: '/home', pathMatch: 'full' },
            { path: '**', redirectTo: '/404', pathMatch: 'full'}
          ])
      
      That’s it. We have created our component and it is time to create a service for error handling.

      Creating a Service for Angular Error Handling

      In the folder shared/services create a new service and name it error-handler.service.ts. Let’s modify that error-handler.service.ts file:
      import { HttpErrorResponse } from '@angular/common/http';
      import { Router } from '@angular/router';
      import { Injectable } from '@angular/core';
      
      @Injectable()
      export class ErrorHandlerService {
        public errorMessage: string = '';
      
        constructor(private router: Router) { }
      
        public handleError(error: HttpErrorResponse){
          if(error.status === 500){
            this.handle500Error(error);
          }
          else if(error.status === 404){
            this.handle404Error(error)
          }
          else{
            this.handleOtherError(error);
          }
        }
      
        private handle500Error(error: HttpErrorResponse){
          this.createErrorMessage(error);
          this.router.navigate(['/500']);
        }
      
        private handle404Error(error: HttpErrorResponse){
          this.createErrorMessage(error);
          this.router.navigate(['/404']);
        }
      
        private handleOtherError(error: HttpErrorResponse){
          this.createErrorMessage(error);
          //TODO: this will be fixed later;
        }
      
        private createErrorMessage(error: HttpErrorResponse){
          this.errorMessage = error.error ? error.error : error.statusText;
        }
      
      }
      Don't forget to import this service into the app.module.ts file, and to register it inside the providers array. First of all, we are injecting the Router which we use for redirecting the user to other pages in the code. In the method handleError(), we are checking for the error’s status code and based on that we are calling the right private method for handling that error. The handle404Error() and handle500Error() functions are responsible for populating the errorMessage property. We are going to use this property as a modal error message or an error page message. We are going to deal with the handleOtherError() function later, thus the comment inside. If you remember in the owner-list.component.ts file, we are fetching all the owners from the server. But there is no error handling in that file. So let’s continue with modifying that owner-list.component.ts file to implement the Angular error handling functionality:
      import { Component, OnInit } from '@angular/core';
      import { RepositoryService } from './../../shared/services/repository.service';
      import { Owner } from './../../_interfaces/owner.model';
      import { ErrorHandlerService } from './../../shared/services/error-handler.service';
      
      @Component({
        selector: 'app-owner-list',
        templateUrl: './owner-list.component.html',
        styleUrls: ['./owner-list.component.css']
      })
      export class OwnerListComponent implements OnInit {
        public owners: Owner[];
        public errorMessage: string = '';
      
        constructor(private repository: RepositoryService, private errorHandler: ErrorHandlerService) { }
      
        ngOnInit() {
          this.getAllOwners();
        }
      
        public getAllOwners() {
          let apiAddress: string = "api/owner";
          this.repository.getData(apiAddress)
            .subscribe(res => {
              this.owners = res as Owner[];
            },
            (error) => {
              this.errorHandler.handleError(error);
              this.errorMessage = this.errorHandler.errorMessage;
            })
        }
      }
      That’s it. You can try it out by changing the code in the server’s method GetAllOwners. As the first line of code add return NotFound() or return StatusCode(500, “Some message”), and you are going to be redirected to the right error page for sure.

      Preparation for the Owner-Details Component

      Let’s continue by creating a component for the owner details. Create a new folder and owner-details component files inside the owner folder and import that component inside the owner.module.ts file. owner-details structure in Angular Error Handling If you prefer, you can use the AngularCLI to achieve the same effect:
      ng g component owner/owner-details
      We need to modify the owner.module.ts file:
      RouterModule.forChild([
            { path: 'list', component: OwnerListComponent },
            { path: 'details/:id', component: OwnerDetailsComponent }
          ])
      
      As you might have noticed, the new path now has the parameter id. So when we click on the Details button we are going to pass this Id to our route and we are going to fetch the owner with that exact id in the OwnerDetails component. We need to add a new interface to the _interfaces folder:
      export interface Account{
          id: string;
          dateCreated: Date;
          accountType: string;
          ownerId?: string;
      }
      
      And modify the Owner interface:
      import { Account } from './account.model';
      
      export interface Owner{
          id: string;
          name: string;
          dateOfBirth: Date;
          address: string;
      
          accounts?: Account[];
      }
      By using a question mark, we are making our property as optional. Change the owner-list.component.html:
      <button type="button" id="details" class="btn btn-default" 
             (click)="getOwnerDetails(owner.id)">Details</button>
      On a click event, we are calling the getOwnerDetails function and passing the owner's id as a parameter. So we need to handle that click event in our owner-list.component.ts file. Add an import statement:
      import { Router } from '@angular/router';
      Then modify constructor and add the function getOwnerDetails(id):
      constructor(private repository: RepositoryService, private errorHandler: ErrorHandlerService, 
          private router: Router) { }
      public getOwnerDetails(id){
          let detailsUrl: string = `/owner/details/${id}`
          this.router.navigate([detailsUrl]);
        }
      

      Implementation of the Owner-Details Component

      We have all the code for supporting the owner-details component. Now it is time to implement business logic inside that component. Firstly, modify the owner-details.component.ts file:
      import { Component, OnInit } from '@angular/core';
      import { Owner } from './../../_interfaces/owner.model';
      import { Router, ActivatedRoute } from '@angular/router';
      import { RepositoryService } from './../../shared/services/repository.service';
      import { ErrorHandlerService } from './../../shared/services/error-handler.service';
      
      @Component({
        selector: 'app-owner-details',
        templateUrl: './owner-details.component.html',
        styleUrls: ['./owner-details.component.css']
      })
      export class OwnerDetailsComponent implements OnInit {
        public owner: Owner;
        public errorMessage: string = '';
      
        constructor(private repository: RepositoryService, private router: Router, 
                    private activeRoute: ActivatedRoute, private errorHandler: ErrorHandlerService) { }
      
        ngOnInit() {
          this.getOwnerDetails()
        }
      
        getOwnerDetails(){
          let id: string = this.activeRoute.snapshot.params['id'];
          let apiUrl: string = `api/owner/${id}/account`;
      
          this.repository.getData(apiUrl)
          .subscribe(res => {
            this.owner = res as Owner;
          },
          (error) =>{
            this.errorHandler.handleError(error);
            this.errorMessage = this.errorHandler.errorMessage;
          })
        }
      
      }
      It is pretty much the same logic as in the owner-list.component.ts file, except now we have the ActivatedRoute imported because we have to get our id from the route. After we execute the getOwnerDetails function, we are going to store the owner object with all related accounts inside the owner property. All we have to do is to modify the owner-details.component.html file:
      <div class="well">
        <div class="row">
          <div class="col-md-3">
            <strong>Owner name:</strong>
          </div>
          <div class="col-md-3">
            {{owner?.name}}
          </div>
        </div>
        <div class="row">
          <div class="col-md-3">
            <strong>Date of birth:</strong>
          </div>
          <div class="col-md-3">
            {{owner?.dateOfBirth | date: 'dd/MM/yyyy'}}
          </div>
        </div>
        <div class="row" *ngIf='owner?.accounts.length <= 2; else advancedUser'>
          <div class="col-md-3">
            <strong>Type of user:</strong>
          </div>
          <div class="col-md-3">
            <span class="text-success">Beginner user.</span>
          </div>
        </div>
        <ng-template #advancedUser>
          <div class="row">
            <div class="col-md-3">
              <strong>Type of user:</strong>
            </div>
            <div class="col-md-3">
              <span class="text-info">Advanced user.</span>
            </div>
          </div>
        </ng-template>
      </div>
      
      <div class="row">
        <div class="col-md-12">
          <div class="table-responsive">
            <table class="table table-striped">
              <thead>
                <tr>
                  <th>Account type</th>
                  <th>Date created</th>
                </tr>
              </thead>
              <tbody>
                <tr *ngFor="let account of owner?.accounts">
                  <td>{{account?.accountType}}</td>
                  <td>{{account?.dateCreated | date: 'dd/MM/yyyy'}}</td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </div>
      
      In this code example, we are conditionally displaying the owner entity. Moreover, we are displaying all the accounts related to this owner: Beginner user - Angular Error Handling   owner-details 2 Angular Error Handling

      Conclusion

      By reading this post you have learned:
      • How to handle errors in separate service by using Angular Error Handling
      • The way to use the conditional rendering of the HTML page
      • How to create details page for your entity
      Thank you for reading the post, hopefully, it was helpful to you. In the next part of the series, I am going to show you how to create child components and how to use @Input, @Output, and EventEmmiters in Angular. By doing so, you are going to learn to split your components into smaller parts (parent-child relation). [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      1936 0 0 0 225 0 0 226 225 0 227 226 0 228 226 0 229 226 0 230 228 0 231 230 0 1452 0 0 1453 1452 0 1456 1453 0 1588 0 0
      Why Docker: The Docker CLI Through Examples https://code-maze.com/why-docker-docker-cli-examples/ Thu, 03 May 2018 06:20:21 +0000 https://code-maze.com/?p=1988 prepared our ASP.NET Core application, we are going to learn how to install and use Docker on Windows 10, the reasons behind using Docker, and some useful Docker CLI commands. Understanding how the Docker CLI works is crucial, and you'll most definitely have a hard time proceeding to the next part without getting a grasp of the basic commands Docker offers through its powerful CLI. [sc name="part_of_series" headline="This article is part of the series"] This is the second article of our Docker Series. You can find the other parts on our Docker Series page. In this part, we are going to talk about: Let's get down to business.

      Short Intro to Docker on Windows

      Docker Desktop for Windows, as well as some basic instructions, can be found in the docker store. The installation is pretty straightforward and you can easily test if it worked with a quick docker --version command. We recommend using PowerShell for all the docker commands we use since it's more verbose than plain old Command Prompt. Docker basically relies on Windows 10 Hyper-V to instantiate the virtual machines which we need as a base for our containers. If you haven't enabled it, you'll need to do it in order to proceed. But before that, let's go through some very basic concepts regarding Docker and ASP.NET Core. As you know, Docker images consist of a bunch of layers witch attribute to the final environment inside that image. There are a lot of predefined images we can pull from the Docker Hub, but we can also make our own images by adding our layers on top of existing ones to create an image that suits our needs. On Windows 10, Docker lets you choose between Linux and Windows containers, which is a very nice feature. In this series, we are going to stick with the Linux containers, but the principles apply both to Windows or Linux containers. The syntax and underlying architecture (eg. paths) may vary a bit.

      Why Docker?

      Before we dive completely into Dockerfiles, compositions and advanced features of Docker, let's play around a bit with the Docker CLI. Understanding how the Docker CLI works will help you later when we move on to Dockerfiles. So, we have an application to play with now, it builds, the tests are passing and we can run it on a local machine. The starting point application can be downloaded from the docker-series-app-prepared branch of the docker-series repo on GitHub. So, how do we start working with Docker? When we started learning Docker, the main problem we had was to grasp the big picture. It looked like just some complicated tool that produces an overhead in our projects. We've been already familiar with continuous integration and delivery, and Docker didn't seem to fit into our CI/CD pipelines. So we'll try to explain why we use Docker and how to use the Docker CLI with our ASP.NET Core app.

      Scenario One: Using Docker to Keep Local Environments Clean

      Let's say you have a Java-oriented friend and you want to explain to him why .NET Core kicks ass. We'll call him Mike. Mike is quite a stubborn lad and doesn't want to install all that Microsoft mumbo-jumbo on his machine, which is perfectly understandable. .NET SDK and Visual Studio can take time and disk space to install and Mike doesn't want that. But, Mike has heard how awesome Docker is and he even installed it at one time on his machine some time ago. So how do we help Mike start with .NET Core as soon as possible? First, give him some ASP.NET Core application to clone. For example our application. Next, navigate to the root of the project and build the project by typing (works only in PowerShell):
      docker run --rm -it -w /home/app/AccountOwnerServer/ -v ${PWD}:/home/app mcr.microsoft.com/dotnet/core/sdk:3.1 dotnet build
      Followed by:
      docker run --rm -it -w /home/app/AccountOwnerServer/ -p 8080:5000 -v ${PWD}:/home/app mcr.microsoft.com/dotnet/core/sdk:3.1 dotnet run
      And just like that we have built and run our application on Mike's machine even though he doesn't have any Microsoft dependencies installed. Now you can run the application on your local machine by typing http://localhost:8080/swagger/ in your browser and you'll get the familiar Swagger docs page. Isn't that just awesome? But those commands look really complicated. Let's break them down.

      A Bit of Clarification

      These commands look scary when you see them for the first time but they are not that complicated once you get familiar with the syntax. The actual command is docker run, followed by a few options, and then the image name, which is the name of the one we've built in the last step. Let's explain what each option stands for:
      • --rm: Automatically removes the container once it stops. If you exclude this command you'll see the Docker container is still attached by typing docker ps -a
      • -it: This command is actually comprised of two parts, -i which means interactive mode, and -t which enables the pseudo-terminal. These commands can be used separately to get the same result, but they are usually used together. The -it command basically gives us the means to see and interact with our application through the terminal.
      • -w /home/app/AccountOwnerServer/: Sets the current working directory for the container when it gets instantiated. In our case that will be /home/app/AccountOwnerServer/ where the main app resides, so we can run our commands from there
      • -p: Maps the external port (left) to the internal port (right) of the Docker container. In our case, this means that we can access our application through the http://localhost:8080 on our local machine, although it runs on a port 5000 inside the Docker container
      • -v ${PWD}:/home/app: This one is a bit tricky but once you get it, it can do wonders. What this actually means is that it maps our current directory to the /home/app directory inside the container. This enables us to change the code on our local system and build it in the container. You can learn more about Docker volumes here.
      The options are followed by the image name, and finally the command we want to run inside the instantiated container. So, what does this mean for Mike the Java programmer? This means Mike can safely change the code on his machine using any IDE, and then run the container to see the results of his work without having had to install any of the dependencies on his machine. You can now watch Mike and enjoy his conversion to C#. :)

      Scenario Two: Using Docker to Test Your App in a Clean Environment

      Mike is now a full-fledged C# programmer. He tried it and he loved it so much that he decided to install the .NET Core SDK on his own machine. Since Mike is a web developer in heart, he wants to make a small ASP.NET Core application to learn how web programming works in .NET Core. By sheer luck, he stumbled across the Code Maze blog and found out there is a great tutorial on how to create ASP.NET Core app and how to prepare it for dockerization. Just what he needs. But since Mike is a good developer and knows about the "It works on my machine" phenomenon, he wants to run the app in a clean environment to test it out and make sure the app works every time. What can Mike do in this situation? He can, of course, build his application on some of the many available CI/CD tools, and then deploy the application on the staging machine. Or he can publish directly using the Visual Studio publish tools. And he needs to do that both for Windows and Linux. But there might be a faster and easier way he can do that: spin up a local docker container optimized for the ASP.NET Core runtime. How can Mike do that? We learned some commands in the previous example, so let's use them here too. We won't rely on mcr.microsoft.com/dotnet/core/sdk:3.1 image since Mike builds his application locally. What he wants to use is the mcr.microsoft.com/dotnet/core/aspnet:3.1 image which is optimized for runtime. But first, he needs to publish his app and make a small Dockerfile in the AccountOwnerServer folder that will instruct Docker to copy the contents of the publish folder to the container. So, Mike should navigate to /AccountOwnerServer and then type dotnet publish. This will publish the application into the ./bin/Debug/netcoreapp3.1/publish directory. After that, he needs to add the Dockerfile to the project root:
      FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
      
      WORKDIR /home/app
      COPY bin/Debug/netcoreapp3.1/publish .
      
      ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"]
      Build his own image using that Dockerfile (make sure not to forget to put the dot at the end of the command):
      docker build -t accountownerapp .
      And then simply run the image:
      docker run -p 8080:80 accountownerapp
      That's it, Mike has run the application on a clean environment and now he can sleep peacefully because he knows that his app can run on more than just his machine.

      Scenario Three: Persisting the Changes and Cross-Platform Development

      Mike has been working now for a few weeks on his ASP.NET Core app. But he has already planned a nice trip with his family and he won't be able to continue working on his desktop machine. But he brings his Mac wherever he goes. Up until now, Mike has been working locally and using the local Docker environment. So in order to continue working in the evening, after his long walks through the woods, he needs a mechanism to persist his work and bring it with him. So how can Mike do this?

      Creating the Image and Pushing It to Docker Hub

      First, he needs to create an account on Docker Hub. It's an easy process and in a few minutes, Mike has a Docker Hub account. Then he can create his own repository and name it MikesDemoApp: DockerHub create repository To be able to work on the project, he needs to use mcr.microsoft.com/dotnet/core/sdk:3.1 image, optimized to build and run ASP.NET Core applications. So, he needs to modify the Dockerfile a bit:
      FROM mcr.microsoft.com/dotnet/core/sdk:3.1
      
      WORKDIR /home/app
      COPY . .
      And build his own image and tag it mikesaccount/mikesdemoapp:
      docker build -t mikesaccount/mikesdemoapp .
      And then he can log in to his Docker Hub account and push the image through the Docker CLI:
      docker login -u mikesaccount -p SomeCoolPassword
      docker push mikesaccount/mikesdemoapp
      
      The result in the console should look something like this: Docker push result And now Mike can navigate to the "Tags" tab on the Docker Hub repository to see his image: DockerHub Now Mike can safely pull the image to his Mac and work some more on his app before he goes to sleep:
      docker pull mikesaccount/mikesdemoimage
      

      Conclusion

      These scenarios are just a few of the unlimited number of possible Docker usages. You have seen how powerful the Docker CLI can be but we've just scratched the surface. Docker's full potential can only be seen when you deploy multiple microservices and connect them together. You can dockerize everything. Applications, databases, monitoring tools, CI tools... Name it and there is an image of it. I encourage you to play around with the Docker CLI and learn a bit more about it since it's a good foundation for what's coming up next: Dockerization of ASP.NET Core application using Dockerfiles. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      1988 0 0 0 144 http://devepar.com/archives/369 0 0 153 http://devepar.com/archives/4819 0 0 418 https://sudhakaryblog.wordpress.com/2018/10/04/docker-for-windows-series-articles/ 0 0 634 http://overpowered.it/ docker run --rm -it -w /home/app/Promoting/ -v ${PWD}:/home/app microsoft/dotnet:2.2-sdk dotnet build and docker run --rm -it -w /home/app/Promoting/ -p 8080:6660 -v ${PWD}:/home/app microsoft/dotnet:2.2-sdk dotnet run my project loses all the nuget packages. PackageManagerConsole: Unable to find fallback package folder '/usr/share/dotnet/sdk/NuGetFallbackFolder'. The bin starts and it listens ad ports http->6660 and https->6661 But im unable to navigate the swagger (or any other page) http://localhost:8080/swagger -> ERR_EMPTY_RESPONSE CLI: Hosting environment: Development Content root path: /home/app/Promoting Now listening on: https://[::]:6661 Now listening on: http://[::]:6660]]> 0 0 708 https://code-maze.com/ 634 0 718 http://overpowered.it/ Hosting environment: Development Content root path: /home/app/AccountOwnerServer Now listening on: http://localhost:5000 Application started. Press Ctrl+C to shut down. As you can see from the attachment, the nuget packages disappear after the `dotnet build`. But the app inside docker, is running. dotnet restore fix the problem (until next build). https://uploads.disquscdn.com/images/9eba02f2b58fc57ee10e0975972d7103c7e62c2f1a301b9d63c02b8013482904.jpg Any ideas? Thanks]]> 708 0
      Dockerizing ASP.NET Core Application With Dockerfiles https://code-maze.com/aspnetcore-app-dockerfiles/ Mon, 07 May 2018 06:17:02 +0000 https://code-maze.com/?p=1990 previous part, we've gone through some scenarios to show off the Docker's potential. We have also seen how to utilize Docker CLI and even made the simplest Dockerfiles to learn how to build the images using Docker. In this article, we are going to focus on dockerizing our ASP.NET Core application with Dockerfiles, and understanding how Dockerfile syntax works. We are also going to spend some effort in optimizing our images to achieve the best results. [sc name="part_of_series" headline="This article is part of the series"] This is the third part of our Docker Series. You can find all the parts on our Docker Series page. To follow along with the steps of this tutorial you need to get the docker-series-app-prepared branch of our docker-series repo. There are a few steps we need to do in order to dockerize ASP.NET Core application: So let's begin.

      Creating a Dockerfile

      The first step we need to do is to navigate to the root folder of our solution and make a new Dockerfile. Dockerfiles are the declarative inputs that we can use to tell Docker what to do with our application. Let's add some actions to our Dockerfile:
      FROM mcr.microsoft.com/dotnet/core/sdk:3.1
      
      WORKDIR /home/app
      
      COPY . .
      
      RUN dotnet restore
      
      RUN dotnet publish ./AccountOwnerServer/AccountOwnerServer.csproj -o /publish/
      
      WORKDIR /publish
      
      ENV ASPNETCORE_URLS="http://0.0.0.0:5000"
      
      ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"]

      A Bit of Explanation

      So what have we done exactly?
      • FROM mcr.microsoft.com/dotnet/core/sdk:3.1: Every Dockerfile starts with the FROM command, which initializes a new build stage and sets the base image that we are going to build upon. In this case, that image is mcr.microsoft.com/dotnet/core/sdk:3.1 image because we want to build our ASP.NET Core application with SDK version 3.1. Microsoft has a plethora of other useful images on their Docker Hub profile, so go check them out.
      • WORKDIR /home/app: The WORKDIR command simply sets the current working directory inside our image. In this case, that is the /home/app folder.
      • COPY . .: The COPY command is pretty straightforward too. In this case, it copies all the files from the local system to the current working directory of the image. Since we don't need to copy all the files to build the project, we're going to use a .dockerignore file to which the COPY command will look up when it starts copying the files.
      • RUN dotnet restore: The RUN command runs any command in a new layer and commits it to the base image. Concretely, in this step, we are restoring the packages for our solution, as we would if we run it locally, but this time it's happening inside the image.
      • RUN dotnet publish ./AccountOwnerServer/AccountOwnerServer.csproj -o /publish/: After we restore the packages, the next step is to publish our application. Since AccountOwnerServer is our main app, we are going to publish it to the /publish folder inside our image.
      • WORKDIR /publish: We are switching our current directory to /publish
      • ENV ASPNETCORE_URLS="http://0.0.0.0:5000": Since launchSettings.json is a file that's just used by our local IDE, it won't make it to the publish folder. This means we need to set the application URL manually. Once again, we can't use localhost or we won't be able to bind the port from the docker container to the local environment.
      • ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"]: The ENTRYPOINT command allows us to configure the container to run as an executable. In this case, after the project has been published and we run the image, a container will spin up by firing dotnet AccountOwnerServer.dll command which will start our application.

      Creating a .dockerignore File

      Since we are copying our files to the docker image on every build, we should create a .dockerignore file and select which files and folders we don't want to copy every time. The advantages of using a .dockerignore include faster image build, improving cache performance, and avoiding potential conflicts when building an application. For example, we don't want to copy our Dockerfile. Why is that? Because besides not being important to the build process, that would mean our COPY step will trigger everytime we change the Dockerfile, and that's not something we want. So you want to put all the files that you don't want to trigger a build into a .dockerignore file. For starters, we are going put these files and folders in our .dockerignore file.:
      **/bin/
      **/obj/
      **/global.json
      **/Dockerfile*
      **/.dockerignore*
      **/*.user

      Building the image

      Once we configured our Dockerfile and .dockerignore, we can start the build and tag our image:
      docker build -t codemazeblog/accountowner:build .
      The first time you run the docker build, it will take a while, since Docker is fetching the base image. Give it some time. After the first build, every following build will use that image from the local machine. First build is fun and you get to see every step of our Dockerfile resolving in real-time. After the first build, if you don't change the project files, all the steps will be cached and you'll get something like this: Powershell build image As you can see every step is cached, and there is no need to rebuild the image. If you want you can force the rebuild with the --no-cache flag.
      docker build --no-cache -t codemazeblog/accountowner:build .
      If you run the docker images command, now you can find our image on the list: Powershell docker images Okay, now we're ready to run our application.

      Running the Image

      Finally, to run the application you can spin up the container by typing:
      docker run --rm -it -p 8080:5000 codemazeblog/accountowner:build
      
      This should run our application as we expect it to so far. But, take a quick look at the image size. It's the biggest so far. That's because we copied, restored and published our code in the same image. Let's try to optimize the process.

      Optimizing the Dockerfile

      As we have learned by now, Docker images consist of different layers, and we've seen the Docker build process. It goes step by step and executes the steps we defined. Once the step is executed, it gets cached, and if there are no changes related to that step it gets pulled from the image cache next time we run the build. So, knowing all this can you guess what we could have done better in our Dockerfile? Well, let's have a look at that third step once again: COPY . ., and then we do RUN dotnet restore. What this means is that whenever we make a change to the source code, we need to run dotnet restore since that will break the cache. So instead of copying all the files, we can just copy the project and solution files, do the dotnet restore, and then copy the rest of the files:
      FROM mcr.microsoft.com/dotnet/core/sdk:3.1
      
      WORKDIR /home/app
      
      COPY ./AccountOwnerServer/AccountOwnerServer.csproj ./AccountOwnerServer/
      COPY ./Contracts/Contracts.csproj ./Contracts/
      COPY ./Repository/Repository.csproj ./Repository/
      COPY ./Entities/Entities.csproj ./Entities/
      COPY ./LoggerService/LoggerService.csproj ./LoggerService/
      COPY ./Tests/Tests.csproj ./Tests/
      COPY ./AccountOwnerServer.sln .
      
      RUN dotnet restore
      
      COPY . .
      
      RUN dotnet publish ./AccountOwnerServer/AccountOwnerServer.csproj -o /publish/
      
      WORKDIR /publish
      
      ENV ASPNETCORE_URLS="http://0.0.0.0:5000"
      
      ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"]
      There, now the dotnet restore won't trigger whenever we change something in our source code. We'll only need to re-publish the assemblies. But why do we copy every project and solution file manually? That's because something like COPY ./**/*.csproj ./ wouldn't work. It won't recreate the folder structure. So you would just get the flat structure with all the project and solution files. And that's no good.

      Optimizing Even Further

      We can push the optimization even further. The downside of the current way of creating the Dockerfile is that we need to manually copy each solution and project file. That means we need to change the Dockerfile whenever our solution structure changes, and it just looks bad. It's a lot of work. So let's see how we can remedy that. Take a look at the next Dockerfile:
      FROM mcr.microsoft.com/dotnet/core/sdk:3.1
      
      WORKDIR /home/app
      
      COPY ./*.sln ./
      COPY ./*/*.csproj ./
      RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done
      
      RUN dotnet restore
      
      COPY . .
      
      RUN dotnet publish ./AccountOwnerServer/AccountOwnerServer.csproj -o /publish/
      
      WORKDIR /publish
      
      ENV ASPNETCORE_URLS="http://0.0.0.0:5000"
      
      ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"]
      So, what happened here? In short, we used some clever syntax as a workaround for the COPY command to recreate the folder structure we need. The command uses the project names to create folders for the files to be copied in. To avoid unnecessary explanations here, you can read more about the command in the Andrew Lock's blog post. Now we don't need to change the Dockerfile even if the add more projects to our solution or change project names. Clear and magical.

      Just Run and Test Boys, Run, and Test

      There is one more tiny little thing we need to do. We need to run our unit test to see if the project is even worth publishing! So let's add that to our Dockerfile as well:
      FROM mcr.microsoft.com/dotnet/core/sdk:3.1
      
      WORKDIR /home/app
      
      COPY ./*.sln ./
      COPY ./*/*.csproj ./
      RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done
      
      RUN dotnet restore
      
      COPY . .
      
      RUN dotnet test ./Tests/Tests.csproj
      
      RUN dotnet publish ./AccountOwnerServer/AccountOwnerServer.csproj -o /publish/
      
      WORKDIR /publish
      
      ENV ASPNETCORE_URLS="http://0.0.0.0:5000"
      
      ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"]
      That's it. Rebuild the image again and check out the result. Once you see the tests pass, change the Assert.IsType<List<Owner>>(result); to Assert.IsNotType<List<Owner>>(result); in our OwnerRepositoryTests.csfile and run the Docker build again. You should get something like this: Test Results Failed The test has failed, and the build has stopped. The publish step hasn't been triggered, which is exactly what we want. Excellent. We've gone through the entire process, but there is one thing that some of you might have noticed. That's not something you want to use to run your containers from. SDK images are powerful and we use to build and run applications. Nevertheless, to deploy our application to a production environment, we should create an image that is optimized for that purpose only. In comparison to SDK images, runtime-only images are much lighter. So let's see how to upgrade our Dockerfile and publish our application to the runtime-optimized image.

      Creating Multistage Builds in Dockerfiles

      For this purpose, we are going to use something called a multistage build in the Docker world. Multistage builds can be created by using FROM command multiple times in a Dockefile. So we are going to do just that to upgrade our process. Instead of using our SDK image to publish our application to, we are going to introduce another base image to the Dockerfile and publish the artifacts inside it. Let's see how to do that:
      FROM mcr.microsoft.com/dotnet/core/sdk:3.1 as build-image
      
      WORKDIR /home/app
      
      COPY ./*.sln ./
      COPY ./*/*.csproj ./
      RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done
      
      RUN dotnet restore
      
      COPY . .
      
      RUN dotnet test ./Tests/Tests.csproj
      
      RUN dotnet publish ./AccountOwnerServer/AccountOwnerServer.csproj -o /publish/
      
      FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
      
      WORKDIR /publish
      
      COPY --from=build-image /publish .
      
      ENV ASPNETCORE_URLS="http://0.0.0.0:5000"
      
      ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"]
      So, what's new here? First, we added as build-image to the first FROM command for easier referencing later. Second, instead of running the application inside the SDK image, we copied the contents of the publish folder inside our SDK image to the publish folder of the runtime image. Third, we moved the entry point to the runtime image, so that we run the application when we instantiate the runtime container. The SDK container shall no longer be responsible for running our application. So, what's the result of all this? Let's see. Type the docker build -t codemazeblog/accountowner:runtime . command and enjoy the process. Powershell docker images comparison When the build finishes, we get two images. One is tagged and one is not. One is runtime and one is SDK. In this case, we are not interested in the SDK image, but you can clearly see the size difference. So that's it, now if you run docker run --rm -it -p 8080:5000 codemazeblog/accountowner:runtime you'll get the same result and you'll be able to see your application at the http://localhost:8080/swagger. Let's wrap the article up with the commands you might find useful while following these steps. All the changes we made in this article can be found on the docker-series-dockerfiles-end branch of our docker-series repo.

      Some Useful Commands

      Here are some of Docker commands that might help you along the way:
      • docker images - lists all Docker images on your machine
      • docker ps - lists all running Docker containers on your machine
      • docker ps -a - lists all the attached but not running containers
      • docker --help - Docker help (no s..t Sherlock)
      • docker logs <container_name> - outputs the container logs (useful when running the container in a detached mode for example)
      • docker events - outputs the events that happen on the server, depending on the Docker object (attach, detach, copy, pull...)
      • docker stop <container_name> - stops the container by name
      • docker rm <container_name> - removes the container by name
      • docker rmi <image_id> - removes the image
      • docker rmi $(docker images -q -f dangling=true) -- force - this one is particularly interesting, removes all the images that are not tagged (<none>)
      • docker rm $(docker ps -a -q) - removes all containers
      • docker rmi -f $(docker images -a -q) - removes all images

      Conclusion

      That's it for this part. We've learned how to build ASP.NET Core application with Dockerfiles, some useful commands and what each one does. We've also learned the difference between the SDK and runtime images, and that we can combine them to achieve the best results by using the multistage Docker builds. Now that we know all this we can move on to the next part, in which we connect our application to MySQL. That will also be the perfect opportunity to introduce docker compose tool, which will make our lives much easier when working with multiple images. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      1990 0 0 0 142 https://www.alvinashcraft.com/2018/05/07/dew-drop-may-7-2018-2719/ 0 0 147 http://devepar.com/archives/638 0 0 148 https://chevenix.wordpress.com/2018/05/12/szumma-109-2018-19-het-microsoft-build-2018/ 0 0 154 http://devepar.com/archives/4819 0 0 169 http://devepar.com/archives/5101 0 0 295 0 0 420 https://sudhakaryblog.wordpress.com/2018/10/04/docker-for-windows-series-articles/ 0 0 551 0 0 657 https://code-maze.com/ 551 0 743 0 0 744 https://code-maze.com/ 743 0 753 744 0 1545 C:Program FilesDockerDockerResourcesbindocker.exe: Error response from daemon: driver failed programming external connectivity on endpoint strange_hopper (f723668e6c985f84e07db63cb30ca3b8b8e944b009f209ee42ba07ae2a9c42b0): Error starting userland proxy: listen tcp 0.0.0.0:8080: bind: An attempt was made to access a socket in a way forbidden by its access permissions Is there a quick fix for this or do I need to use Windows containers?]]> 0 0
      Angular - Angular Input Output Decorators & Directives - Code Maze https://code-maze.com/net-core-web-development-part12/ Mon, 02 Apr 2018 05:01:49 +0000 https://code-maze.com/?p=2015 the following link: Introduction of the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. The source code is available at GitHub .NET Core, Angular and MySQL. Part 12 - Source Code This post is divided into several sections:

      Overview

      In the situations where we want to send some content from a parent to a child component, we need to use the @Input decorator in a child component to provide a property binding between those components. Moreover, we could have some events in a child component that reflect its behavior back to a parent component. For that purpose, we are going to use @Output decorator with the EventEmitter. To handle success messages and error messages (which are not 500 or 404 messages), we are going to create a modal window child components. We are going to reuse them in every component that requires displaying these types of messages. When you want to register your reusable component it is a good practice to create a shared-module and to register and export your components inside that module. Then, you can use those reusable components in any higher-level component you want by registering the shared module inside a module responsible for that higher-order component.

      Creating the Shared Module

      Let’s start by creating the shared module in the shared folder. Then modify the shared.module.ts file:
      import { NgModule } from '@angular/core';
      import { CommonModule } from '@angular/common';
      
      @NgModule({
        imports: [
          CommonModule
        ],
        declarations: [
        ],
        exports: []
      })
      export class SharedModule { }
      
      Finally, include the SharedModule inside our owner.module.ts file:
      import { SharedModule } from './shared/shared.module';
      imports: [
          CommonModule,
          SharedModule,
      

      Error Modal Component

      Let's execute AngularCLI command to create error modal component:
      ng g component shared/modals/error-modal
      Besides creating component files, this command is going to import a new component into the shared-module. But you need to export it manually:
      declarations: [
          ErrorModalComponent
        ],
        exports: [
          ErrorModalComponent
        ]
      error modal @Input, @Output In the modals folder, create one more CSS file with a name modal-shared.component.css. We are creating this file because we are going to have the same CSS for error and success modal windows without repeating the same code in two different CSS files. Now modify the error-modal.component.ts file:
      import { Component, OnInit, Input } from '@angular/core';
      
      @Component({
        selector: 'app-error-modal',
        templateUrl: './error-modal.component.html',
        styleUrls: ['./error-modal.component.css', '../modal-shared.component.css']
      })
      export class ErrorModalComponent implements OnInit {
        @Input() public modalHeaderText: string;
        @Input() public modalBodyText: string;
        @Input() public okButtonText: string;
        
        constructor() { }
      
        ngOnInit() {
        }
      
      }
      We are decorating our properties with the @Input decorator and by doing so our parent component can set values to these properties by using property binding. Let’s continue by modifying the error.modal.component.html file:
      <div id="errorModal" class="modal fade" role="dialog">
        <div class="modal-dialog">
          <div class="modal-content">
            <div class="modal-header">
              <button type="button" class="close" data-dismiss="modal">&times;</button>
              <h4 class="modal-title">{{modalHeaderText}}</h4>
            </div>
            <div class="modal-body">
              <p>{{modalBodyText}}</p>
            </div>
            <div class="modal-footer">
              <button type="button" class="btn btn-danger" data-dismiss="modal">{{okButtonText}}</button>
            </div>
          </div>
        </div>
      </div>
      Then modify shared modal.css file like this:
      .modal-title {
          margin: 0;
          line-height: 1.42857143;
          font-size: 30px;
          text-align: center;
      }
      
      .modal-body p {
          text-align: center;
          margin-top: 10px;
      }
      
      @media (min-width: 768px){
          .modal-dialog {
              width: 500px;
              margin: 20% auto;
          }
      }
      

      Creating Success Modal Component

      Excellent. Now we have the error modal created and let's continue by creating success modal in the same way as we did with the error modal: success modal @Input, @Output Import success-modal component into the shared.module.ts file and export it as well. Then modify your success-modal.component.ts file:
      import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
      
      @Component({
        selector: 'app-success-modal',
        templateUrl: './success-modal.component.html',
        styleUrls: ['./success-modal.component.css', '../modal-shared.component.css']
      })
      export class SuccessModalComponent implements OnInit {
        @Input() public modalHeaderText: string;
        @Input() public modalBodyText: string;
        @Input() public okButtonText: string;
        @Output() public redirectOnOK = new EventEmitter();
      
        constructor() { }
      
        ngOnInit() {
        }
      
        public emmitEvent(){
          this.redirectOnOK.emit();
        }
      
      }
      
      The @Ouput decorator with the EventEmitter is used to emit an event from a child to a parent component, and that is exactly what we want to do. We are going to use success component when create, update or delete action is completed successfully and by pressing the OK button we are going to redirect a user to the owner-list component. For this emitter to work, we need to make the subscription in the parent component to this emmitEvent function. This is going to be handled in the next lecture. Finally, modify the success-modal.component.html file:
      <div id="successModal" class="modal fade" role="dialog">
        <div class="modal-dialog">
      
          <div class="modal-content">
            <div class="modal-header">
              <button type="button" class="close" data-dismiss="modal" (click)="emmitEvent()">&times;</button>
              <h4 class="modal-title">{{modalHeaderText}}</h4>
            </div>
            <div class="modal-body">
              <p>{{modalBodyText}}</p>
            </div>
            <div class="modal-footer">
              <button type="button" class="btn btn-success" data-dismiss="modal" (click)="emmitEvent()">{{okButtonText}}</button>
            </div>
          </div>
      
        </div>
      </div>
      

      Invoking Child Component

      Great. Now we have both of our child reusable components. For now, we still don’t have a place to invoke our child components, but for example, if you want to invoke your error-modal component into some parent component all you have to do is to add its selector into the parent component:
      <app-error-modal [modalHeaderText]="'some string in here'"
      [modalBodyText]="some property from a component in here without single quotes"
      [okButtonText]="'OK'"></app-error-modal>
      
      We are including selector <app-error-modal> from our child component, and setting up the @Input properties within the square brackets (Property binding). To set up a value for the property binding, always use double quotes and then specify the value between them. If we want to pass a string, then that string must be wrapped inside single quotes first. Now that you know how to use child components, you can split the owner-details component into the parent-child relationship. Try to do that on your own, because the practice is the best way of learning things. Of course, you can find this implementation inside the source code for this blog post.

      Directives

      As you might have noticed, our owner has a dateOfBirth property which we need to work with, in our Create and Update forms. It is going to be just an input text element on the form, but it is going to have different behavior. We are going to use it as a date-picker. We could change its behavior in both components (Create and Update) separately but we won’t do that. For that purpose, we are going to create an Angular directive so we could change the behavior of that input field. In the shared folder, create new folder directives and inside create a new file datepicker.directive.ts. Modify that file:
      import { Directive, ElementRef, Output, EventEmitter, OnInit } from '@angular/core';
      
      @Directive({
        selector: '[appDatepicker]'
      })
      export class DatepickerDirective implements OnInit {
        @Output() public change = new EventEmitter();
      
        constructor(private elementRef: ElementRef) { }
      
        public ngOnInit() {
          $(this.elementRef.nativeElement).datepicker({
            dateFormat: 'mm/dd/yy',
            changeYear: true,
            yearRange: "-100:+0",
            onSelect: (dateText) => {
              this.change.emit(dateText);
            }
          });
        }
      }
      
      In the styles.cssfile (global file for styles) just add this styling:
      .ui-widget-header {
          color: #564040;
      }
      
      Don't forget to import this directive inside the shared module in the declarations and exports array. Now our directive is ready to be injected in any HTML element. We are going to use it later but for example, you can use it like this:
      <input type="text" class="form-control" appDatepicker>

      Conclusion

      By reading this post you have learned:
      • What is the purpose of Angular Input Output decorators
      • How to share components through the project
      • The way of creating child components
      • How to invoke child component into a parent component
      • How to create directives
      Thank you for reading the post, hopefully, it was helpful to you. In the next part of the series, we are going to start with the creation of the owner and form validation. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      2015 0 0 0 264 0 0 265 264 0 266 265 0 267 266 0 1219 0 0 1220 1219 0 1221 1220 0 1222 1221 0 1223 1222 0
      .NET Core 2.0, Angular and MySQL. Form Validation https://code-maze.com/net-core-web-development-part13/ Mon, 09 Apr 2018 06:17:47 +0000 https://code-maze.com/?p=2118 the following link: Introduction of the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. The source code is available at GitHub .NET Core, Angular and MySQL. Part 13 - Source Code This post is divided into several sections:

      Preparation For the Create Owner Component

      Let’s start by creating our component inside the owner folder. To do this, execute the Angular CLI command:
      ng g component owner/owner-create
      Create owner structure Angular Form Validation Modify the owner module by adding this route:
      { path: 'create', component: OwnerCreateComponent }
      When we click on the “create” link inside the owner-list.component.html we want the application to direct us to the creation page. So, let's modify the <a> tag inside the owner-list.component.html file:
      <a [routerLink]="['/owner/create']">Create owner</a>

      About Form Validation and ReactiveFormsModule

      Now we can start writing code to create our entity and to validate the form. There are two types of validation in the Angular: template driven validation and reactive form validation. In our project, we are going to use reactive form validation because it is easier to read an HTML file. Furthermore, it doesn’t make HTML file so much "dirty" with too many code lines and all validation is in the component which makes it easier for maintenance. Just before modifying our component, we need to modify the owner.model.ts file. Let's import the ReactiveFormsModule because this is a module which supports the reactive form validation:
      import { ReactiveFormsModule } from '@angular/forms';
      imports: [
          CommonModule,
          SharedModule,
          ReactiveFormsModule,
      
      Create a new interface:
      export interface OwnerForCreation {
          name: string;
          dateOfBirth: string;
          address: string;
      }
      

      Angular Form Validation Html Part

      Modify the owner-create.component.html file:
      <div class="container-fluid">
        <form [formGroup]="ownerForm" autocomplete="off" novalidate (ngSubmit)="createOwner(ownerForm.value)">
          <div class="form-horizontal well">
      
            <div class="form-group">
              <label for="name" class="control-label col-md-2">Name of the owner: </label>
              <div class="col-md-5">
                <input type="text" formControlName="name" id="name" class="form-control" />
              </div>
              <div class="col-md-5">
                <em *ngIf="validateControl('name') && hasError('name', 'required')">Name is required</em>
                <em *ngIf="validateControl('name') && hasError('name', 'maxlength')">Maximum allowed length is 60 characters.</em>
              </div>
            </div>
      
            <div class="form-group">
              <label for="dateOfBirth" class="control-label col-md-2">Date of birth: </label>
              <div class="col-md-5">
                <input type="text" formControlName="dateOfBirth" id="dateOfBirth" class="form-control" appDatepicker 
                      (change)="executeDatePicker($event)" readonly/>
              </div>
              <div class="col-md-5">
                <em *ngIf="validateControl('dateOfBirth') && hasError('dateOfBirth', 'required')">Date of birth is required</em>
              </div>
            </div>
      
            <div class="form-group">
              <label for="address" class="control-label col-md-2">Address: </label>
              <div class="col-md-5">
                <input type="text" formControlName="address" id="address" class="form-control" />
              </div>
              <div class="col-md-5">
                <em *ngIf="validateControl('address') && hasError('address', 'required')">Address is required</em>
                <em *ngIf="validateControl('address') && hasError('address', 'maxlength')">Maximum allowed length is 100 characters.</em>
              </div>
            </div>
      
            <br><br>
      
            <div class="form-group">
                <div class="col-md-offset-5 col-md-1">
                    <button type="submit" class="btn btn-info" [disabled]="!ownerForm.valid">Save</button>
                </div>
                <div class="col-md-1">
                    <button type="button" class="btn btn-danger" (click)="redirectToOwnerList()">Cancel</button>
                </div>
            </div>
            
          </div>
        </form>
      
        <app-success-modal [modalHeaderText]="'Success message'" 
        [modalBodyText]="'Action completed successfully'" [okButtonText]="'OK'" 
        (redirectOnOK)="redirectToOwnerList()"></app-success-modal>
      
        <app-error-modal [modalHeaderText]="'Error message'" 
        [modalBodyText]="errorMessage" [okButtonText]="'OK'"></app-error-modal>
      
      </div>
      Now let’s explain this code. In the form tag, we are creating the formGroup with a name ownerForm. This form group contains all the controls which we need to validate in our form. Moreover, with the (ngSubmit) we are calling a function when a user presses the submit button. As a parameter for that function, we are sending the ownerForm’s value which contains all the controls with the data we need for the validation. There is a formControlName attribute inside every control. That attribute represents the control name which we are going to validate inside the ownerForm and it is a mandatory attribute. Furthermore, in the <em> tags we are displaying error messages if there are any. Errors will be written on the page only if the functions validateControl() and hasError() return true as a result. The function validateControl() is going to check if the control is invalid and the hasError() function is going to check which validation rules we are validating against (required, max length…). Both validateControl and hasError functions are our custom functions which we are going to implement in the component file. There is also a submit button which is going to be disabled until form becomes valid and a cancel button which is going to redirect the user away from the creation form. Furthermore, we import our child modal components inside this form to show success and error messages. As you might remember from the previous post, the success modal had the @Output decorator named redirectOnOk. This @Output decorator is emitting the EventEmmiter and in here we are subscribing to it with the event binding and assigning a function to it. The function redirectToOwnerList() is going to be executed as soon as the user clicks on the OK button from the success modal window. Excellent, let’s continue on.

      Angular Form Validation Component Part

      Modify the owner-create.component.ts file:
      import { Component, OnInit } from '@angular/core';
      import { FormControl, FormGroup, Validators } from '@angular/forms';
      import { OwnerForCreation } from './../../_interfaces/owner-for-creation.model';
      import { ErrorHandlerService } from './../../shared/services/error-handler.service';
      import { RepositoryService } from './../../shared/services/repository.service';
      import { Router } from '@angular/router';
      import { DatePipe } from '@angular/common';
      
      @Component({
        selector: 'app-owner-create',
        templateUrl: './owner-create.component.html',
        styleUrls: ['./owner-create.component.css']
      })
      export class OwnerCreateComponent implements OnInit {
        public errorMessage: string = '';
      
        public ownerForm: FormGroup;
      
        constructor(private repository: RepositoryService, private errorHandler: ErrorHandlerService, private router: Router, private datePipe: DatePipe) { }
      
        ngOnInit() {
          this.ownerForm = new FormGroup({
            name: new FormControl('', [Validators.required, Validators.maxLength(60)]),
            dateOfBirth: new FormControl('', [Validators.required]),
            address: new FormControl('', [Validators.required, Validators.maxLength(100)])
          });
        }
      
        public validateControl(controlName: string) {
          if (this.ownerForm.controls[controlName].invalid && this.ownerForm.controls[controlName].touched)
            return true;
      
          return false;
        }
      
        public hasError(controlName: string, errorName: string) {
          if (this.ownerForm.controls[controlName].hasError(errorName))
            return true;
      
          return false;
        }
      
        public executeDatePicker(event) {
          this.ownerForm.patchValue({ 'dateOfBirth': event });
        }
      
        public createOwner(ownerFormValue) {
          if (this.ownerForm.valid) {
            this.executeOwnerCreation(ownerFormValue);
          }
        }
      
        private executeOwnerCreation(ownerFormValue) {
          let owner: OwnerForCreation = {
            name: ownerFormValue.name,
            dateOfBirth: this.datePipe.transform(ownerFormValue.dateOfBirth, 'yyyy-MM-dd'),
            address: ownerFormValue.address
          }
      
          let apiUrl = 'api/owner';
          this.repository.create(apiUrl, owner)
            .subscribe(res => {
              $('#successModal').modal();
            },
            (error => {
              this.errorHandler.handleError(error);
              this.errorMessage = this.errorHandler.errorMessage;
            })
          )
        }
      
        public redirectToOwnerList(){
          this.router.navigate(['/owner/list']);
        }
      
      }
      
      Let’s explain this code. As soon as a component mounts we are initializing our FormGroup variable named ownerForm with all the FormControls. Pay attention that the keys in the ownerForm object are the same as the names in the formControlName attribute for all input fields in a .html file, which is mandatory. Moreover, they have the same name as the properties inside the owner object (address, dateOfBirth, and name). When instantiating a new form control as a first parameter we are providing the value of control and as a second parameter the Validators array, which holds all the validation rules for our controls. In the validateControl() method, we are checking if the current control is invalid and touched (we don’t want to show an error if the user didn’t place the cursor inside control at all).  Furthermore, the hasError() function will check which validation rule the current control has violated. Our dateOfBirth control has the appDatepicker directive, which attaches datepicker inside that input control. Selecting the date won't patch a value for that control inside the ownerForm, so we need to do that manually inside the executeDataPicker() function. The createOwner just calls the function that sends the HTTP request to the server. Notice that in this example using the date pipe is not restricted only to HTML files. Of course, to make it work inside a component, we need to import the DatePipe pipe and inject it inside the constructor. We also need to import the DatePipe inside the app.module.ts file and to place it inside the "providers" array. If you look at the redirectToOwnerList() function, you are going to see the familiar code for navigating back to the previous component. There is another way of doing this by importing the Locationfrom the @angular/common and injecting it inside the constructor and then just calling the back() function on that injected property (location.back()). What you decide to use is totally up to you. After all, we are extracting values from the ownerForm, and sending a POST request to our server. Now just modify our root CSS file (styles.css), to show <em> messages with the red color and the bold style:
      em{
          color: #e71515;
          font-weight: bold;
      }
      
      Similarly, modify the owner-create.component.css file to wrap the inputs in the red color if they are invalid:
      .ng-invalid.ng-touched{
          border-color: red;
      }

      Inspecting Results

      Form with required errors: Input errors Angular Form Validation Form with additional errors: additional input errors Angular Reactive Form Validation Valid form: valid form Angular Reactive Form Validation The owner created successfully: Owner created successfully Angular Form Validation When you click the OK button, you will be redirected to the owner-list page and the new owner is going to be on the list. In the ErrorHandler service change the handleOtherError function:
      private handleOtherError(error: HttpErrorResponse){
          this.createErrorMessage(error);
          $('#errorModal').modal();
        }
      
      Now if the error other than 500 or 404 appears, we are going to show a modal message to the user: error modal Angular Form Validation

      Conclusion

      By reading this post you have learned:
      • About different validation types in the Angular
      • How to use reactive form validation in the HTML file
      • How to use reactive form validation in the component file
      • The way to create a new entity on the client-side
      Thank you for reading the post, hopefully, it was helpful to you. In the next part of the series, we are going to write the update part of the project, by sending the POST request towards our server. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      2118 0 0 0 149 0 0 150 149 0 262 0 0 263 262 0 297 263 0 298 297 0 299 298 0 1626 0 0 1627 1626 0 1628 import { DatePipe } from '@angular/common'; and to provide it in the providers array: providers: [ EnvironmentUrlService, RepositoryService, ErrorHandlerService, DatePipe ], just do that, and everything will work. I will update the code and article ASAP. I hope this will help you. Thank you a lot for the suggestion. Best regards.]]> 1627 0
      Angular - Angular PUT Actions https://code-maze.com/net-core-web-development-part14/ Mon, 16 Apr 2018 05:30:22 +0000 https://code-maze.com/?p=2421 the following link: Introduction of the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. The source code is available at GitHub .NET Core, Angular and MySQL. Part 14 - Source Code This post is divided into several sections:

      Folder Structure and Routing

      Prior to any update action, we need to create our component files. So, let’s create them by using the Angular CLI command which is going to create all the files and import the created OwnerUpdate component in the owner.module.ts file:
      ng g component owner/owner-update
      UpdateOwner folder structure Angular PUT Actions We need to modify the owner.module.ts file:
      RouterModule.forChild([
            { path: 'list', component: OwnerListComponent },
            { path: 'details/:id', component: OwnerDetailsComponent },
            { path: 'create', component: OwnerCreateComponent },
            { path: 'update/:id', component: OwnerUpdateComponent}
      
      Now we are going to change our owner-list.component.html file and the owner-list.component.ts file, to enable navigation between the OwnerList and the OwnerUpdate components:
      <button type="button" id="update" class="btn btn-success" (click)="redirectToUpdatePage(owner.id)">Update</button>
      public redirectToUpdatePage(id){
          let updateUrl: string = `/owner/update/${id}`;
          this.router.navigate([updateUrl]);
      }

      Handling Angular PUT Actions in the HTML File

      Our owner-update.component.html file is going to be almost the same as the HTML file for creating the owner. Since that's the case, let’s start with the implementation. Firstly, add the wrappers code in the owner-update.component.html file:
      <div class="container-fluid">
        <form [formGroup]="ownerForm" autocomplete="off" novalidate (ngSubmit)="updateOwner(ownerForm.value)">
          <div class="form-horizontal well">
      
            
          </div>
        </form>
      </div>
      
      We already know from the previous post that the formGroup is going to contain all of the controls inside its value. This value is exactly what we send as a parameter inside the updateOwner action. Secondly, we are going to create our controls between the form-horizontal div tag:
      <div class="form-group">
        <label for="name" class="control-label col-md-2">Name of owner: </label>
        <div class="col-md-5">
         <input type="text" formControlName="name" id="name" class="form-control" />
        </div>
        <div class="col-md-5">
         <em *ngIf="validateControl('name') && hasError('name', 'required')">Name is required</em>
          <em *ngIf="validateControl('name') && hasError('name', 'maxlength')">Maximum allowed length is 60 characters.</em>
        </div>
      </div>
      
      <div class="form-group">
        <label for="dateOfBirth" class="control-label col-md-2">Date of birth: </label>
        <div class="col-md-5">
          <input type="text" formControlName="dateOfBirth" id="dateOfBirth" class="form-control" appDatepicker 
                (change)="executeDatePicker($event)" readonly />
        </div>
        <div class="col-md-5">
          <em *ngIf="validateControl('dateOfBirth') && hasError('dateOfBirth', 'required')">Date of birth is required</em>
        </div>
      </div>
      
      <div class="form-group">
        <label for="address" class="control-label col-md-2">Address: </label>
        <div class="col-md-5">
          <input type="text" formControlName="address" id="address" class="form-control" />
        </div>
        <div class="col-md-5">
          <em *ngIf="validateControl('address') && hasError('address', 'required')">Address is required</em>
          <em *ngIf="validateControl('address') && hasError('address', 'maxlength')">Maximum allowed length is 100 characters.</em>
        </div>
      </div>
      Every input element contains a formControlName attribute which we are going to use in the component file for the validation. Furthermore, the validateControl and the hasError functions are the custom functions that will help us display error messages (still the same thing that we did in the CreateOwner component). Below the last form-group, add the code for displaying the buttons:
      <br><br>
      <div class="form-group">
          <div class="col-md-offset-5 col-md-1">
              <button type="submit" class="btn btn-info" [disabled]="!ownerForm.valid">Update</button>
          </div>
          <div class="col-md-1">
             <button type="button" class="btn btn-danger" (click)="redirectToOwnerList()">Cancel</button>
          </div>
      </div>
      Finally, below the form tag, let’s add our custom modal components:
        <app-success-modal [modalHeaderText]="'Success message'" 
        [modalBodyText]="'Action completed successfully'" 
        [okButtonText]="'OK'" 
        (redirectOnOK)="redirectToOwnerList()"></app-success-modal>
      
        <app-error-modal [modalHeaderText]="'Error message'" 
        [modalBodyText]="errorMessage" 
        [okButtonText]="'OK'"></app-error-modal>
      
      Excellent. Now we have our HTML file and it is time to implement business logic for the owner-update.component file.

      Handling Angular PUT Actions in the Component

      Modify the owner-update.component.ts file:
      import { Component, OnInit } from '@angular/core';
      import { FormGroup, FormControl, Validators } from '@angular/forms';
      import { ErrorHandlerService } from './../../shared/services/error-handler.service';
      import { RepositoryService } from './../../shared/services/repository.service';
      import { Router, ActivatedRoute } from '@angular/router';
      import { Owner } from './../../_interfaces/owner.model';
      import { DatePipe } from '@angular/common';
      
      @Component({
        selector: 'app-owner-update',
        templateUrl: './owner-update.component.html',
        styleUrls: ['./owner-update.component.css']
      })
      export class OwnerUpdateComponent implements OnInit {
      
        public errorMessage: string = '';
        public owner: Owner;
        public ownerForm: FormGroup;
      
        constructor(private repository: RepositoryService, private errorHandler: ErrorHandlerService, private router: Router,
          private activeRoute: ActivatedRoute, private datePipe: DatePipe) { }
      
      }
      
      This is the basic setup for this component. We are creating our properties and injecting all the services we need inside this component. Let’s add this code, below the constructor:
      ngOnInit() {
        this.ownerForm = new FormGroup({
          name: new FormControl('', [Validators.required, Validators.maxLength(60)]),
          dateOfBirth: new FormControl('', [Validators.required]),
          address: new FormControl('', [Validators.required, Validators.maxLength(100)])
        });
      
        this.getOwnerById();
      }
      
      private getOwnerById() {
        let ownerId: string = this.activeRoute.snapshot.params['id'];
          
        let ownerByIdUrl: string = `api/owner/${ownerId}`;
      
        this.repository.getData(ownerByIdUrl)
          .subscribe(res => {
            this.owner = res as Owner;
            this.ownerForm.patchValue(this.owner);
            $('#dateOfBirth').val(this.datePipe.transform(this.owner.dateOfBirth, 'MM/dd/yyyy'));
          },
          (error) => {
            this.errorHandler.handleError(error);
            this.errorMessage = this.errorHandler.errorMessage;
          })
      }
      
      In the ngOnInit function, we instantiate the ownerForm with all the form controls and add the validation rules. Then we call the getOwnerById function to fetch the owner with the exact id from the server. Inside this function, we execute familiar actions. Pulling the id from the url, sending the PUT request and processing the response whether it is success or error response. One thing to pay attention to is converting the dateOfBirthvalue from the format with hours, minutes and seconds, to the format we expect inside our input control. With the JQuery function, we place that value in the input field. We use DatePipe to transform the date format. Let’s add additional functions below the getOwnerById function:
      public validateControl(controlName: string) {
        if (this.ownerForm.controls[controlName].invalid && this.ownerForm.controls[controlName].touched)
          return true;
      
        return false;
      }
      
      public hasError(controlName: string, errorName: string) {
        if (this.ownerForm.controls[controlName].hasError(errorName))
          return true;
      
        return false;
      }
      
      public executeDatePicker(event) {
        this.ownerForm.patchValue({ 'dateOfBirth': event });
      }
      
      public redirectToOwnerList(){
        this.router.navigate(['/owner/list']);
      }
      
      These are the familiar functions for validating the input fields, patching the value in the ownerForm property and redirecting to the OwnerList component. Finally, we need to execute the update action by adding the code right below the redirectToOwnerList function:
      public updateOwner(ownerFormValue) {
        if (this.ownerForm.valid) {
          this.executeOwnerUpdate(ownerFormValue);
        }
      }
      
      private executeOwnerUpdate(ownerFormValue) {
       
        this.owner.name = ownerFormValue.name;
        this.owner.dateOfBirth = ownerFormValue.dateOfBirth;
        this.owner.address = ownerFormValue.address;
      
        let apiUrl = `api/owner/${this.owner.id}`;
        this.repository.update(apiUrl, this.owner)
          .subscribe(res => {
            $('#successModal').modal();
          },
          (error => {
            this.errorHandler.handleError(error);
            this.errorMessage = this.errorHandler.errorMessage;
          })
        )
      }
      That’s it. Give it a try and make some updates. Try to create success responses and error responses from the server to test the modal components out as well. After that, you can check if the form validation works.

      Conclusion

      By reading this post you have learned:
      • How to create HTML part of the update action
      • The way to patch values from the server into the form group
      • How to send the PUT request to the server
      Thank you for reading the post, hopefully, it was helpful to you. In the next part of the series, we are going to write the delete part of the project, and slowly wrap the coding part of the series up. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      2421 0 0 0 151 0 0 152 151 0 155 0 0 156 undefined) you are going to see the undefined part. This is the place where the GUID of your owner object (that you want to update) should be placed. But you are mapping it wrong thus having the undefined value. Your app works perfectly because your server can't find the user with the none existing Id and returns to you 404 NotFound(). Now how to solve it: 1) Take a look at your owner.module.ts file, there must be these two lines of code { path: 'update/:id', component: OwnerUpdateComponent }, { path: 'delete/:id', component: OwnerDeleteComponent } 2) Take a look at your owner-list..component.html file. Both update and delete buttons should call the corresponding functions and pass the owner.id parameter. Also check if your owner object returned from the server has that id property. If none of this helps, you could update your project and send me the link. Then I could take a look at it. It seems as if you made mistake in the owner.module file or that you don't have the id property (maybe it is named ownerId or something like that) At this point this is all I could do, until you send me your code. All the best mate.]]> 155 0 167 156 0 168 undefined part again in your requests. So I am assuming that your object id is undefined and maybe complete object is bad. Do you see any data populated in the input fields, once you land on the update page? If not than something is wrong with your object retrieved from the server while landing the Update or Delete page. Again, I can't help you a lot like this. All I know is that your Id param i undefined. It could help you if you could place a break point inside chrome developer tools to the place where you send that request and to inspect what is the Id value. Finally you can upload your code and send me the link, than I will go through it and try to find what is wrong. All the best mate.]]> 167 0 247 168 0 248 247 0 249 248 0 250 249 0 303 0 0 304 303 0 305 304 0 306 305 0
      Angular - Working with Angular Delete Actions https://code-maze.com/net-core-web-development-part15/ Wed, 18 Apr 2018 05:30:35 +0000 https://code-maze.com/?p=2526 the following link: Introduction of the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. The source code is available at GitHub .NET Core, Angular and MySQL. Part 15 - Source Code This post is divided into several sections:

      Folder Structure and Routing

      Execute the Angular CLI command, which is going to create the required folder structure. Moreover, it is going to import OwnerDelete component inside the owner.module.ts file:
      ng g component owner/owner-delete
      Folder structure Angular Delete Actions In addition, we have to modify the owner.module.ts file to enable routing for this component:
      RouterModule.forChild([
            { path: 'list', component: OwnerListComponent },
            { path: 'details/:id', component: OwnerDetailsComponent },
            { path: 'create', component: OwnerCreateComponent },
            { path: 'update/:id', component: OwnerUpdateComponent},
            { path: 'delete/:id', component: OwnerDeleteComponent }
          ])
        ]
      Furthermore, let’s modify the owner-list.component.html file and owner-list.component.ts file to enable navigation to the delete page:
      <button type="button" id="delete" class="btn btn-danger" (click)="redirectToDeletePage(owner.id)">Delete</button>
      public redirectToDeletePage(id){
        let deleteUrl: string = `/owner/delete/${id}`;
        this.router.navigate([deleteUrl]);
      }
      

      Handling Angular Delete Actions in the HTML File

      To create HTML part of the component, let’s start with the wrapper code:
      <div class="container-fluid">
        <div class="row">
          <div class="col-md-10 well">
      
          </div>
        </div>
        <br>
      </div>
      Inside the div with the col-md-10 well classes, we are going to show all the details from the entity we want to delete:
      <div class="row">
        <div class="col-md-3">
          <label for="name" class="control-label">Owners name:</label>
        </div>
        <div class="col-md-7">
          <span name="name">
            {{owner?.name}}
          </span>
        </div>
      </div>
      
      <div class="row">
        <div class="col-md-3">
          <label for="dateOfBirth" class="control-label">Date of birth:</label>
        </div>
        <div class="col-md-7">
          <span name="dateOfBirth">
           {{owner?.dateOfBirth | date: 'MM/dd/yyyy'}}
          </span>
        </div>
      </div>
      
      <div class="row">
        <div class="col-md-3">
          <label for="address" class="control-label">Address:</label>
        </div>
        <div class="col-md-7">
          <span name="address">
            {{owner?.address}}
          </span>
        </div>
      </div>
      
      Right below the <br/> tag, let’s add the buttons:
      <div class="row">
        <div class="col-md-offset-8 col-md-1">
          <button type="submit" class="btn btn-info" (click)="deleteOwner()">Delete</button>
        </div>
        <div class="col-md-1">
          <button type="button" class="btn btn-danger" (click)="redirectToOwnerList()">Cancel</button>
        </div>
      </div>
      
      Finally, we are going to add our custom modal components:
      <app-success-modal [modalHeaderText]="'Success message'" 
        [modalBodyText]="'Action completed successfully'" 
        [okButtonText]="'OK'"
        (redirectOnOK)="redirectToOwnerList()"></app-success-modal>
      
      <app-error-modal [modalHeaderText]="'Error message'" 
        [modalBodyText]="errorMessage" 
        [okButtonText]="'OK'"></app-error-modal>
      
      Excellent. Our HTML part of the component is ready. All we have to do is to implement the business logic.

      Handling Angular Delete Actions in the Component File

      Modify owner-delete.copmonent.ts file, by importing all the necessary resources and by injecting services in the constructor:
      import { Component, OnInit } from '@angular/core';
      import { ErrorHandlerService } from './../../shared/services/error-handler.service';
      import { RepositoryService } from './../../shared/services/repository.service';
      import { Owner } from './../../_interfaces/owner.model';
      import { Router, ActivatedRoute } from '@angular/router';
      
      @Component({
        selector: 'app-owner-delete',
        templateUrl: './owner-delete.component.html',
        styleUrls: ['./owner-delete.component.css']
      })
      export class OwnerDeleteComponent implements OnInit {
        public errorMessage: string = '';
        public owner: Owner;
      
      constructor(private repository: RepositoryService, private errorHandler: ErrorHandlerService, private router: Router,
        private activeRoute: ActivatedRoute) { }
      
      }
      
      Bellow the constructor, we are going to add the logic for fetching the owner and redirecting to the owner-list component:
      ngOnInit() {
        this.getOwnerById();
      }
      
      private getOwnerById() {
        let ownerId: string = this.activeRoute.snapshot.params['id'];
        let ownerByIdUrl: string = `api/owner/${ownerId}`;
      
        this.repository.getData(ownerByIdUrl)
          .subscribe(res => {
            this.owner = res as Owner;
          },
          (error) => {
            this.errorHandler.handleError(error);
            this.errorMessage = this.errorHandler.errorMessage;
          })
      }
      
      public redirectToOwnerList() {
        this.router.navigate(['/owner/list']);
      }
      
      Finally, let’s implement the delete logic right below the redirectToOwnerList function:
      public deleteOwner() {
        let deleteUrl: string = `api/owner/${this.owner.id}`;
        this.repository.delete(deleteUrl)
          .subscribe(res => {
            $('#successModal').modal();
          },
          (error) => {
            this.errorHandler.handleError(error);
            this.errorMessage = this.errorHandler.errorMessage;
          })
      }
      
      That is it. We have finished our Angular part of this application. As a result, we have a fully functional application ready for deployment. Therefore, all we have left to do is to prepare Angular files for the production and to deploy the completed application to the Windows and Linux environment.

      Conclusion

      By reading this post you have learned:
      • How to create HTML part of the delete action
      • How to send the DELETE request to the server
      Thank you for reading the post, hopefully, it was helpful to you. In the next part of the series, we are going to publish this complete application on the Windows environment by using the IIS. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      2526 0 0 0 261 0 0 1178 0 0 1179 1178 0 1180 1179 0
      ASP.NET Core Web API - IIS Deployment https://code-maze.com/net-core-web-development-part16/ Mon, 23 Apr 2018 04:05:05 +0000 https://code-maze.com/?p=2556  We should deploy our application to the staging environment as soon as we start building it. For the purpose of this deployment, we are going to build our Angular application for production to produce optimized static files and to combine them with the .NET Core server. This process is pretty much the same for any client-side project you want (React, Vue.js or any other). So, let's start. If you want to see all the basic instructions and complete navigation for the .NET Core series, check out the following link: Introduction of the .NET Core series. For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. For the previous part check outCreating Angular client side – Angular Delete Actions All required projects and the publish files are available at GitHub .NET Core, Angular and MySQL. Part 16 - Source Code This post is divided into several sections:

      Building Angular Production Files

      First, we need to create the production files for our Angular project by executing the command:
      ng build --prod
      Building Angular Production Files IIS Deployment This is the way to create the production files for the Angular project. But if we were to use React or Vue.js for the client-side, the command would be:npm run build

      Publishing .NET Core Files for the IIS Deployment

      Before we publish our files to the required location, we have to modify our .NET Core app configuration a bit:
      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
          else
          {
              app.Use(async (context, next) =>
              {
                  await next();
                  if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
                  {
                      context.Request.Path = "/index.html";
                      await next();
                  }
              });
              // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
              app.UseHsts();
          }
      
          app.UseHttpsRedirection();
      
          app.UseCors("CorsPolicy");
      
          app.UseForwardedHeaders(new ForwardedHeadersOptions
          {
              ForwardedHeaders = ForwardedHeaders.All
          });
      
          app.UseStaticFiles();
      
          app.UseMvc();
      }
      If we don't modify our configuration like this, we won't be able to start our deployed application at all (as soon as we type the required URL address). But with it, we are safe to continue. Angular CLI is going to create a new folder with the name “dist” inside our project and publish all the production files inside. Copy all those files from the dist folder and paste them into the wwwroot folder inside the .NET Core's main project. Now with the static files in the right place, we are going to use Visual Studio's feature to create publish files for the entire application. Let's create a folder on the local machine with the name publish. Inside that folder, we want to place all of our files for the deployment. Then, right-click on the AccountOwnerServer project and click the Publish option: Publish menu IIS Deployment In the next window, we are going to pick a Folder as the publish target, choose the place where we want to publish our files and click Create Profile: publish screen In the next window, we should just click the Publish button. Excellent. Now we have all the files in the right place.

      Windows Server Hosting Bundle and the Hosts File

      Prior to any further action, let’s install .NET Core Windows Server Hosting bundle on our system to install the .NET Core Runtime. Furthermore, with this bundle, we are installing the .NET Core Library and the ASP.NET Core Module. This installation will create a reverse proxy between IIS and the Kestrel server, which is crucial for the deployment process. During the installation, it will try to install the Microsoft Visual C++ 2015 Redistributable, so just let it do that. If you have a problem with missing SDK after installing Hosting Bundle, follow this solution suggested by Microsoft: Installing the .NET Core Hosting Bundle modifies the PATH when it installs the .NET Core runtime to point to the 32-bit (x86) version of .NET Core (C:\Program Files (x86)\dotnet\). This can result in missing SDKs when the 32-bit (x86) .NET Core dotnet command is used (No .NET Core SDKs were detected). To resolve this problem, move C:\Program Files\dotnet\ to a position before C:\Program Files (x86)\dotnet\ on the PATH environment variable. After the installation, locate the Windows hosts file on C:\Windows\System32\drivers\etc and add the following record at the end of the file: 127.0.0.1   www.accountowner.com Finally, save the file.

      Installing IIS and the Site Deployment

      If you don’t have IIS installed on the machine, you need to install it by opening ControlPanel and then Programs and Features: Installing IIS for IIS Deployment After the IIS installation finishes, open the Run window (windows key + R) and type: inetmgr to open the IIS manager: Run window IIS Deployment Now we can create a new website: IIS Manager for IIS Deployment In the next window we need to add a name to our site and a path to the published files: Add WEbsite IIS Deployment After this step, we are going to have our site inside the "sites" folder in the IIS Manager. Additionally, we need to set up some basic settings for our application pool: Application pool IIS Deployment After we click on the Basic Settings link, let's configure our application pool: Edit Application Pool IIS Deployment Your website and the application pool should be started automatically. In order to deploy the application to IIS, we need to register the IIS integration in our .NET Core part of the project. We have already done that in our ServiceExtensions class, in Part 2 of this tutorial. Excellent. Everything is in place. Now let’s open a browser and type http://www.accountowner.com to inspect the result: Home screen IIS Deployment Owner List IIS Deployment

      Conclusion

      By reading this post you have learned:
      • How to build production files from the client-side application
      • The way to publish files by using Visual Studio
      • Which additional resources we need for IIS deployment to work
      • How to install IIS
      • To deploy the application on IIS
      Thank you for reading the post, hopefully, it was helpful to you. In the next part of the series, we are going to publish this complete application to the Linux environment. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      2556 0 0 0 195 0 0 196 195 0 200 195 0 201 200 0 202 195 0 399 0 0 400 399 0 619 0 0 645 0 0 688 619 0 716 645 0 738 688 0 1295 0 0 1296 1295 0 1297 1296 0 1298 https://code-maze.com/ 1297 0 1299 1298 0 1300 https://code-maze.com/ 1299 0 1319 0 0 1321 1319 0 1322 1321 0 1495 0 0 1503 1495 0
      ASP.NET Core Web API - Linux Deployment https://code-maze.com/net-core-web-development-part17/ Mon, 30 Apr 2018 04:14:10 +0000 https://code-maze.com/?p=2630 https://www.putty.org/ https://filezilla-project.org/download.php So, let’s start. If you want to see all the basic instructions and complete navigation for the .NET Core series, check out the following link: Introduction of the .NET Core series. For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series. For the previous part check outCreating Angular client side – IIS Deployment All required projects and the publish files are available at GitHub .NET Core, Angular and MySQL. Part 17 - Source Code This post is divided into several sections:

      Machine's IP Address and SSH

      If you have purchased a VM from the provider, then you have its IP address and SSH enabled for sure. But if you are using a local VM, you need to find out the IP address and to enable SSH on it. To find out our VM’s IP address, let’s log in to the Linux OS and search for a terminal window. Then type ifconfig to locate the IP address: Terminal window .NET Core Linux Deployment ifconfig .NET Core Linux Deployment Great. Now we have our IP address, and let’s enable SSH by typing the command:
      sudo apt-get install openssh-server openssh-client
      After that, let’s open PuTTY and connect to our VM with a valid IP address. The default port number should be 22.

      Credentials and File Preparations With FileZilla

      It is bad practice to log in to a VM as the root user. So if your current user is the root user, you should create a new one with the command: sudo adduser username, and then give it admin rights with the command: usermod –aG sudo username. After these steps, log in again to the VM with new credentials by using PuTTY. Let’s create a new folder on the local machine and name it publishLinux. Place it next to the IIS publish folder, which we have created in the previous part. Copy all the files from the windows publish folder to a new one. We have created the production files in the previous part, so feel free to read it if you haven’t already done that. To prepare the database on Linux, open FileZilla and login to your VM with it. Then transfer your backup.sql file to a VM, because we are going to need that file to populate the database: Filezzila .NET Core Linux Deployment Once we transfer a backup file to the VM, we need to install MySQL server and import that file.

      Installing MySQL and Restoring the Backup File

      First, let's update the packages with the command:
      sudo apt-get update
      Then, when the update finishes, we need to install MySQL with the command:
      sudo apt-get install mysql-server
      Moreover, after the MySQL installation finishes, for the secure installation we need to execute the command:
      mysql_secure_installation
      We are going to answer all the questions with the default answers except the one about creating a password. We don’t want to create a new password because it has been created during MySQL installation. Let’s log in to the MySQL server by executing:
      sudo mysql –p
      Enter the password and we are in. Now we are going to create a database accountowner with the command:
      Create database accountowner;
      Notice the semicolon character. It is mandatory for the MySQL statements. To use this new database, we have to type:
      use accountowner;
      With this command, we are notifying MySQL server which database is currently active. Finally, to restore the backup file let’s execute the command:
      source /home/mare/Desktop/2017-11-29-backup.sql;
      (this depends on your file name and the location your backup file is in). To see if all the tables are created just type:
      show tables;
      Tables in MySQL .NET Core Linux Deployment

      Removing MySQL Case Sensitivity

      MySQL server is case-sensitive, and that could lead to certain problems once the application is published. So, we are going to ignore case sensitivity for the table names inside the MySQL configuration file. To do that let’s type exit to leave MySQL server, and then:
      sudo nano /etc/mysql/my.cnf
      MySQL Configuration File .NET Core Linux Deployment Now, let’s restart MySQL with the command:
      sudo /etc/init.d/mysql restart

      Configuring Firewall

      Great, we are done with a database, and now we need to configure the firewall by executing:
      sudo ufw default deny
      sudo ufw default allow outgoing
      sudo ufw allow ssh
      sudo ufw allow 80/tcp
      sudo ufw allow 443/tcp
      sudo ufw enable
      sudo ufw status verbose

      .NET Core Installation

      We have MySQL on our machine, let’s install .NET Core. First, we have to register the Microsoft signature, and for that, we need the curl on our Linux system:
      sudo apt install curl
      After the installation, let’s register the Microsoft key and the feed:
      curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
      sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
      To continue, let's register the Microsoft product feed on Ubuntu 16.04:
      sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
      If you are using some other Linux version check out: https://www.microsoft.com/net/download/linux-package-manager/ubuntu16-04/runtime-2.0.6 and in drop-down list choose your own version. Then let’s install .NET Core:
      sudo apt-get update
      sudo apt-get install dotnet-sdk-2.0.3

      Small File Modifications

      Before we transfer all of the published files to VM, we need to change one more thing. If you may remember, in part nine of the series, we have configured URL address for the production environment to be: http://www.accountowner.com. But because we are using a VM for the Linux deployment, we need our IP address. In the publishLinux folder open the wwwroot folder and then open the file that starts with main. In that file search for http://www.accountowner.com and replace it with http://192.168.0.15 (type your IP address) and save the file. Furthermore, in the nlog.config file we need to change the path for the logging messages:
      fileName="/home/mare/logs/${shortdate}_logfile.txt"
      (Again, this is the path on our machine. You probably have a different folder name after the home part).

      Starting the Application

      Let’s use FileZilla to create a new folder and name it var. Inside that folder, create another one and name it publish. All these folders must reside inside the folder with your username: Folder Structure .NET Core Linux Deployment Let’s create one more folder with name logs, for our logging files. Finally, transfer all the publishing files from the local machine to this new publish folder. To start the application and check if everything works, we need to navigate to the place where we transferred our publish files and to execute:
      dotnet AccountOwnerserver.dll
      Linux start .NET Core Application If you see the screen like this, then you've successfully started the application. But we don’t want to start our application manually. So, let's create a service which will start our application automatically as soon as the server starts.

      Working With Kestrel

      Execute the command:
      sudo nano /etc/systemd/system/kestrel-accountowner.service
      and modify the file: Kestrel service .NET Core Linux Deployment Now, we need to enable this service, start it and check its state:
      sudo systemctl enable kestrel-accountowner.service
      sudo systemctl start kestrel-accountowner.service
      sudo systemctl status kestrel-accountowner.service

      Nginx Configuration

      If you may remember, in part 2 of this tutorial we've added this code:
      app.UseForwardedHeaders(new ForwardedHeadersOptions
      {
          ForwardedHeaders = ForwardedHeaders.All
      });
      
      Nginx supports reverse proxy, and while directing client requests towards the backend server, some data could be lost (http or https, the client IP address...). Therefore, we need the UseForwardedHeaders middleware to retrieve these data through the HTTP headers. Now let's install Nginx on Linux OS:
      sudo apt-get install nginx
      After the installation is completed, open and modify this file:
      sudo nano /etc/nginx/sites-available/default
      We won’t delete any commented parts, just modify the parts that we need: Nginx .NET Core Linux Deployment Test our Nginx configuration with the command:
      sudo nginx –t
      And just reload nginx if everything looks alright:
      sudo nginx –s reload
      That is it, now let’s navigate to our IP address with a browser and see the application deployed on the Linux server. Home .NET Core Linux Deployment Owner-list .NET Core Linux Deployment

      Conclusion

      With this post, we have wrapped up all the actions to create and deploy our AccountOwner application. By reading this post you have learned:
      • How to set up a user account on VM
      • The way to install MySQL on Linux
      • The way to install .NET Core on Linux
      • How to set up Kestrel for automatic application start
      • How to set up Nginx
      Thank you for reading the post, hopefully, it was helpful to you. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      2630 0 0 0 175 0 0 176 175 0
      React - Preparing the Project and Creating Components https://code-maze.com/react-dot-net-core-creating-react-project/ Tue, 08 May 2018 06:52:04 +0000 https://code-maze.com/?p=2863
    • Preparing the Project and Creating Components (Current article)
    • Navigation and Routing
    • HTTP, Axios, and Redux
    • React Lazy Loading and HOC Component
    • Error Handling and Additional Components
    • Dynamic Form Creation and Modal Components
    • Form Validation and Sending the POST Requests
    • React PUT Requests
    • React DELETE Requests
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see all the basic instructions and complete navigation for the .NET Core series, check out the following link: Introduction to the .NET Core series. For the complete navigation and all the basic instructions of the React series, check out: Introduction to the React series. The source code is available at GitHub: React series - Creating React Project - master branch This post is divided into several sections:

      Creating a New Project

      To create React project, we need to execute the Create React App command which is going to create a new application. Let's open Visual Studio Code, and in a terminal window (CTRL+`), we need to navigate to the folder we want our project in and type the command:
      npx create-react-app accountownerclient
      After some time a new project is going to be created: Created project - Creating React Project If you want to learn in more detail about the Create React App, visit this site: https://github.com/facebook/create-react-app.

      Third-Party Libraries

      We are going to use the React-Bootstrap library for styling our components, so let’s install it and import it into the project. Type this command to install the React-Bootstrap and Bootstrap version 3 libraries:
      npm install --save react-bootstrap bootstrap@3
      React Bootstrap - Creating React Project After the installation, import the Bootstrap library inside the index.js file:
      import 'bootstrap/dist/css/bootstrap.css';
      import 'bootstrap/dist/css/bootstrap-theme.css';
      Now we can use Bootstrap in our project.

      React Components Overview

      The React is a framework for creating the SPA’s (Single Page Application) applications. Therefore, we are going to create all of our pages in one page. That page is the index.html. If we take a look at that page, we are going to find this code line:
      <div id="root"></div>
      This is the place where all of our pages are going to be generated. But how does React know to render all the pages inside that tag? Well, if we take a look at the index.js file, we are going to find this code line:
      ReactDOM.render(<App />, document.getElementById('root'));
      What this means is that React will render all the components from the App component to the index.html page inside the div with id=root. This also means that App component is going to be the main component of our entire application. So let’s talk a bit about components in React. There are two types of components:
      • Stateful (class) components and
      • Stateless (functional) components
      The App component is a stateful component because it has access to the state. Stateful components also have the lifecycle events and access to the props object with the this.props expression. If we look in the App.js file, we are going to notice the render() function which is one of the lifecycle functions. Stateful components must have at least the render() function from all the lifecycle functions inside the React. They must inherit from the Component class. Stateless components are much simpler than stateful components. They don’t have a lifecycle and should be used when there's no need for the state inside our component. They have access to the props as well, but that props object is provided by React as a parameter. Whether we use the stateful or the stateless components we must export that component to use it in any other component.

      Creating Our Components

      Before we start with the creation of the components, let’s modify the App.js and App.css files. Remove all the code from the App.css file and modify the App.js file:
      import React, { Component } from 'react';
      import logo from './logo.svg';
      import './App.css';
      
      class App extends Component {
        render() {
          return (
          );
        }
      }
      
      export default App;
      
      We are going to notice an error now because the return() function demands one root tag, but we are going to fix this soon. Before we continue, let’s create the base folder structure for the stateful components (containers) and functional components (components). This action is not a must but it is a good practice to separate your class and functional components. We are going to put the App.js file inside the containers folder and to modify the index.js file because it imports the App.js. Folder structure - Creating React Project Now just modify import statement inside the index.js file and remove import for the logo.svg:
      import App from './containers/App';
      Excellent. Let’s create our first component. In the components folder, create a new folder and name it Layout. Then inside this folder, create a Layout.js file and modify it: Layout created - Creating React Project
      import React from 'react';
      import { Grid, Row } from 'react-bootstrap';
      
      const layout = (props) => {
          return (
              <Grid>
                  <Row>
                      This is the place for the navigation component.
                  </Row>
                  <main>
                      {props.children}
                  </main>
              </Grid>
          )
      }
      export default layout;
      Let’s take some time to review this code. First of all, this is a stateless component (functional) and that's because we don’t have class in here but just a function (arrow function to be more precise). Because this is not a class component we don’t need to extend Component, therefore we are not importing it at all. The Grid and a Row are React-Bootstrap components. If you are familiar with the Bootstrap library, the Grid and the Row are equivalents to the <div class=”row”> and <div class=”container”> elements. The functional component is fetching the props object through the props argument and all the properties from the props object are going to be available inside this functional component. One of those properties is the “children” property, which is going to show all the data between the opening and closing Layout tag (<Layout> everything in here is a children property of a props object </Layout>). We are going to see this in action in the next example. Finally, we are exporting this component. This type of export is called the default export. Let’s continue by modifying the App.js file:
      import React, { Component } from 'react';
      import './App.css';
      import Layout from '../components/Layout/Layout';
      
      class App extends Component {
        render() {
          return (
            <Layout>
              <strong>This content is going to be rendered as the props.children inside the Layout component.</strong>
            </Layout>
          );
        }
      }
      
      export default App;
      
      Execute the npm start command in our terminal and we should be able to see our page on the localhost:3000 (not a pretty page but it is a start :) ) : First page - Creating React Project

      Home Component

      We are going to continue with the Home component. So, let's create a folder structure for this component first: Home component folder structure Now, let's modify the Home.js component:
      import React from 'react';
      import { Col, Row } from 'react-bootstrap';
      import './Home.css';
      
      const home = (props) => {
          return (
              <Row>
                  <Col md={12}>
                      <div className={'homeText'}>
                          "WELCOME TO ACCOUNT-OWNER APPLICATION"
                      </div>
                  </Col>
              </Row>
          )
      }
      
      export default home;
      
      Then we need to modify the Home.css file:
      .homeText{
          font-size: 35px;
          color: red;
          text-align: center;
          position: relative;
          top:30px;
          text-shadow: 2px 2px 2px gray;
      }
      
      Finally, modify the App.js file:
      import React, { Component } from 'react';
      import './App.css';
      import Layout from '../components/Layout/Layout';
      import Home from '../components/Home/Home';
      
      class App extends Component {
        render() {
          return (
            <Layout>
              <Home />
            </Layout>
          );
        }
      }
      
      export default App;
      
      When we save all of our files, the page on the localhost:3000 should look like this: Home component - Creating React Project

      Conclusion

      At this point, we have a working component and a React application that we can run in our browser. But it is just a beginning. We have a long way ahead of us because there are still a lot of important React features to introduce to the project. By reading this post you've learned:
      • The way to set up third-party libraries
      • The overview of the React components
      • How to create components
      • Difference between stateless and stateful components
      • How to use React-Bootstrap
      Thank you for reading the article and I hope you found something useful in it. In the next part of the series, we are going to learn how to create navigation in the project and also how to use routing. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      2863 0 0 0 143 https://www.alvinashcraft.com/2018/05/08/dew-drop-may-8-2018-2720/ 0 0 158 http://devepar.com/archives/4834 0 0 171 http://devepar.com/archives/5111 0 0 623 0 0 625 623 0 1004 0 0 1005 1004 0
      Adding MySQL to ASP.NET Core App With Docker Compose https://code-maze.com/mysql-aspnetcore-docker-compose/ Mon, 14 May 2018 06:05:23 +0000 https://code-maze.com/?p=2068 previous post, we've dockerized our ASP.NET Core application. In this post, we are going to add a MySQL database as another container and connect it with our application. Since we'll have multiple containers running we are going to introduce the Docker Compose tool which is one of the best tools for configuring and running multi-container applications. Docker Compose comes together with the Docker installation on the Windows machine so you should be all set. [sc name="part_of_series" headline="This article is part of the series"] The starting point source code can be found on the docker-series-dockerfiles-end branch of our docker-series repo on GitHub. So let's see what we are going to go through in this article: There is a lot to cover, so let's start.

      What is Docker Compose?

      As we already mentioned in the intro, Docker Compose is a tool for defining and running multi-container applications. But how does it do that? Docker Compose uses a YAML file to define the services and then run them with a single command. Once you define your services, you can run them with the docker-compose up command, and shut them down with the docker-compose down command. If you've followed along with the Docker Series you might have noticed that running Docker images can get pretty complicated. This is not such a big problem once you learn what each Docker command does, but once you have multiple images, you can imagine the pain of running all of them manually. That's where Docker Compose comes in. Is that all it does? Well, not quite. Other features of Docker Compose include:
      • Using project names to create multiple isolated environments on a single host
      • Creating a network inside the environment
      • Preserving the volume data that your containers used
      • Only recreating the containers that have changed by caching the configuration
      • Allowing usage of variables inside the YAML file so you can customize the configuration for different environments and users
      • Working well with continuous integration tools (like TeamCity for example)
      Overall, Compose is a nifty and powerful tool for managing your multi-container apps, and we are going to see just how to use it for our ASP.NET Core App by adding the MySQL image as our database container. Let's drill down into it and see how awesome Compose can be.

      Adding Docker Compose to Our Application

      Compose relies on a YAML file. This file is usually called docker-compose.yml and it's placed at the root of our project. So let's start by adding a docker-compose.yml file to the root of our solution. Once we've created the file, we can start adding some commands to it:
      version: '3.0'
      
      services:
         accountownerapp:
           image: codemazeblog/accountowner:runtime
           ports:
             - "8080:5000"
      This is the simplest of Compose files, and it's practically the same thing we did with the docker run command in the last part of the series. To jog your memory, we used to run our application like this: docker run --rm -it -p 8080:5000 codemazeblog/accountowner:runtime. Now the only command we need to run to achieve the same result is docker-compose up. As you may see, the docker-compose.yml contains the set of services. In our case, this is just our application, but soon we're going to add one more service to it. We've described what image we want to run and which port we need to expose. We can run our services in the background by adding -d flag: docker-compose up -d. In that case, we would need to stop the service(s) with docker-compose down. One thing to note here is that if we quit a container with Ctrl+C, it won't kill the container or the network created by Compose. To make sure you release the resources you need to run docker-compose down.

      Building the Image With Compose

      Ok, so until now, we've just run the existing image with docker compose. But if we make some changes to the application or the Dockerfile, Compose would still run the image we've built before those changes. We would need to build the image again with the docker build -t codemazeblog/accountownerapp . command, and then run the docker-compose up command in order to apply those changes. To automate this step we can modify our docker-compose.yml file with the build step:
      version: '3.0'
      
      services:
         accountownerapp:
           image: codemazeblog/accountowner:runtime
           build:
             context: .
           ports:
             - "8080:5000"
      The build step sets the context to the current directory and enables us to build our image using the Dockerfile defined in that context. So, now we can add the --build flag to our docker-compose command to force the rebuild of the image: docker-compose up --build. Now try running the command and see for yourself if that's the case. We have removed the need for the docker build command just like that. Okay, great. Let's move on to the main part, and where the real fun begins.

      Adding a MySQL Database with Docker Compose

      In part 1 of the series, while preparing our ASP.NET Core application for dockerization, we switched from the MySQL database to the in-memory one. We did this in order to demonstrate Docker commands easier. Now that we introduced Docker Compose, we can revert the changes we made to the ServiceExtensions.cs class:
      public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration config)
      {
      	var connectionString = config["mysqlconnection:connectionString"];
      	services.AddDbContext<RepositoryContext>(o => o.UseMySql(connectionString));
      }
      Now, we can start using MySQL again. There is one more thing we need to change, and that's the connection string in the appsettings.json file since we don't want to use the root user. We are also changing the server from localhost to db. You'll see why in a moment:
      "mysqlconnection": {
          "connectionString": "server=db;port=3306;userid=dbuser;password=dbuserpassword;database=accountowner;"
      }
      Okay, excellent. Now that we prepared our application, let's add the MySQL image to our docker-compose.yml. We are going to use mysql:5.7 image since it's compatible with our application:
      version: '3.0'
      
      services:
         db:
           image: mysql:5.7
           environment:
             MYSQL_RANDOM_ROOT_PASSWORD: 1
             MYSQL_DATABASE: accountowner
             MYSQL_USER: dbuser
             MYSQL_PASSWORD: dbuserpassword
           volumes:
             - dbdata:/var/lib/mysql
             - ./_MySQL_Init_Script:/docker-entrypoint-initdb.d
           restart: always
      
         accountownerapp:
           depends_on:
             - db
           image: codemazeblog/accountowner:runtime
           build:
             context: .
           ports:
             - "8080:5000"
      
      volumes:
          dbdata:
      So, as you may see, we added the db service to our docker-compose.yml. This service name is also a server name for that container and that's why we've used it in our connection string. In the db service, we are configuring the MySQL container once we run the image. A MySQL image has some predefined environment values that it looks for while initializing. For example:
      • MYSQL_RANDOM_ROOT_PASSWORD: 1 sets the random password for the root user. This password will be shown in the console output
      • MYSQL_DATABASE: accountowner creates the "accountowner" database
      • MYSQL_USER: dbuser creates the user "dbuser"
      • MYSQL_PASSWORD: dbuserpassword generates the password for the user "dbuser"
      After that, we create two volumes: one for the database data, and one to map our init.sql script that will create database tables and populate them with data. MySQL container will use the first volume to save all the data to the /var/lib/mysql directory. With the second volume, we are mapping our scripts directory /AccountOwnerServer/DbScript to the docker-entrypoint-initdb.d directory. Every script that is located in the docker-entrypoint-initdb.d directory will be executed in the alphabetical order. The important thing to note here is that these environment variables are set only during the first initialization of the container. After that, you'll have to make modifications to the container manually through the MySQL CLI. You can access the running container by typing docker exec -it <mysql-container-name> bash. With restart: always we are instructing Docker Compose to restart our service in case the container goes down for whatever reason. And finally, we've added depends_on: - db to our app service, to make db service be the first to load.

      Docker-Compose Up/Down Demonstration

      We're all set to run our ASP.NET Core app and use MySQL as a preferred persistence provider. And how do we do that? Well, we simply type docker-compose up --build and watch the magic happen. If you don't have mysql:5.7 image on your host machine, you'll probably have to wait a little longer because Docker will be pulling the image from Docker Hub. Once everything is started, we can navigate to http://localhost:8080/swagger and play around with the API. You'll see that some owners and accounts already exists and that's because we've populated our database using the init.sql script. If we try to get owners, our your response should look like this: get owners response What do you think would happen if we add a new owner to our database and do a Ctrl+C right now? Would the owner be there if we run docker-compose up again? The answer is: YES because by doing Ctrl+C in the command prompt we won't delete the volumes. Even if we do the docker-compose down, we won't destroy the volumes created by previous containers. If we want to delete the volumes for any reason, there is a special flag we can add: docker-compose flag -v. This flag will tell Docker Compose explicitly to destroy any volumes created by the containers. Now that we understand how docker-compose up and docker-compose down work, we can wrap this part up.

      Conclusion

      In this part, we have gone through the basics of the Docker Compose tool. We've learned how to make a Compose file and how to make it build and run our images with the docker-compose up and docker-compose down commands. Docker Compose has allowed us to easily add a MySQL database container that our ASP.NET Core app can persist its data in. This setup will help us in the next part where we are going to learn how to make a local registry and push our images to it instead to DockerHub. You can find the complete source code of this part on the docker-series-docker-compose-end branch of our docker-series repo on GitHub. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      2068 0 0 0 157 https://www.alvinashcraft.com/2018/05/14/dew-drop-may-14-2018-2724/ 0 0 170 http://devepar.com/archives/5101 0 0 337 0 0 342 https://code-maze.com/ 337 0 1548 0 0
      Docker Hub vs Creating a Local Docker Registry https://code-maze.com/docker-hub-vs-creating-docker-registry/ Mon, 21 May 2018 06:15:41 +0000 https://code-maze.com/?p=2107 previous part, we've learned how to utilize Docker Compose to tie multiple containers together with a single command. We've added a MySQL database to our ASP.NET Core application and successfully run it. Before proceeding further, we need to learn more about image management and distribution. There are several ways to do that, whether locally or in the cloud, so you should definitely know these concepts before starting with continuous integration and application deployment. [sc name="part_of_series" headline="This article is part of the series"] In this part, we are going to learn the difference between a Docker registry and a Docker repository and how to persist the changes we made to our images. We're also going to learn more about Docker Hub and how to make our own local Docker registry. To see the complete overview of the series, go to the Docker Series page. The starting point source code for this part can be found on the docker-series-docker-compose-end branch of our docker-series repo on GitHub. Let's see what we are going to go learn in this article: So let's start off with clearing some concepts up.

      Difference Between Docker Repository and Docker Registry

      Besides sounding similar these terms can sometimes cause confusion so it seems appropriate to start out by explaining what each one means. Docker Registry (Docker Trusted Registry - DTR) is an enterprise-grade storage solution for Docker images. In other words, it's an image storage service. Think about GitHub, but for Docker Images. We can use one of the existing and well-established cloud registries like Docker Hub, Quay, Google Container Registry, Amazon Elastic Container Registry or any other. We can also make our own registry and host it locally. We're going to learn how to do that later on in this post. One docker registry can contain many different docker repositories. Docker Repository is a collection of Docker images with the same name and different tags. For example, the repository we've used several times so far, mcr.microsoft.com/dotnet/core/aspnet repository, has many different images in it. Each image has it's own tag. For example, for the entire series, we've been using the mcr.microsoft.com/dotnet/core/aspnet:3.1 image which contains ASP.NET Core runtime with version 3.1. We can choose which one we want to pull by typing docker pull image-name:tag similar to GitHub repo and commits. We can go back to whichever commit we want and pull it to the local machine. That's putting it in very simple terms. But now that we cleared the air around these terms we can proceed to the next section.

      More About Docker Hub

      As we've mentioned, Docker Hub is just one of the registry providers. And a good one at that. We can find all sorts of images over there and push our own. We can create unlimited public repositories and one private repo free of charge. If you need more private repositories, you can choose one of the Docker Hub monthly plans. But that's not all it does. Besides providing a centralized resource for image discovery and distribution, Docker Hub's functionality extends to:
      • Automated builds of images on source code changes and parallel builds
      • Webhooks on image creation and push
      • Groups and organizations management
      • GitHub and BitBucket integration
      You can create your own account on Docker Hub right now and try it out. To push the image from the local machine to Docker Hub we need to type docker login and enter the credentials of your account in the prompt. After that, you can easily push the image by typing docker push accountname/imagename:tag. Easy as that. If we don't specify the tag Docker will apply the :latest tag to it. If we want to pull the image from the Docker Hub to the local machine, we need to type docker pull accountname/imagename:tag. The same rule applies here. If you don't specify the tag, you are going to pull the image tagged :latest. Let's move on.

      Creating a Local Docker Registry

      Docker Hub is super neat and very intuitive and offers a great deal of functionality for free. But what if we need more privacy? Or our client wants to use its own server. If that's the case, we can make our own Docker Registry. So how do we do that? Well, we can set up the registry in two different ways:
      • directly with the Docker command
      • using Docker compose
      Creating a local registry using docker is pretty straightforward and shouldn't be such a big deal if you followed the docker series so far. The registry repository is located on the Docker Hub here. (heh, registry repository) Aren't you glad now that we talked about the differences between these terms :) So if we want to set up the local registry we can type:
      docker run -d -p 50000:5000 --restart always --name my-registry registry:latest
      Now we can navigate to http://localhost:50000/v2/_catalog and see for yourself that your registry is up and running and that you have no repositories pushed to it. You should be able to see something like this:
      // 20191218210805
      // http://localhost:50000/v2/_catalog
      
      {
        "repositories": [
        ]
      }
      The same thing can be done with Docker Compose that we introduced in the previous part of the series. Let's navigate to the root of our solution and make a new folder Infrastructure and in it, another one called Registry. In the Registry folder, we are going to create a docker-compose.yml file:
      version: '3.0'
      
      services:
        my-registry:
          image: registry:latest
          container_name: my-registry
          volumes:
            - registry:/var/lib/registry
          ports:
            - "50000:5000"
          restart: unless-stopped
      volumes:
        registry:
      We defined the same thing we did with Docker command but with some additional goodies. We added a volume to persist our data and defined the restart policy as unless-stopped, to keep the registry up unless it is explicitly stopped. Now we can stop the registry we've spin up before with docker stop my-registry and docker rm my-registry to remove the attached container. After that run docker-compose up -d in the /Infrastructure/Registry folder. And guess what? We have the exact same registry we spin up before and we can access it by navigating to http://localhost:50000/v2/_catalog. We should also add the entry to the windows hosts file so we can use my-registry instead of localhost (usually at C:/Windows/system32/drivers/etc/hosts):
      127.0.0.1     my-registry

      Pushing Images to a Local Docker Registry

      Ok, so now we have our own local registry. Let's push some images to it. If you've followed the series you have the codemazeblog/accountowner:runtime image on your local machine. If not, please refer to part 4 of the series where you can learn how to create images with Docker Compose. To push the image to local registry we need to tag it appropriately first:
      docker tag codemazeblog/accountowner:runtime my-registry:50000/codemazeblog/accountowner:runtime
      And then we can push it to the registry:
      docker push my-registry:50000/codemazeblog/accountowner:runtime
      Now, if we browse to http://localhost:50000/v2/_catalog we will see that our repository list is no longer empty. We successfully added our image to the local registry! Moreover, if we navigate to http://localhost:50000/v2/codemazeblog/accountowner/tags/list we'll see:
      // 20191218210646
      // http://localhost:50000/v2/codemazeblog/accountowner/tags/list
      
      {
        "name": "codemazeblog/accountowner",
        "tags": [
          "runtime"
        ]
      }
      Here we can find the list of all the available tags of our image. To test this out we can remove the local image with docker rmi my-registry:50000/codemazeblog/accountowner:runtime. Now you can pull the image from the local registry by typing docker pull my-registry:50000/codemazeblog/accountowner:runtime and behold, once again, the image is on your local machine! That wasn't too hard, was it? There is one important note here. Pushing images to the insecure registries is not recommended and we can easily be the target of the MITM attack if we are not careful. We've talked about HTTP Security and MITM in part 5 of our HTTP Series so if you're not familiar with it, that's a good starting point. Setting a certificate for the registry is beyond the scope of this article and will be described later. For now, you can find more info on how to set a certificate for a local registry here. So be careful and secure your registries if you want to use them in production. Let's wrap this up with some examples of when we might to set up a local Docker registry.

      Use Cases for Local Docker Registry

      Now that you know pretty much everything you need to run a local registry, you might wonder: "But why should I use a local registry when I have all those nice options available?". There are a few reasons for that:
      • Total control of our registry and repositories
      • We need to set up a local network and to easily distribute images throughout it, to save the bandwidth
      • We have a closed network without internet access
      • Setting up a local CI build server that uses that registry
      • We don't want to pay some crazy plans to cloud providers to host our repositories
      • Many, many others
      So there you go.

      Conclusion

      In this part, we've tackled the concepts of the Docker registry and the Docker repository. We've learned what the differences between those are and we've learned more about a cloud registry service Docker Hub. We have also learned how to create our own registry locally and how to push images to it, and pull images from it. Local registries are neat and have several practical use cases. We gave you some ideas on how you might apply your Docker registry. Now that we know all this, we can easily transition to the next part where we are going to talk about Continuous Integration with Docker. The complete source code with the modifications we've made in this part can be found on the docker-series-local-registry-end branch of the docker-series repo on GitHub. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      2107 0 0 0 166 https://www.alvinashcraft.com/2018/05/21/dew-drop-may-21-2018-2729/ 0 0 173 http://www.baeldung.com/java-weekly-230 0 0 318 0 0 359 https://harrykjg.wordpress.com/2018/09/01/docker-compose/ 0 0 371 https://jpinjpblog.wordpress.com/2018/09/06/docker-local-registry-for-raspberry-pi/ 0 0 443 0 0 445 https://code-maze.com/ 318 0 446 https://code-maze.com/ 443 0 471 0 0 472 https://code-maze.com/ 471 0 664 0 0 722 https://code-maze.com/ 664 0 1522 0 0 1523 1522 0 1526 http://www.code-maze.com/ 1523 1
      Preparing a Continuous Integration Environment for Docker https://code-maze.com/preparing-ci-environment-docker/ Mon, 28 May 2018 05:30:26 +0000 https://code-maze.com/?p=2415 series, we've gone through a lot of different concepts and learned a thing or two about Docker. We've learned how powerful it can be and how to build and run images in several different ways. We dockerized our ASP.NET Core application and added MySQL image to it using Docker Compose. We had to clear some air around concepts like repositories and registries and learned how to set up and push to our own registry. All the things we have learned up till now led to this exact moment. Now we need to tie everything up in a neat little bundle of joy and connect our containerized application to the continuous integration server.  Docker revolutionized the way we think about developing applications, but at the same time, it improved the entire continuous delivery lifecycle. We can deploy applications easier than ever and that's all because of the power of Docker. [sc name="part_of_series" headline="This article is part of the series"] In this part, we're going to learn why we need CI in our lives, what makes a good CI tool, install TeamCity using Docker and prepare our continuous integration environment. In the end, we are going to run the whole process to see what we have accomplished. The starting point for this part is the docker-series-local-registry-end branch of the docker-series repo on our GitHub. Here are some of the things we are going to learn in this part:

      Why Do We Do Continuous Integration?

      If you are still not sure why we need to do continuous integration, I suggest you read through our article about continuous integration and its importance. Simply put, continuous integration is a process that helps us find problems in our applications sooner than later. When we develop an application locally on your machine, the environment is configured to your needs and "polluted" with a lot of different libraries, SDKs, and whatnot, and that's not the ideal environment to test if we want a production-ready application. We've all heard the expression "But it works on my machine!". That's what continuous integration is all about. It needs to work on every machine! Now that we clarified that, let's move on.

      What Makes a Modern CI Tool and Which One We're Going to Use

      There are a lot of excellent CI tools out there. Some are here for a long time (in terms of IT), and some are relatively fresh. It's a bit redundant to say that a modern CI tool must be fast, user-friendly and flexible since those are the features we already expect out of the box. To stay on top of the game, every tool needs to follow the trends and integrate with the newest technologies, Docker being one of them. You can't possibly expect any DevOps today without Docker, so it has become a key factor for a CI tool to jump on that bandwagon. We've described our favorite CI tools, and all of them support Docker integration to a degree. Some of them are even made ground up around Docker. For this article and series, our tool of choice will be TeamCity. Why TeamCity you may ask? As you know there are two types of CI tools. Those hosted and maintained by another company, and those that you can host yourself. TeamCity is a latter type, and that will enable us to try out all of the concepts we've talked about so far. That, and the fact TeamCity is a mature and excellent tool that can be used both by small teams and enterprises alike. So if you aren't familiar with TeamCity, I recommend you download it and try it out on your local machine first. We have an introductory article about TeamCity that can help you understand how the tool works. Of course, you can use your own tool of choice to follow along, but it might be a bit harder since some concepts may differ between tools. Onto the fun part.

      Installing TeamCity

      Hopefully, you did install and try out TeamCity on your machine before this chapter. If you haven't and it's your first time configuring TeamCity, you might find this chapter a bit confusing, but we'll do our best to explain it as simple as possible. Instead of downloading TeamCity directly from the website, we are going to use the Docker images provided by JetBrains on their official DockerHub account. So we are going to run TeamCity Server and Agent as Docker containers. And then, we'll use them to build and test our application by utilizing TeamCity's Docker features. Sounds a bit complicated, but stay with us, it's not that bad. You'll get what's going on soon enough.

      Pulling the Images

      First things first. Let's pull the images we need. We'll need a TeamCity Server, which can be found on the jetbrains/teamcity-server repo. So we are going to pull it by typing the docker pull jetbrains/teamcity-server command. We'll also need the TeamCity Agent, which we can acquire by pulling the official TeamCity Agent image form the jetbrains/teamcity-agent repo. So, we need to type docker pull jetbrains/teamcity-agent to pull the image to the local machine. That's all we need.

      Creating the docker-compose.yml File

      If you've visited the pages of the TeamCity Server and Agent, you've probably seen the instructions on how to run them. It looks something like this for the server:
      docker run -it --name teamcity-server-instance  \
          -v <path to data directory>:/data/teamcity_server/datadir \
          -v <path to logs directory>:/opt/teamcity/logs  \
          -p <port on host>:8111 \
          jetbrains/teamcity-server
      And for the agent:
      docker run -it -e SERVER_URL="<url to TeamCity server>"  \ 
          -v <path to agent config folder>:/data/teamcity_agent/conf  \      
          jetbrains/teamcity-agent
      Not that complicated, but we know the better way since we've learned a tool we can use when we need to run multiple containers that are connected. Yes, the tool we've talked about in part 4 of the series, Docker Compose. So let's see how we can translate this into the compose file. First, let's create a new folder inside our existing Infrastructure folder and name it TeamCity. After that create a docker-compose.yml file inside it. Now let's add the TeamCity Server configuration first:
      version: '3.1'
      
      services:
        teamcity:
          image: jetbrains/teamcity-server:latest
          volumes:
            - teamcity-server-datadir:/data/teamcity-server/datadir
            - teamcity-server-logs:/opt/teamcity/logs
          ports:
            - 8111:8111
      
      volumes:
        teamcity-server-datadir:
        teamcity-server-logs:
      As you can see, we are telling Docker Compose to pull the jetbrains/teamcity-server:latest image, create two volumes: one for data, and another for logs, and to expose the 8111 port so we can access the server at our http://localhost:8111 later. Ok, that was easy. Let's continue on by adding the TeamCity agent to the same file:
      version: '3.1'
      
      services:
        teamcity:
          image: jetbrains/teamcity-server:latest
          volumes:
            - teamcity-server-datadir:/data/teamcity-server/datadir
            - teamcity-server-logs:/opt/teamcity/logs
          ports:
            - 8111:8111
        teamcity-agent:
          image: jetbrains/teamcity-agent:latest
          environment:
            SERVER_URL: http://teamcity:8111
          volumes:
            - teamcity-agent-conf:/data/teamcity_agent/conf
            - /var/run/docker.sock:/var/run/docker.sock
            
      volumes:
        teamcity-server-datadir:
        teamcity-server-logs:
        teamcity-agent-conf:
      So, by doing this we added the TeamCity agent to our compose file. We told Docker Compose to pull the jetbrains/teamcity-agent:latest image, set the environment variable SERVER_URL to the URL of our TeamCity server, and then, finally, provided the volumes for agent configuration and the location of the docker.sock so that the agent can build and run Docker images. That's it, we successfully configured our Docker Compose. All that is left is to run it and see if it actually works.

      Running the TeamCity Server and Agent

      Open your PowerShell and navigate to the /Infrastructure/TeamCity folder if you already haven't done that. Let's try our luck and run spin up our containers by running the docker-compose up -d command. This should run the Server and the Agent in the background. If it's your first time running the command it might take a while since both images need to be downloaded to your local environment first. Now, you might run into the bug with some of the latest versions of Docker Compose and it might be something like this:
      ERROR: for teamcity-agent Cannot create container for service teamcity-agent: b'Mount denied:\nThe source path "\\\\var\\\\run\\\\docker.sock:/var/run/docker.sock"\nis not a valid Windows path'
      If you see this message, you can quickly fix it by setting the COMPOSE_CONVERT_WINDOWS_PATHS environment variable to something truthy in your PowerShell:
      $Env:COMPOSE_CONVERT_WINDOWS_PATHS=1
      After that, run the docker-compose up -d again, and you should not have the same problem anymore. If everything started correctly you should be able to access the TeamCity server on http://localhost:8111. TeamCity has a pretty straightforward setup, but if you are not sure what to do, you can check out our article on setting up TeamCity to learn how to do it step-by-step. Once you've finished the setup part, the only thing that remains is to authorize the TeamCity agent with the Server.: Agent authorization Once you click the Authorize agent button, your TeamCity Server should be ready to use.

      Building and Running our App with TeamCity

      To wrap this article up, we are going to set up a TeamCity project with a build configuration to pull and build our image from a local registry. Hopefully, you still have your local registry running, if not, refer to the previous part of the series to refresh your memory on how to create it. To start out with TeamCity, let's set up a new TeamCity project: teamcity project init You can use our repository or your own. We'll start out with the command line build step, and see what other options are available to us later. To test things out we've put the docker version command. We'll change it later to something more appropriate, but for now, let's run our build and see if it actually works. Just make sure to set the branch to the right one before running the build. You can use the docker-series-docker-compose-end branch from our repo. Let's change the default branch: edit default branch And we're all set to run the build. Just press the little run button at the top, and see how it goes. If you've done everything correctly, you should see the docker version output in your build log:
      [12:14:38]	[Step 1/1] Starting: /opt/buildagent/temp/agentTmp/custom_script6146963977557132132
      [12:14:38]	[Step 1/1] in directory: /opt/buildagent/work/8e843e245d8634bd
      [12:14:38]	[Step 1/1] Client:
      [12:14:38]	[Step 1/1]  Version:	17.12.0-ce
      [12:14:38]	[Step 1/1]  API version:	1.35
      [12:14:38]	[Step 1/1]  Go version:	go1.9.2
      [12:14:38]	[Step 1/1]  Git commit:	c97c6d6
      [12:14:38]	[Step 1/1]  Built:	Wed Dec 27 20:11:19 2017
      [12:14:38]	[Step 1/1]  OS/Arch:	linux/amd64
      [12:14:38]	[Step 1/1] 
      [12:14:38]	[Step 1/1] Server:
      [12:14:38]	[Step 1/1]  Engine:
      [12:14:38]	[Step 1/1]   Version:	18.03.1-ce
      [12:14:38]	[Step 1/1]   API version:	1.37 (minimum version 1.12)
      [12:14:38]	[Step 1/1]   Go version:	go1.9.5
      [12:14:38]	[Step 1/1]   Git commit:	9ee9f40
      [12:14:38]	[Step 1/1]   Built:	Thu Apr 26 07:22:38 2018
      [12:14:38]	[Step 1/1]   OS/Arch:	linux/amd64
      [12:14:38]	[Step 1/1]   Experimental:	false
      [12:14:38]	[Step 1/1] Process exited with code 0
      Great! Now let's replace the docker version command with something more useful:
      image="my-registry:50000/codemazeblog/accountowner:build-%build.number%"
      docker build -t $image .
      
      docker push $image
      So what's happening here? First, we are going to set the image variable to the one we pushed to the local registry in the last post. But, not only that, we'll add one of the predefined TeamCity variables to it, to tag each built image differently by appending :build-%build.number%. You'll see in a minute how that works. After that, we are going to build and tag the image with the docker build -t $image . command. And finally, we are going to push the newly built and tagged image back to the local registry. So, save the configuration and press the button again. This time it should take longer to build the image, and if you look at the build log, you should see all the build steps we defined in our Dockerfile, including the tests and publishing to runtime image.

      The Final Result

      Once you kick off a few of those builds and try changing the project, navigate once again to the registry at http://my-registry:50000/v2/codemazeblog/accountowner/tags/list and see how it changed. For us it looks something like this:
      // 20191219180458
      // http://localhost:50000/v2/codemazeblog/accountowner/tags/list
      
      {
        "name": "codemazeblog/accountowner",
        "tags": [
          "build-3",
          "runtime"
        ]
      }
      And of course, there are new images on the file system since we are using local registry. And if you've followed these instructions, your repo will trigger the build on every check-in, since the VCS trigger is automatically configured by the TeamCity server. There you go, a fully functioning TeamCity powered continuous integration process, and everything thanks to Docker containers.

      Conclusion

      This has been a brief introduction to the Continuous Integration with Docker and TeamCity. We did everything locally, but since everything is done using Docker, this setup is easily portable to any other machine. You could for example provision an AWS EC or GCE instance and host the entire setup there. Mind you, that in the production environments, it's not wise to host both the TeamCity Server and TeamCity Agent on the same machine. There are still things we need to cover, so in the next part, we are going to add some integration tests and learn how to play around with Docker Compose to trigger the build every time instead of using Docker cache. We are also going to learn how to make dependent configurations and make our own environment variable that we can use with TeamCity. So stay tuned, a lot of good stuff coming your way! Full source code with the modifications we made throughout this article can be found on the docker-series-ci-prepared-end branch of our docker-series repo. [sc name="subscribe" formNumber="3820" contentType="CI/CD"]  ]]>
      2415 0 0 0 177 https://www.alvinashcraft.com/2018/05/29/dew-drop-may-29-2018-2734/ 0 0 179 http://www.baeldung.com/java-weekly-231 0 0 182 http://kirblog.idetalk.com 0 0 184 https://code-maze.com/ 182 0 185 184 0 186 https://code-maze.com/ 185 0 197 https://chevenix.wordpress.com/2018/06/03/szumma-112-2018-22-het/ 0 0
      Transcript of the Interview with Troy Hunt https://code-maze.com/troy-hunt-interview-transcript/ Wed, 09 May 2018 04:30:45 +0000 https://code-maze.com/?p=2670 This is the transcript of the intrerview with Troy Hunt. You can find the full interview, video, audio and infographic here. Vladimir: Hi everyone, my name is Vladimir Pecanac and I'm an author and a founder of Code Maze blog. My guest today is a Microsoft Regional Director and the Most Valuable Professional for developer security, a family guy and a prolific author on Pluralsight. He also blogs a lot and attends conferences all over the world and still finds the time to have a personal life and play tennis with his children (Troy laughing). It is my great pleasure and honor to talk to one and only Troy Hunt. So Troy welcome, nice to have you today with me. Troy: Thanks Vladimir, we should tell people I had to, I didn’t realize, I don’t know I had something in my calendar and I knew we are gonna talk and I forgot that it was gonna be recorded. So I had to come home from a tennis court and I am still like sweaty and puffing and tired. So we’ll see if I make any sense. Vladimir: Yeah, I’m sorry about that. I should have mentioned that we’ll be recording. Troy: No, no, no it’s ok. We’ll manage. Vladimir: (Vladimir laughing) So, have I missed something in that elaborate intro? Do you have something more to add? Maybe … Troy: Did you mention the “Have I Been Pwned”? Vladimir: I don’t think I have. Maybe you should. Troy: Ah, oh right. Alright, right so yes all of the things that you’ve said and the creator of “Have I Been Pwned” another breach verification service, which has become surprisingly popular. So (Troy laughing) that is that as well. Vladimir: So “Have I been pwned” was your pet project, to say it like that and then grew up to be something entirely else. So what do people do when they get to “Have I Been Pwned”? How do they use it? Troy: Well they go to haveibeenpwned.com and they enter their email address and then it comes back and it tells them all the websites that they have been breached in and then they cry for a little bit (both laughing) and then they figure out, hopefully, they figure out that is actually very important to have unique credentials on every website. So, what I’m really trying to drive people towards is using a password manager. So, “Have I Been Pwned” is sort of interesting for knowing where you have data exposed, but the real impact I hope it makes is that people improve their online hygiene, their security hygiene and honestly the best way to do that and really the two best possible things that people can do after finding themselves is to get a password manager. I’ve used “1password” for years, the program “1password”. I don’t just use a single password (Troy laughing), so I use the program called “1password” and turn on two-factor for multistep verification, or whatever they like to call it on every service. And if we can get just more people doing those two really simple things, we’d make a really big difference to online security. Vladimir: Yeah, so you’re trying to raise awareness in people’s minds of where they put their passwords to and that even the biggest websites like Dropbox and LinkedIn can get hacked at some time? Troy: You’re right and to be honest it’s almost a bit contradictory to sort of say “Be aware of where you put your passwords” and then say “Yeah, but even the biggest services get hacked!” you know, because some people might say well if the site doesn’t look trustworthy, then maybe I shouldn’t use it. But if it’s LinkedIn, you know or MySpace or Dropbox. Some of these are some of the biggest web assets in the world. You can’t reliably make a decision about how the site is going to protect your credentials, just based on how familiar you are with it. So we always have to sort of work with this assumption of, we’re using services which may one day be breached and we as normal everyday consumers of these services need to be resilient to that. Vladimir: Yeah, I completely agree. I’ve been guilty of using my passwords all over the place and when I've got a bit more aware of the consequences of that I started using different passwords. And it’s pretty strange that people are using passwords like admin1234 or something like that. I heard that there is even a list with billions of passwords circling around lately, so … Troy: Yeah look, we’ve seen a few of these lists. There was a lot of news in December about a list of 1.2 billion usernames and passwords. What that was, was someone consolidating their credentials from many different breaches and consolidating it all into one single collection of email addresses and passwords. And really the reason why that was newsworthy and the reason why it is worrying is because there’s a lot of usernames and passwords in there which will work on many different systems. So, because of password reuse we see people logging into other people’s say Gmail accounts or their bank account or something totally unrelated to where they originally had their password exposed, but because it’s the same password, well it works in other places. Vladimir: Yeah, that’s a pretty tough problem for the most of the people. So (Troy agreeing), I think your website will help raise awareness about that. And I’ve even found one of my emails from MySpace and I don’t even recall registering for MySpace ever, because haven’t used it. (laughing shamefully) So … Troy: Well this is also the problem, right. Like we have been online for many many years and there are so many places where you need to create accounts, even to do simple things that there’s no way you’re going to remember all the places and ultimately that the reality that we all have to face, is that our data has been exposed many times over. Many times we don’t even know about, that “Have I been pwned” doesn’t even know about, because the site owners don’t know about it (Vladimir agrees). And it could have been a service that you’ve signed up to ten years ago and something you might have used once. So, it’s you know we have a very, very long trail of digital footprints. Vladimir: Yeah, okay. So that’s about that. We won’t go any further. I think it’s pretty clear that people should care about their passwords a lot more. And let’s go on. One thing I think we haven’t mentioned... You live in Australia, yeah? Troy: Oh, yeah (Troy laughing). Vladimir: I forgot to mention that, but your accent is giving away your location in the world. Troy: (Troy laughing) Yeah, it does give it away. And for anyone who thinks my accent is British, no no it’s not. Vladimir: No, it’s similar, but you can tell (Troy disagrees amused). So, how do you cope with living upside down most of the time? Troy: I cope very well with it (Troy laughing). Vladimir: Do you use some glue to put on your shoes or something? I don’t know how… Troy: There’re actually conspiracy theories out there that think Australia does not exist and that it’s all made up (Vladimir laughing). But no, look its real. You know I spend a lot of time traveling as well. So I spend a lot of the year overseas and I get to, sort of see other ways of living. I’ve lived overseas a lot myself as well. So it’s you know, I guess I have a perspective where I can look at Australia in the context of the rest of the world and it really is such a unique place here. That, yeah everyone who comes here loves it and it’s just a very, very nice place to come home to each time. Vladimir: I’ve heard a lot of pretty nasty creatures live in Australia. Is that true or …? Troy: Yes. (grimly) Vladimir: Yes?! (both laughing) Everything that lives in Australia tries to kill you, pretty much. Troy: It’s like you got to be rational about it, right. So you know, where we live and what are the things here that kill us? So we live on the water where we have sharks. We know we have sharks because we’ve caught sharks before. We’ve literally been fishing and caught sharks, but they’re not big sharks and you know like maybe a meter and something long. Well, you’re very unlikely to be killed by them. But it’s a little bit like anything where you sort of do your risk assessment. So I wouldn’t be swimming in murky water at dusk or during the night. Like the people that get taken by sharks, it’s like you’re swimming at 2 a.m. in a canal. Vladimir: (Laughing) You’ve asked for it, so you got it. Troy: Yeah, it’s the same every time someone gets taken by a crocodile in Australia, it’s often a tourist and they went swimming next to the sign that says don’t swim with the crocodiles (Vladimir laughing) So what do you expect. You’re gonna be eaten by a crocodile. Vladimir: (Laughing) You’ve asked for it. Troy: It’s funny that they call it, yeah this is like Darwin theory, right. Like the theory of evolution. The weakest and the stupidest will get eaten. And a lot of our crocodiles are actually in a place in Australia called Darwin. So you know, there might be something there. Vladimir: (Laughing) That’s pretty ironic. I like it. So you live on the Golden Coast? Troy: On the Gold Coast. So we live in an area of Australia, if anyone looks at a map of Australia we’re sort of on the very far eastern corner. Just above the far eastern corners, Australia looks a little bit like a diamond on the eastern side. And the Gold Coast is a 6th largest city in the country. We have about 600.000 people here. We’ve got everything you could possibly want in the city, in terms of schools for our kids and health care and all sort of thing, but it’s also very very lifestyle orientated place. So there’s lots of outdoor activity. Most people are here because they just really want to live in this place. They’re not here because they have to be, like Sydney or Melbourne. And if anyone’s curious about the Gold Coast, just go to Google Images and google the Gold Cost Australia and you’ll know. Vladimir: Yeah, I’ve seen some videos of you riding your jet ski on the Gold Coast. It looks wonderful. Troy: Yeah, sounds about right. (laughs amused) Vladimir: Okay, so you seem to be a very busy man. You travel a lot and you produce a lot of content and do a lot a traveling and conferences, but you’re also a family guy. How do you manage those two? How do you balance that? Do you have any problems balancing it? Troy: Well I think, maybe the first thing to mention is if anyone’s interested in sort of how I do that, there’s a talk that I’ve done called “Hack Your Career”. And if you go to youtube and search for Troy Hunt “Hack Your Career” I talk a bit in more detail there. I guess to sort of answer it more specifically here, probably the most important thing with this is, that because I’ve got a wife and two kids this only works because she supports what I do. And if I’m gonna travel overseas, for example, we’ll talk about it and go “Hey, you know does this make sense? Should I go overseas now? Are you gonna be alright with the kids? Is it going to work?” So we have a shared vision, if you like in terms of the way I work and what we find is that sometimes that can make things very hard. So I’ve been away for say four weeks at the time before, which I’m trying not to do anymore, but I mean that’s a very long period and then she has to look after two kids on her own. But the balance if you like is, you know we’ve just said, “Okay, off I’ve been out playing tennis with my son. I mean it’s nearly 4:30 in the afternoon here. Most people would be at work in their shirt and you know, doing work things and I’m just doing whatever I want.” But the offset is that I’ll probably be working very late tonight and I normally start at 5 or 6 o’clock in the morning as well. So I am happy with that balance and I’m happy with the uncertainty that sort of independent life gives me, but that also doesn’t work for everyone. I’m really contentious of that. Vladimir: Yah, I asked that because I find it to be the most difficult aspect for me at least for now, while I have a full-time job and I write a blog and do interviews and all kinds of stuff and still have to attend to my wife. It’s so difficult to balance all that. She does support me and she understands that I need to invest some time in all that activities, but it’s still hard to achieve all that. So kudos to you for balancing all that. Troy: Yeah, look thanks for that and just a full perspective as well and maybe to make you feel a little bit better about it too. I started blogging in 2009 and this was when I first started investing my personal time in this pursuit and it took years of blogging on evenings and weekends and then doing conferences and talks and everything in spare time and often holidays, before I made any money whatsoever out of it or before I had any sense that it might actually be something that I could make a career out of. So it took a very long time of sacrifices before things started happening and it’s like I’m really really contentious of that every day, even now. It’s because it’s still recent history now. It doesn’t seem that long ago to look back. It’s only sort of 8 or 9 years. Vladimir:  You need to have that motivation and understanding that you need to sacrifice some things to get something else in the future. So that’s also the hard part. You need to believe in yourself and to be patient enough to get to that part when things start rolling down. Troy: Well I think also you got to enjoy the journey. (Vladimir agrees) You know if someone was sort of saying “I’m only doing this so that one day I could make more money out of it“. You don’t want to go like years being miserable and certainly, I did not, you know. Like I went years like not necessarily expecting this to turn into what it is today, but enjoying what I was doing … Vladimir: Yeah, you started because you like to do it. Troy: Yeah exactly. And look if you’re not enjoying it, you might as well just go back and working on … Vladimir: Yeah, it won’t succeed if you don’t enjoy it. At least it’s what I think. Because it requires a lot of sacrifices and you need to be motivated and how are you going to be motivated if you don’t like what you do? Troy: Well you know, your passion will sort of show through in what you do, too. And if it is something that you genuinely love and you’re enthusiastic about, then that will be obvious as you do what you do. And if you don’t, well you know it’s gonna be hard. Vladimir: Yeah, yeah my thoughts exactly. So I have one question for you. It might be a rumor, it might not, but I’ve heard that you teach people to do something called “squirrel injection”. Is that right? Troy: Yeah! (Laughing) Vladimir: (Laughing) Is that right? Are you a veterinarian, also? Troy: Oh man. How much time we’ve got to explain this? So I’ve been doing a talk. I think I first did this talk I mustered up a year and a half ago. A lot of the talks that I do at conferences; look they’re security talks but their stories as well, right. So that they’re not just the mechanics of security. I like to sort of take people on a bit of a journey and make it something that they enjoy watching. Like I want people to come to my talks and learn stuff, but to have fun too. So I try and draw a lot of sort of examples from the real world. I try and bring a lot of interesting insights into information security when I talk about it. And one of the things that I wanted to demonstrate to people is that SQL injection is a very very easily exploitable vulnerability. And when I was preparing for this talk I was looking at YouTube videos, because I wanted to see some of the YouTube videos that children were making about hacking. And when I say children, we’re talking often sort of 15, 16, 17 years old … Vladimir: Yeah, teenagers. Troy: Legally children, but really still kids. And there are tools out there that make attacks like SQL injection in particular, really really simple. And I found this video of this kid and it was just perfect for what I wanted to demonstrate because the kid was very unsure of himself. He spoke in a not very confident sort of way. He used terms which were very strange. Like obviously he didn’t actually understand what they mean. He had trouble sort of finding the right words and one of the things that he said, several times in the talk is, instead of saying SQL or "sequel", he called it squirrel. Vladimir: (Laughing) I laughed my ass off. It was hilarious. Troy: This is like it’s just the most perfect thing to show in a talk, because everyone’s laughing at it, right. Saying this is hilarious. But there’s a deeper message here, which is that this kid is doing something highly illegal which is really really damaging to the organization and someone built the code in this application that a kid, who doesn’t even know how to pronounce SQL can come along and exploit and suck your data out of, you know. Like that to me is that that’s just a really really significant thing. So yeah, there was a bit of fun that talk and every time the kid said squirrel I put a picture of a squirrel up on the screen … Vladimir: (Laughing) Yeah, that squirrel jumped out of there. That was hilarious. You’ve had a few of those talks that are really memorable and I remember them to this day. I don’t have much of a visual memory but those talks really resonated with me and I liked them a lot. Troy: Awesome. I’m really glad to hear that. Look, I mean if anyone wants any more talks that were done before and wants to see them; if you go to troyhunt.com/recorded-talks there’s how many now? Well, there are 38 talks that have been recorded and are listed there at the moment. So heaps of stuff including "squirrel injection". Vladimir: Yeah, you can watch for days. (Troy laughing) There’s, there was one hilarious one. I think you tried, not tried but you did some SQL injection with your son when he was 4. Troy: Hmmm, yeah. I‘ve actually used him a couple of times. I’ve used him initially when he was 3 to demonstrate SQL injection. Squirrel injection! Vladimir: Three years old?! Troy: Yeah, yeah because if you can hold a mouse you can do SQL injection. And like again that the point of this was to show that all I really needed to get him to do copy and paste. Because using automated tools it’s as simple as taking a URL, usually with the query string parameter and pasting it into one of these tools and just hitting the button and it goes. As he’s gotten a little bit older I’ve actually used him in some conference talks. So I’ll be on the stage and I’ll do my talk and then I’ll sort of say “Look you know, let me show you how simple SQL injection is”. So I would get my son up on the stage. I’ve got him to do it a couple of times last year in Oslo and also in Sydney for the NDC conferences. He comes up on stage and he’s like, he’s 7 years old. Like he’s still a little kid. So he stands up on a chair and he has a hoodie, so he puts his hoodie on (Vladimir laughing), cause he’s gotta do that before he starts hacking. And everyone thinks it’s hilarious and I get really good marks for the talk because there’s a kid. Vladimir: That time he did it independently, you didn’t help him or anything. He had a script and did it from the start to the end. Troy: We rehearsed it a few times because he’s old enough now to actually know how to copy and paste on his own. Vladimir: So besides attending conferences, you do a lot of Pluralsight and you have how many video courses right now? Troy: It’s a good question. It must be nearly 40 of them. Actually, I’ve got quite a few that are just waiting to publish. So I’ve been doing a quarterly series right now called “Security culture”. I put one of those out. Yeah, so one of those went out in January, in fact, I might just look now. It says 35, but I think within the next couple of weeks that will go up to about 40. So there’s the q2 of 2008 security culture is all recorded and ready to go. I’ve also got some play by plays coming up. So the play by play is where you have two people being videoed and they have a discussion a topic, so normally security for me and the people get videoed and then we show some things on screen and they get videoed as well. So whatever, this is the exclusive insight of what’s coming. So I’ve got one coming on “JavaScript security” with Aaron Powell, who’s a JavaScript MVP down in Sydney. I have got one on the “OWASP top 10 2017” with Andrew van der Stock and he’s on the OWASP Board and has been a really instrumental part of OWASP. And then I’ve also got two play by plays coming with Casey Ellis. So he’s a mate of mine who’s the founder of Bugcrowd, the manage bug bounty program. So I was just actually putting the finishing touches on one of those today. So there’s one for bug bounties for companies that are thinking of running a bounty and there’s also another one which is bug bounties for researchers. So, if you are a researcher or a hacker and you want to get involved in bug bounties that course will help. So once all those are out we’ll be up to 40 courses. Vladimir: Yeah, play by plays are pretty useful in my opinion. I’ve watched a few of those on Pluralsight and I learned a lot there. There’s one I think, about TDD or something like that, it’s really really nice to see someone’s thought process when doing something like that especially if he’s experienced, you learn a tremendous amount. Troy: That’s cool. I’m very glad to hear that. It’s always nice to actually hear from people that watch the courses, because for us as authors, most of the time we’re sitting at home on our own with no humans around and it gets really boring and repetitive. So it’s actually really nice to hear that. Vladimir: Yeah, you live in your head and don’t know if it’s really useful or not and … Yeah, yeah feedback is really important. I love feedback. Every comment I get I read it and think about it and respond to it because I think that’s the way you learn and that’s the most important part of sharing knowledge. Troy: Yeah, absolutely. Vladimir: So, how much time you still have? Do we have time for some more questions or… Troy: Yeah, I’m okay. I can keep going. Vladimir: Okay. So I saw you recently attended a meeting with American Congress. So, how’s that been for you? Have you been nervous, maybe? Troy: You know, I wasn’t nervous. I don’t get nervous anymore and I know that there are some speakers who are very experienced and they say they still get nervous, but it doesn’t happen for me anymore and I think if anything I get a bit excited. Insofar I actually really enjoyed doing these things and maybe the excitement sort of overcomes the nerves. But for Congress you know, I was there at the end of November testifying on how data breaches are impacting our ability to do knowledge-based authentication. So yeah, if your bank calls you up and says or let’s say you call your bank and you need to verify who you are and your bank says, ok well just tell us your date of birth and then we’ll know that you are who you say you are. Except, your date of birth has been leaked in all these data breaches. Yeah, so you can’t reliably use that piece of knowledge-based authentication. And the Congress thing was very interesting because it was my first trip to Washington DC I was seeing all of these things that I’d only seen in the movies before. Vladimir: Yeah, I can imagine. Troy: Yeah, like all the memorials and stuff like that. You know, I’ve seen the White House I’ve only seen that in movies, like being blown up and stuff like that. You know maybe like “Independence Day” or something. And yeah, when I went into Congress that the really interesting thing there was that it’s obviously a very formal environment. So on the one hand, there’s a lot of structure to it, but on the other hand, I was speaking to people who were sort of the staffers, who were organizing everything and they were very very normal, down-to-earth people. Very lovely people. And what I sort of really really worked on for my testimony, for my written testimony and then my… So you had to sort of submit a written testimony, which I think was sort of a few thousand words and then a verbal testimony which is a five and a talk and then you got questions from congressmen and congresswomen and what I really had to work on was to try and explain things in ways that non-technical people could understand, but also give enough information such that they can then give my testimony to staffers, so that they can go away and do something useful with it. So you know, don’t make it detailed, but give enough detail they can use. And it’s like that was fun. I kind of like figuring out how to communicate in a way that works across those demographics. Vladimir: Yeah, and while you’re at it. I watched some of your videos at Pluralsight and they seem pretty down-to-earth and simple. Do you intentionally make them that way? Do you think it resonates better with people or it comes naturally to you? Because when I’m writing my blog I’m trying to dumb down things as much as I can because I think that most important thing is that more people understand it and not to get technical and complicated and to sound more authoritative or something like that. Troy: Well yeah, there are a few things there. So in terms of Pluralsight, the vast majority of demographics of people who watch the courses want introductory content and we know this empirically. So we know from the numbers, from the evidence that if you write something for an intermediate audience, the number of people that watch it is going to be significantly less and if you write something for an advanced audience it’s going to be significantly less again. And Pluralsight is a royalty based model, so when you sit there and you’re deciding “What do I do with my time? What is the highest and best use of my time?” If it’s anything not targeting beginners, it’s basically a waste of time, financially. And I want to caveat it there because it’s going to be very useful for some people, but if your purpose is there to make money out of it, then that’s not gonna be a good return. So yeah, that’s why the vast majority of my content is very entry level. In fact, just coincidentally, I’ve got some stats up in front of me at the moment and my most popular course in terms of, that the return of the effort is one called the “Web Security and the OWASP Top 10: The Big Picture”. And this is just slides. This is targeted at manager or people who might watch it over lunch or people with not any technical depth whatsoever. And there’s about 32,000 hours’ worth of that I’ve been viewed. Vladimir: Oh my God. That’s so much. Troy: Yeah well you know, that is very important in terms of the monetary return. So the effort, it has to have a return. Now in terms of my blog, you’ll see that there’s a really broad range there, you know. So if I think about some of the recent things I’ve written, I’ve gone into a lot of detail on content security policies, SRI, HSTs and that goes down to as much detail as anyone would need to actually implement it. Vladimir: Yeah, that’s much deeper than some courses you’ve done. Troy: Correct. And then you have a look at other blog posts and they might be very high level. Some of them might not even be technical. I’m writing one at the moment, that I hope to publish today about how real-life examples that are not very good analogies for digital equivalents. So you know, when someone says: Well, taking data off the server is like walking into someone’s and stealing something. No, it’s not, it’s a terrible example. So I’m writing about that, but it’s not technical. Vladimir: Yeah, yeah, yeah … And it’s a pretty way to explain to people that they shouldn’t rely on real-life examples. Like there was one with exchanging cards and data breaches, but if you exchange data either party gets both their data and the data they acquired from the second party. So it’s not like exchanging cards where you give a card and get another one in return. Troy: Well look, I understand why people are doing it. It’s because they’re trying to explain things in relatable terms, but the problem is that very often in an attempt to do that they actually conflate the issues and they make it more confusing. And my sort of conclusion and what I’ve actually done is I’ve taken a lot of tweets that I got over last few days, in response to another blog post and I’ve tried to use them to demonstrate that look we just can’t explain the technology with these IRL equivalents and these in real life equivalents. And in fact, in this particular case, you can explain the technology perfectly, well just by talking about the technology. Vladimir: Yeah. So, you must be careful when choosing examples to explain … Troy: Yeah. I think so. I think we can adequately explain things with just the technical terms. Vladimir: Yeah, okay. That’s fair enough. So you have done a lot in your career and have many titles and accomplishments. What are you most proud of? Is there something you would like to tell us? Troy: I think to date it would have to be “Have I been pwned” because this was … It’s funny when I think back about it because I was about to say this was something I never really gave a lot of thought to, you know. I just created it on a whim. I had some downtime when I was in my old job. I was in the Philippines for a little while and I had nothing to do so sat in the hotel room and I started building this. But there’s a part of me as well where I’ve always had this sense and if I think back even to sort of .com era and I’m thinking 1997-98 where I was building little things and marveling how even then, and that was before, cloud is out today as well … So I was marveling how you could just sort of sit on your own and create something that the world could use and could actually turn out to be really valuable one day. And I’ve always wanted to do something like that. And I didn’t necessarily think that “Have I been pwned” would be that thing, but now when I look back at it; it has become something that’s actually really valuable and really useful. And I still find it just amazing that I can just sit at home and do this. It’s just one guy, right. Vladimir: That’s amazing and you talked about having millions of requests someday, some days when you go live on some media. That’s unimaginable. Troy: Yeah, yeah well. I mean it’s even more than that because it’s not requests, it’s unique users. Vladimir: Unique users per day?! Troy: This is millions of unique users. In fact, I’m just gonna look back on my... Vladimir: There was some 68 million or something like that you’ve mentioned one time. Troy: Yeah look, sometimes things do just go really nuts in terms of scale. You know, when I look at requests I had one-day last month, this was on 30th of March and I had 49 million requests on that day. Now that’s not users that was only 7,900 users only. (Troy laughing) Vladimir: (Laughing) They liked it. Troy: I know. This is the thing though, this is still just like the one guy sitting at home building some stuff. I’m still running in on yet very very cheap infrastructure as well. And you’ve got nearly a million people in a day and the normal day now is somewhere between 100,000 and 200,000 visitors. Vladimir: Yeah, that’s crazy. I can’t even imagine how that would be. So let’s wrap this up and let’s tell people where they can find you and maybe recommend some Pluralsight videos where they can start if they want to go into security a bit deeper. Troy: Sure. So look, I mean obviously the most logical place to start is troyhunt.com. That’s got a lot of stuff on it. You’ll find me on Twitter as well @troyhunt. That’s got a lot of stuff. I use Twitter quite a lot. If you start there, those two places like lead to everything else, you know. Like that’s the center of the universe of Troy. Vladimir: Yeah, so there’s the best place to start. Troy: Absolutely. Vladimir: Okay Troy. It was a pleasure talking to you. I would like to do this again sometime if you find the time. I was surprised you had time for this interview too. Troy: I’ll make time. Look it’s fun to do these things and you know, it gives me a break from just sort of sitting there blogging or playing tennis. Vladimir: Yeah, thank you for that and thank you for the wonderful interview. Troy: My pleasure. Vladimir: We’ll talk again sometime. Troy: Good on you! Vladimir: Cheers. Bye.  ]]> 2670 0 0 0 Interview with Troy Hunt https://code-maze.com/interview-with-troy-hunt/ Wed, 09 May 2018 04:30:18 +0000 https://code-maze.com/?p=2677 The Interview https://youtu.be/skOA2a_DBBU To download just the audio (if you prefer listening): Interview With Troy Full Audio. And for the readers out there: Transcript of the Interview. And if you subscribe you can even get the BONUS infographic of the interview: [thrive_2step id='3133']Troy Hunt Infographic[/thrive_2step]

      Summary

      1. Introduction
        • Introduction to the interview
        • Who is Troy Hunt?
      2. About haveibeenpwned.com
        • Troy talks about his website Have I Been Pwned because I've missed to mention it in the introduction *facepalm*
      3. Raising awareness of password security
        • We're talking about the main goal of haveibeenpwned.com and how easily people give away their passwords
        • Troy reveals how he manages his passwords for years, and recommends the right way to do it
      4. Password lists circulating through the internet
        • About the password aggregators and the password lists that have accumulated over the years
        • The problem of too many services out there that eagerly collect our credentials, even if we use them once or twice
      5. How living in Australia is for Troy
        • How Troy copes with living upside down all the time
        • About Troy's "British" accent
        • Australia doesn't exist, what?
      6. Nasty creatures inhabiting Australia
        • How to get killed in Australia
        • About Darwin's theory and a place called "Darwin"
        • About Gold Cost (where Troy lives)
      7. How Troy manages to balance his work life and family life
        • Hack your career
        • Troy explains how he manages to balance the most important parts of his life: family and career
      8. The requirements of being successful
        • About the sacrifices, motivation, and love of what you do
      9. About the veterinarian side of Troy (Squirrel injection)
      10. How even the youngest children can hack stuff
      11. Exclusive information about Troy's latest plans for Pluralsight courses
      12. How he attended the American Congress
        • Troy talks about his experience in Washington DC and the movie-like atmosphere of the city
        • Does he ever get nervous?
      13. About the technique that he uses to make his Pluralsight courses so good and profitable
        • Troy shares what makes a good Pluralsight course
      14. How real-life examples translate badly into the technical world
      15. What his greatest accomplishment is
        • Hint: HIBP - Troy gives a bit of background on how haveibeenpwned.com started and where it is now
      16. About the number of people and requests, he gets on haveibeenpwned.com sometimes
        • Troy gives some insight into the number of requests he gets on certain days on haveibeenpwned.com
      17. Where you can find more about Troy Hunt
      18. Conclusion
      [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      2677 0 0 0 ]]> ]]> ]]> 145 0 0 146 https://code-maze.com/ 145 0
      Top Mobile Continuous Integration Tools https://code-maze.com/top-mobile-continuous-integration-tools/ Tue, 15 May 2018 06:30:15 +0000 https://code-maze.com/?p=2719 It is continuous integration and continuous delivery. We've talked about the importance of continuous integration, and about the difference between web and mobile continuous integration tools. It seems that companies that create mobile apps are still not fully aware of the importance of the CI/CD tools in their development process. Continuous integration tends to be overlooked when you have small teams working on an app, or when an app is not big enough. This is a wrong way to look at it and whatever you do or create, you can benefit from using a CI tool. Our focus will be on the CI tools that specialize in mobile apps only. So let's check out the Top Mobile Continuous Integration Tools:

      Nevercode.io

      Nevercode page The first continuous integration & delivery tool on our list is Nevercode.io, a polished and robust tool to fully automate your mobile app build and test process. If you opt-in to use Nevercode.io you get:
      • Automatic configuration and setup of iOS, Android, Cordova, Ionic & React Native projects
      • Test automation with Firebase/ Amazon Device Farm and test parallelization
      • Automatic publishing to various platforms including Google Play, iTunes Connect, HockeyApp, Crashlytics, TestFairy
      • Comprehensive analytics
      • Concurrent builds
      • Integration with Slack, HipChat and other communication channels
      • Encryption of data
      • First class support
      Nevercode.io offers a 14-day trial, and three pricing plans to their customers and they are pretty flexible, so you can choose the one most suitable for your team. Each plan includes unlimited team members support, but the difference lies in build parallelization and live support and even training. If you ever find yourself stuck you can check the comprehensive documentation pages and blog, or talk to the support team. Verdict: Polished and modern tool, with a heap of very neat features, flexible pricing plans, 14-day trial period, comprehensive documentation pages and lively support.

      Visual Studio App Center

      Visual Studio App Center page As you might have guessed it by the name, Visual Studio App Center comes from Microsoft labs. Microsoft is known to have both good and bad products (looking angrily at Skype). So let's see which side the App Center is on. At first glance, you get a simplistic design, but when you dig deeper you get a lot of goodies out of the box. As you can see in the picture above, App Center supports a lot of different kinds of applications, and can automatically detect what app you want to build. When you decide to use App Center, you can count on:
      • Support for: iOS, Android, Windows, React Native, Xamarin, and even more apps
      • Intuitive but powerful UI
      • Integration with GitHub, BitBucket, and VSTS
      • Push notifications
      • "Crash reports so nice, you’ll  almost want your app to crash."
      • A lot of analytics and charts
      There is a free 30-day trial period which includes 240 build minutes per month, unlimited users, analytics and crash reports, up to 5 audience segments for push notifications. You can scale your plans by adding additional build concurrencies or by adding device testing concurrency which is a nice feature, but a bit pricey. Verdict: Simplistic but powerful tool from Microsoft, with a focus on parallel device testing, push notifications for audience segments, and a ton of useful analytics and crash reports.

      Fastlane

      fastlane page Fastlane is a bit different from the tools we've seen so far, in a sense that it is open source under the MIT license and it's a Google project which means Google Privacy Policy and Terms of Service apply to it. You can freely become a contributor and make this tool even better. The other difference is that it's not a hosted solution, but you need to install it on your machine (Similarly to what you do with TeamCity for example). To install Fastlane you need to have the latest version Xcode command line tools installed. The complete tutorial on how to install Fastlane is described in the documentation pages. Currently, Fastlane is only supported for MacOS, but the team is looking to expand it to Linux and Windows. What you get with Fastlane:
      • Around 170 integrations with other services
      • 100% open source project under the MIT license
      • The on-premises solution, so you know where your data is, no need to worry about data security (except your own heh)
      • Integration with all major CI tools
      • Support for iOS, Mac, and Android apps
      • Unlimited customization possibilities
      • Deployment from anywhere
      There are no pricing plans, Fastlane is completely free and open source, so you can play with it and customize it to your own liking. Verdict: Open-source tool under MIT license, a bit different and maybe not so rich in features like the other tools from this list, but the open-source nature leaves a lot of room for customization. Since it's an on-premises tool, you know where your data is and that might be important to some of you or your clients.

      Bitrise.io

      bitrise front page Another great mobile ci tool, Bitrise.io made by the company of the same name. Bitrise.io offers a few different ways of configuring your mobile applications. You can use a workflow editor to create your build process, customize it to your will with the usage of .yml files or by using the Bitrise CLI. Bitrise CLI is an open source project and you can fork it to mod it for your need or make a pull request if you think you can add a new feature to it. You can get a quick look at the state of app development in 2017 on the Bitrise.io blog. By using Bitrise.io you get:
      • A lot of different integrations
      • Support for: iOS, Android, Cordova, Xamarin, MacOS, React Native and Ionic
      • Three different ways to configure and build your applications
      • Excellent documentation and blog pages
      • A community engaging environment by using the Open Source projects
      • Engineering support
      • Less coffee/month as a service (there is a lot more of those)
      Bitrise.io offers three pricing plans: Hobby, Team, and Organization (Standard and Elite), so they are pretty flexible with their plans. In addition to 14-day trial period, the Hobby plan is free but very limiting. Team and Organization plans are much more serious and include unlimited members/builds per month, but are priced by concurrent builds. Verdict: Another great tool, focusing on integration flexibility, community engagement and offers several different ways to configure your projects

      Buddybuild

      buddybuild page The last tool on our list is buddybuild. We've saved it for the last because it doesn't seem to stand out in a crowd in any way. Furthermore, the UI of the home page is not clear enough on how to get started with the app or what the pricing plans are. We've tried logging in with both GitHub and BitBucket and we got the following page: buddybuild sign-in fail An ambiguous message, that doesn't tell you what to do next. Trying to find the solution in the documentation pages didn't help us either. Trying to contact the support hasn't gone any better, we've got a lot of ambiguous answers, that haven't helped us understand what is happening with the service or how to use it. Upon further investigation, we've learned that buddybuild has been bought by Apple and that it's not open to new customers or Android projects. We'll leave the tool on this list since we don't have any more information for now. Verdict: We couldn't test buddybuild properly or learn more about what's going to happen with it. The overall experience with the tool left us with a bad taste in our mouth. We hope that sometime in the near future, the buddybuild team will know more/say more about the service status. You don't have a choice but to avoid this tool for now.

      Still Not Sure If You Should Use a Mobile CI Tool?

      CI tools have come a long way recently and there isn't much overhead if you want to start using a CI tool. On average, setup phase only takes a few minutes, and it's a well-known fact that earlier in the lifecycle you catch the bug, the less of the cost you'll have later. cost of bug fixing As you can see it's pretty straightforward. The earlier you catch the bug, the less damage it makes. So are you willing to make the leap of faith and not use one of the CI tools for your mobile app?

      Conclusion

      We've gone through a few of the mobile continuous integration tools available on the market. The awareness about these tools is still not up to the desired level so we hope this article will help you understand the value and the importance of using a CI tool for your next mobile project. Is there a mobile-oriented CI tool you prefer and want to see this list? Do let us know. [sc name="subscribe" formNumber="3820" contentType="CI/CD"]]]>
      2719 0 0 0 361 http://www.alexcurylo.com/2018/01/18/ci-cd-looking-for-a-new-buddy/ 0 0 798 https://brandwebdirect.com/website-design-portfolio-samples/website-design-for-ecommerce-online-sales-web-development-service 0 0 799 http://www.brandwebdirect.com/website-design-services 0 0 1027 https://www.brandwebdirect.com/website-design-portfolio-samples/web-design-portfolio-sample 0 0
      Continuous Integration Tools for Mobile vs Web. What's the Difference? https://code-maze.com/ci-tools-for-mobile-apps/ Tue, 15 May 2018 06:30:42 +0000 https://code-maze.com/?p=2818 Go mobile or go home!

      Mobile apps have taken center stage in the world of software. With seemingly unstoppable growth, the winner is the one who can deliver healthy code to the end-user faster. This merciless race for the five-star rating has created a race for the greatest mobile DevOps tools and methods that can help you stay in the competition and deliver high-quality code faster. People often underestimate the complexity of the mobile ecosystem. Mobile brings with it a whole set of unique challenges that make implementing CI for it difficult. Continuous integration as a practice is nothing new to developers building web apps; however, when you’re developing mobile apps, you’ll learn that CI/CD tailored specifically for mobile has a lot to offer to the process. While the CI concept remains the same, its practicalities are somewhat different. So, let's dig into what makes building mobile apps so different from web apps? Secondly, why you really need to implement a continuous integration tool tailored for mobile to build kick-ass applications... FAST!

      What is Continuous Integration (CI)?

      In short, continuous integration (CI) is a software development practice of integrating code changes daily, if not multiple times a day, into a shared code repository. Each integration is verified by an automated build to detect errors and get to the root of the problem as soon as possible. Check out all the reasons why you need continuous integration and delivery in your software development process.

      CI for Mobile Apps vs Web Apps

      The foundational building blocks and the workflow of the continuous integration process for mobile mirror those of web app development: you write code, commit, push, then watch the CI service fetch your repo, install dependencies, build your app and test it. And this is where the challenges pop in.

      Continuous integration Tools for Mobile

      Testing Mobile Apps vs Web Apps

      It’s never too early to begin testing an application. If the end-user perceives bad performance from your app, their next click will likely be on your competitors. While all types of performance, QA, and functional tests as well as non-functional tests, such as UX testing, are the same for mobile and web, running functional tests on a mobile application requires the use of an emulator/simulator or a physical device. Simulators/emulators: Virtual environments can save you lots of time and money by expanding your testing coverage to multiple devices, so you can be more confident to deliver code that works on (almost) all the devices. Real devices: The vast variety of mobile devices, all with their own specifics, can outperform the emulator/simulator in terms of bug detection, which is why you’ll need to test on physical handsets to reveal device-specific bugs. With a cloud-based device lab like Firebase Test Lab for Android or Amazon Web Services (AWS) Device Farm, you can run tests and interact with a large selection of physical devices without actually having to invest in their ownership or setup. The next step for a web app would be to deploy the application to a hosting service, making the new version of the web application immediately available to the public. And again, this is not how things work for mobile apps!

      Distributing Mobile Apps vs Web Apps

      The difference at the deployment stage comes down to the distribution model. In many ways, mobile apps take us back to the days of shipping shrink-wrapped CDs for each new version of our software. A single copy of your web application is the only thing needed to make it available to the entire world. By contrast, hundreds, thousands and (hopefully) millions of copies of your mobile app will need to be delivered to users’ phones. And while you have full control of the deployment pipeline for your web app, someone else is in charge of the mobile distribution channels and you have to be ready to play by their rules. To begin with, all mobile platforms have a set of guidelines to which all apps must adhere to be accepted into the official stores. For example, you can take a look at Google’s. Apple goes a step further and requires your app’s code to be signed. For this purpose, you will need to obtain a digital certificate and a provisioning profile from Apple. Once your app meets all the necessary requirements of one or multiple mobile platforms, the next step is to get it into Apple’s and Google’s walled gardens. While using a mobile CI service, such as Nevercode, cannot shortcut this, it can make things significantly less painful by automating the key steps. Once a build completes successfully, mobile-focused CI service can automatically publish your build artifacts — i.e. distribution packages for Android and/or iOS — to the relevant channels. For instance, you can set up the CI tool to send your successful release builds directly to iTunes Connect —- Apple’s pipeline for submitting apps to the App Store.

      Beta Testing

      In addition to the release channels described above, you may wish to publish your builds elsewhere. Most web developers are familiar with the concept of environments — “dev” for development work, “staging” for testing by a small group of users or a client, and “master” for wide distribution to the public. The same concepts apply to mobile app development. It’s only the “master” branch of your project that you’ll want to submit to Apple or Google. Before that, you’ll want to have the beta version of the app evaluated by your teammates or — if you have them — dedicated beta-testers. The simplest by far is to configure the CI tool to distribute the app via email to the addresses you provide. While this is very straight-forward, you can get a lot more out of your beta testers if you connect with them via a third party, such as HockeyApp, Crashlytics, or TestFairy. These services will not only help you with distribution but can also monitor crashes, provide detailed reports, and collect user feedback. Here, too, many CI tools for mobile, have your back with optional automatic publishing to all three.

      Supporting Legacy Code

      Once again, it feels like we’re going back in time. “Fragmentation” was not a concept that applied to web applications, but mobile brings it roaring back. Just because an awesome new version of your app is available, there is no reason to expect your entire user base to install it ASAP. (But you won’t have to wait too long before seeing their 1-star reviews pour in!) Having to do upgrade testing and supporting multiple versions of your software simultaneously is not an easy task no matter how you slice it. Luckily, a good mobile-oriented CI service can help here by keeping a reliable version record of the binaries it builds. You can then use these as a reference when handling user issues. Do you still think Jenkins, will do the job? Check, the maintenance side of Jenkins.

      Wrap-Up

      Here’s a quick summary of the key differences between web and mobile CI you should be aware of before getting started:
      CI for web CI for mobile
      Testing Functional tests do not require extra action. Functional tests require the use of an emulator/simulator or a physical device.
      Deployment Deploy to live instantly, no code signing Submit to store for approval, may need uploading of certificates to CI.
      Beta Testing Deploy to staging and invite users. Distribute build via email or 3rd party.
      Legacy Version Support N/A: newest version instantly available for all. Use CI to keep track of binaries versions for reference.
        The only right way to compete in today’s mobile-first world is to adopt the best tools for mobile app development. You will undoubtedly discover that a continuous integration service tailored for mobile development is a needed tool. The real power of CI for mobile application lies in automating the mundane and error-prone tasks, thereby letting you spend more time doing what you love — writing the code. As with any tool, a proper understanding of how CI works within a particular setup is critical for getting the most out of it without undue effort and frustration. What's your opinion about and experience with continuous integration for mobile apps? Share your thoughts and questions in the comments. [sc name="subscribe" formNumber="3820" contentType="CI/CD"]]]>
      2818 0 0 0 174 http://ukr-news.ml/2018/05/%d0%b4%d0%b0%d0%b9%d0%b4%d0%b6%d0%b5%d1%81%d1%82-%d0%b8%d0%bd%d1%82%d0%b5%d1%80%d0%b5%d1%81%d0%bd%d1%8b%d1%85-%d0%bc%d0%b0%d1%82%d0%b5%d1%80%d0%b8%d0%b0%d0%bb%d0%be%d0%b2-%d 0 0 159 https://www.alvinashcraft.com/2018/05/15/dew-drop-may-15-2018-2725/ 0 0 199 http://119.3.12.182/wordpress/?p=88115 0 0
      React - Navigation and React Routing https://code-maze.com/react-dot-net-core-navigation-routing/ Wed, 16 May 2018 06:36:20 +0000 https://code-maze.com/?p=2971
    • Preparing the Project and Creating Components
    • Navigation and Routing (Current article)
    • HTTP, Axios, and Redux
    • React Lazy Loading and HOC Component
    • Error Handling and Additional Components
    • Dynamic Form Creation and Modal Components
    • Form Validation and Sending the POST Requests
    • React PUT Requests
    • React DELETE Requests
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see all the basic instructions and complete navigation for the .NET Core series, check out the following link: Introduction to the .NET Core series. For the complete navigation and all the basic instructions of the React series, check out: Introduction to the React series. For the previous part check out: Preparing the Project and Creating Components The source code is available at GitHub: React series - react-series-part2-end branch This post is divided into several sections:

      React Routing Configuration, BrowserRouter, and Route

      To begin with the React Routing, we need to install React Router by executing this command in the terminal window:
      npm install --save react-router-dom
      React router installation React Routing After the installation completes, let’s modify the App.js file to support React routing:
      import React, { Component } from 'react';
      import './App.css';
      import Layout from '../components/Layout/Layout';
      import Home from '../components/Home/Home';
      import { BrowserRouter, Switch, Route } from 'react-router-dom';
      
      class App extends Component {
        render() {
          return (
            <BrowserRouter>
              <Layout>
                <Switch>
                  <Route path="/" exact component={Home} />
                </Switch>
              </Layout>
            </BrowserRouter>
          );
        }
      }
      
      export default App;
      
      For routing to work, the central component of our application must be inside the BrowserRouter component. Our central component is the Layout component and we are wrapping it with the BroswerRoutercomponent. You could do this in another way inside the index.js file by wrapping the <App/> component inside the ReactDOM.render() function. Either way you choose produces the same result. The Switch component renders the first child that matches the location. Once it finds the matching route it stops searching for another. Finally, the Route component is the one that is going to redirect a user from one component to another. For now, we only have the Home component and it is going to be served on the “/” path. If we navigate to localhost:3000 we are going to see the same result as before but with the difference of using routing to provide our components. Now with the routing set in place, let’s create a navigation menu. Inside the components folder, create a new folder and name it Navigation. Inside create the two files: Navigation.js and Navigation.css: Navigation structre React Routing We are going to use the Bootstrap navigation element to create our own navigation. Prior to the component creation let’s install another library which React needs to merge a Bootstrap navigation with a React-Router navigation:
      npm install --save react-router-bootstrap
      React-Router-Bootstrap React Routing Now, let's modify the Navigation.js file:
      import React from 'react';
      import './Navigation.css';
      import { Col, Navbar, Nav, NavItem } from 'react-bootstrap';
      import { NavLink } from 'react-router-dom';
      import { LinkContainer } from 'react-router-bootstrap';
      
      const navigation = (props) => {
          return (
              <Col md={12} >
                  <Navbar inverse collapseOnSelect>
                      <Navbar.Header>
                          <Navbar.Brand>
                              <NavLink to={'/'} exact >Account-Owner</NavLink>
                          </Navbar.Brand>
                          <Navbar.Toggle />
                      </Navbar.Header>
                      <Navbar.Collapse>
                          <Nav>
                              <LinkContainer to={'/owner-list'} exact>
                                  <NavItem eventKey={1}>
                                      Owner Actions
                                  </NavItem>
                              </LinkContainer>
                              <LinkContainer to={'/account-list'}>
                                  <NavItem eventKey={2}>
                                      Account Actions
                                  </NavItem>
                              </LinkContainer>
                          </Nav>
                      </Navbar.Collapse>
                  </Navbar>
              </Col>
          )
      }
      
      export default navigation;
      

      Navigation Menu Code Explanation

      As you might have noticed from the import statement the Navbar, the Nav and the NavItemcomponents are the Bootstrap components that exist to help us create the visual part of our navigation component. But for the routing to work we need to use the NavLink component, which is the react-router-dom's component.  With the NavLink component we can navigate to exact path and moreover, it implements the styling to the active links inside the navigation menu. Now, we need to pay attention to the NavItem component. This is Bootstrap’s component to create a single navigation item. And as we already know we need to use the NavLink and not the NavItem component for the routing to work. Replacing the NavItem with the NavLink component won't do the trick because the navigation's visual part would be scrambled. So the solution is to use the LinkContainer component from the react-router-bootstrap library, which we have already installed. This component simulates the NavLink component completely, therefore enabling us to use this navigation without any problems. As a continuation, we need to modify the Navigation.css file first:
      div a.active, ul li.active{
          font-weight: bold!important;
          font-style: italic!important;
          color: #fff!important;
      }
      
      And to include this Navigation component into the Layout component:
      import React from 'react';
      import { Grid, Row } from 'react-bootstrap';
      import Navigation from '../Navigation/Navigation';
      
      const layout = (props) => {
          return (
              <Grid>
                  <Row>
                      <Navigation/>
                  </Row>
                  <main>
                      {props.children}
                  </main>
              </Grid>
          )
      }
      
      export default layout;
      
      As a result, we are able to see our navigation menu: Navigation created - React Routing

      Creating the Not-Found Component

      We are going to add one more feature to complete this part of the post. Whenever a user types a nonexisting URL address the app is going to redirect him or her to the not-found (404) component. First, let’s create a new folder inside the components folder and name it ErrorPages. Inside create a new folder with a name NotFound. There, create two new files NotFound.js and NotFound.css: Not-Found structure - React Routing Now, let's modify the NotFound.js file:
      import React from 'react';
      import './NotFound.css'
      
      const notFound = (props) => {
          return (
              <p className={'notFound'}>
                  "404 SORRY COULDN'T FIND IT!!!"
              </p>
          )
      }
       
      export default notFound;
      
      We've created a functional component which returns some JSX code inside the return() block. The JSX is a syntax extension to JavaScript. It is recommended to use it with React to describe what a UI should look like. Even though it might remind you of a template language, it utilizes the full power of JavaScript. Then, we need to modify the NotFound.css file:
      .notFound{
          font-weight: bold;
          font-size: 50px;
          text-align: center;
          color: #f10b0b;
      }
      
      Finally, let's modify the App.js file:
      import NotFound from '../components/ErrorPages/NotFound/NotFound';
      <BrowserRouter>
          <Layout>
            <Switch>
              <Route path="/" exact component={Home} />
              <Route path="*" component={NotFound} />
            </Switch>
          </Layout>
      </BrowserRouter>
      
      With this modification in place whenever a user types an URL address that doesn’t exist, our application is going to render the NotFound component (even if we type localhost:3000/404 :D ) Not-Found Page - React Routing

      Conclusion

      Excellent. We have completed successfully the navigation part of the series. By reading this post you've learned:
      • How to setup React-Routing
      • The way to create the Navigation component
      • How to use the React-Router-Bootstrap library to merge Bootstrap and the React-Router navigation
      • The way to create a 404 component
      Thank you for reading the article and I hope you found something useful in it. In the next part of the series, we are going to learn how to prepare HTTP repository by using Axios and Redux. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      2971 0 0 0 160 0 0 161 160 0 162 161 0 163 162 0 855 0 0 856 855 0 901 0 0 902 901 0 903 902 0
      React - How to use Redux in React Applicatoin https://code-maze.com/react-net-core-http-axios-redux/ Wed, 23 May 2018 06:30:16 +0000 https://code-maze.com/?p=3036 requests from every component but we are going to do that by centralizing the HTTP logic as some kind of a repository. For this type and size of the project, the centralized solution fits perfectly fine. But if you have a larger and more complicated project it would be a good practice to split repository files, and thus, splitting the states. We are going to use axios as a third party library to send HTTP requests and Redux for centralizing the repository logic. There is one important thing to note. We don’t have to use Redux in React to create a central place for handling HTTP requests (this is just one way), we could create an additional file and export functions (that are going to handle HTTP requests) from that file. But Redux is quite common in the React projects, therefore we are going to explain in detail how Redux works. So let’s start. [sc name="part_of_series" headline="This article is part of the series"] If you want to see all the basic instructions and complete navigation for the .NET Core series, check out the following link: Introduction to the .NET Core series. For the complete navigation and all the basic instructions of the React series, check out: Introduction to the React series. For the previous part check out: Navigation and Routing The source code is available at GitHub: React series - react-series-part3-end branch This post is divided into several sections:

      Creating the Axios Instance

      To install axios execute this command:
      npm install --save axios
      
      Axios install - HTTP, Axios, Redux Even though we could use native axios instance (the one we have just installed) to send HTTP requests, the better way is to create our own instance. In this custom instance, we can define base url property and assign headers and other useful elements. If we need more instances of axios we can create them as well. So let’s create our custom axios instance. Inside the src folder, create a new folder and name it axios. Inside that folder create a new file and name it axios.js. Now let's modify the axios.js file:
      import axios from 'axios';
      
      const instance = axios.create({
          baseURL: 'http://localhost:5000',
          headers: {
              headerType: 'example header type'
          }
      });
      
      export default instance;
      
      In this code example, we import the axios library and then create a new instance with the additional properties. This is very useful because we don’t have to write full endpoints anymore (axios.get(http://locahost:5000/api/owner)) for our HTTP requests. Now we can use relative paths (axios.get(/api/owner)) because we are using our custom axios instance with the predefined baseURL property.

      About Redux in React App

      Redux is a state container for the JavaScript applications. Even though it seems a bit complicated at the beginning, with a bit of practice, you are going to realize that Redux is not that hard at all. And it helps a lot. Redux is not React’s library, it can fit with any other JavaScript framework, but it works well with React. To install Redux execute this command:
      npm install --save redux
      Redux installation - HTTP, Axios, Redux The Redux on its own is not going to be enough. We need to create a relation between React and Redux and to accomplish that let’s install the react-redux library:
      npm install --save react-redux
      React-Redux installation - HTTP, Axios, Redux Excellent. Now we have both libraries required for our application to work with Redux. So, we can continue to the Redux implementation. But before we continue, let's take a look at this diagram which closely explains how Redux works (Components part is a starting point): Redux diagram - Redux in React

      Redux - Action Types

      Let’s start by creating the following structure inside the src folder: Redux folder structure In the actions folder, let's create a new file and name it actionTypes.js. Now let's modify that file:
      export const GET_DATA_SUCCESS = 'GET_DATA_SUCCESS';
      export const POST_DATA_SUCCESS = 'POST_DATA_SUCCESS';
      export const PUT_DATA_SUCCESS = 'PUT_DATA_SUCCESS';
      export const DELETE_DATA_SUCCESS = 'DELETE_DATA_SUCCESS';
      
      Our reducer file (which we are going to create later in this post) is going to use this action types to switch between different ways of updating the state.

      Redux - Repository Actions (Action Container)

      We need to create a new file in the actions folder and name it repositoryActions.js. We are going to handle HTTP async requests inside this file and return an object which reducer file is going to use to update the state. Every returned object must have at least one property with a name “type”. The value of the type property is going to be one of the actionTypes from the actionType.js file (previously created). Now let's add the import statements for the actionTypes and for our axios instance in the repositoryActions.js file:
      import * as actionTypes from './actionTypes';
      import axios from '../../axios/aixos';
      
      Then we need to add two functions. One to handle the GET request and the second one to return an object with the type property and the data from the server:
      const getDataSuccess = (data) => {
          return {
              type: actionTypes.GET_DATA_SUCCESS,
              data: data
          }
      }
      
      export const getData = (url, props) => {
          return (dispatch) => {
              axios.get(url)
              .then(response => {
                  dispatch(getDataSuccess(response.data));
              })
              .catch(error => {
                  //TODO: handle the error when implemented
              })
          }
      }
      
      What are we doing in here? We are exporting thegetData function. This function will be called from our component to fetch the data from the server (therefore we must export it from this file). Then with axios, we are sending the GET request. If it is successful we are dispatching the getDataSuccess function which returns an object for the reducer file to use. This object has the mandatory type property and also the data property fetched from the server. Below the getData function, let's implement all the other functions, by following the same pattern:
      const postDataSuccess = (response) => {
          return {
              type: actionTypes.POST_DATA_SUCCESS,
              response: response
          }
      }
      
      export const postData = (url, obj, props) => {
          return (dispatch) => {
              axios.post(url, obj)
              .then(response => {
                  dispatch(postDataSuccess(response));
              })
              .catch(error => {
                  //TODO: handle the error when implemented
              })
          }
      }
      
      const putDataSuccess = (response) => {
          return {
              type: actionTypes.PUT_DATA_SUCCESS,
              response: response
          }
      }
      
      export const putData = (url, obj, props) => {
          return (dispatch) => {
              axios.put(url, obj)
              .then(response => {
                  dispatch(putDataSuccess(response));
              })
              .catch(error => {
                  //TODO: handle the error when implemented
              })
          }
      }
      
      const deleteDataSuccess = (response) => {
          return {
              type: actionTypes.DELETE_DATA_SUCCESS,
              response: response
          }
      }
      
      export const deleteData = (url, props) => {
          return (dispatch) => {
              axios.delete(url)
              .then(response => {
                  dispatch(deleteDataSuccess(response));
              })
              .catch(error => {
                  //TODO: handle the error when implemented
              })
          }
      }
      
      That’s it, we now have an implementation of the actions.js file and it's a time to create and implement the reducer file.

      React - Reducer

      Let's create a new file inside the reducers folder and name it repositoryReducer.js: Reducer file - HTTP, Axios, Redux In this file, we are going to check the type property which we return from the repositoryAction.js file. Then, based on the value of the type property, we are going to update our state. So, let's modify the repositoryAction.js file:
      import * as actionTypes from '../actions/actionTypes';
      
      const initialState = {
          data: null,
          showSuccessModal: false
      }
      
      We import the actionTypes and create the state with the name initialState. The data property is going to store the data from the server and the showSuccessModal property serves to show or hide the success modal when a POST, PUT or DELETE action is successful. Now let’s create a reducer function below our state object:
      const reducer = (state = initialState, action) => {
          switch (action.type) {
              case actionTypes.GET_DATA_SUCCESS:
                  return executeGetDataSuccess(state, action);
              case actionTypes.POST_DATA_SUCCESS:
                  return executePostDataSuccess(state, action);
              case actionTypes.PUT_DATA_SUCCESS:
                  return executePutDataSuccess(state, action);
              case actionTypes.DELETE_DATA_SUCCESS:
                  return executeDeleteDataSuccess(state, action);
              default:
                  return state;
          }
      }
      
      export default reducer;
      
      This reducer function is accepting two parameters, the state which we initialize with our initial state and the action. We are going to use this state parameter to update our initialState and the action parameter to store the object (with at least type property) sent from therepositoryAction.js file. So, whenever we dispatch any action (which returns an object with at least type property and all the other properties) from the repositoryAction.js file, this reducer function is going to trigger and to accept the sent object inside the action parameter. As a consequence, the reducer is going to switch through the action types and to execute the corresponding function. Finally, let’s add those corresponding functions right above our reducer function:
      const executeGetDataSuccess = (state, action) => {
          return {
              ...state,
              data: action.data
          }
      }
      
      const executePostDataSuccess = (state, action) => {
          return {
              ...state,
              showSuccessModal: true
          }
      }
      
      const executePutDataSuccess = (state, action) => {
          return {
              ...state,
              showSuccessModal: true
          }
      }
      
      const executeDeleteDataSuccess = (state, action) => {
          return {
              ...state,
              showSuccessModal: true
          }
      }
      
      These functions are updating our state. First, we are deeply cloning our state object by using the spread (…) operator and then just overriding the property we want to update in our state object. Because objects and arrays are reference types we need to execute a deep clone on them prior to any changes. That way we are updating the state immutably. For this state to be available inside any owner component we need to register this reducer to the index.js file.

      Reducer File Registration

      Before we register our reducer file we must install one more third-party library named thunk:
      npm install --save redux-thunk
      Thunk installation - Redux React This library enables us to send async requests with the Redux actions. Now we can register our reducer:
      import React from 'react';
      import ReactDOM from 'react-dom';
      import './index.css';
      import App from './containers/App';
      import registerServiceWorker from './registerServiceWorker';
      import 'bootstrap/dist/css/bootstrap.css';
      import 'bootstrap/dist/css/bootstrap-theme.css';
      import repositoryReducer from './store/reducers/repositoryReducer';
      import { Provider } from 'react-redux';
      import { createStore, applyMiddleware } from 'redux';
      import thunk from 'redux-thunk';
      
      const store = createStore(repositoryReducer, applyMiddleware(thunk));
      
      ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
      registerServiceWorker();
      
      In the code above, we import all the necessary files we need to register our reducer. Then we create the store and apply the middleware thunk and finally use the Provider component to provide our reducer to the React app. There it is, we have prepared our redux setup for our project. The best part about it is that we can use it for any component inside our project and if we have components which require more complex or different kind of redux setup, all we have to do is to create another action and reducer file and to register it inside the index.js file. In one of the next posts, we are going to show you how to accomplish that by combining reducers inside theindex.js file.

      Conclusion

      Even though you may find a little bit hard to understand how Redux works, we believe that with this article and some practice you'll get a handle on it. Bottom line is that Redux is not that hard, just as the opposite, it is pretty straightforward, after some practice. By reading this post, you've learned:
      • The way to set up the new Axios instance
      • How to install Redux and React-Redux
      • About Action Types, Action Containers and Reducers
      • How to register reducer file
      Thank you for reading the article and I hope you found something useful in it. In the next part of the series, we are going to learn how to prepare the HTTP repository by using Axios and Redux. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      3036 0 0 0 172 https://www.alvinashcraft.com/2018/05/23/dew-drop-may-23-2018-2731/ 0 0 942 0 0 944 942 0 1539 { return { getUserRoles: (url, props) => dispatch(repositoryActions.getData(url, props)), getWorkRoles: (url, props) => dispatch(repositoryActions.getData(url, props)) }; }; than mapstatetoprops : const mapStateToProps = state => { return { data: state.repository.data }; }; I got the data displayed in dropdown but the last fetched data overwrites the first from first api call. It is obvious why that is happening because I have only one property data in mapstatetoprops and in reducerrepository. How can I without making more logic in redux and adding one fetch for userroles and other one for workroles with this "generic" let's say method getData() get both api data and render them each in separate dropdownlists? So one getData() from redux, 2 api calls in component for 2 dropdownlists. I have tried create state and save every call data but I can't because data is comming from mapstatetoprops and there I only have one data from repository? I hope you understand what I am trying to achieve.]]> 0 0 1540 1539 0 1895 0 0 1896 1895 0
      ASP.NET Core Web API Best Practices https://code-maze.com/aspnetcore-webapi-best-practices/ Mon, 11 Jun 2018 06:00:17 +0000 https://code-maze.com/?p=3106
    • .NET Core Tutorial
    • Angular Development Best Practices
    • Top REST API Best Practices
    • Enabling CORS in ASP.NET Core Web API
    • ASP.NET Core Web API with EF Core Code-First Approach
    • [sc name="part_of_series" headline="Recommended Articles"] So, what should we pay attention to? In this post, we are going to write about what we consider to be the best practices while developing the .NET Core Web API project. How we can make it better and how to make it more maintainable. We are going to go through the following sections:

      Startup Class and the Service Configuration

      In the Startup class, there are two methods: the ConfigureServices method for registering the services and the Configure method for adding the middleware components to the application’s pipeline. So, the best practice is to keep the ConfigureServices method clean and readable as much as possible. Of course, we need to write the code inside that method to register the services, but we can do that in a more readable and maintainable way by using the Extension methods. For example, let's look at the wrong way to register CORS:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddCors(options =>
          {
              options.AddPolicy("CorsPolicy",
                  builder => builder.AllowAnyOrigin()
                  .AllowAnyMethod()
                  .AllowAnyHeader());
          });
      }        
      
      Even though this way will work just fine, and will register CORS without any problem, imagine the size of this method after registering dozens of services. That’s not readable at all. The better way is to create an extension class with the static method:
      public static class ServiceExtensions
      {
          public static void ConfigureCors(this IServiceCollection services)
          {
              services.AddCors(options =>
              {
                  options.AddPolicy("CorsPolicy",
                      builder => builder.AllowAnyOrigin()
                      .AllowAnyMethod()
                      .AllowAnyHeader());
              });
          }
      }
      
      And then just to call this extended method upon the IServiceCollection type:
      public void ConfigureServices(IServiceCollection services)
      {
          services.ConfigureCors();
      }
      
      To learn more about the .NET Core’s project configuration check out: .NET Core Project Configuration.

      Project Organization

      We should always try to split our application into smaller projects. That way we are getting the best project organization and separation of concerns (SoC). The business logic related to our entities, contracts, accessing the database, logging messages or sending an email message should always be in a separate .NET Core Class Library project. Every small project inside our application should contain a number of folders to organize the business logic. Here is just one simple example of how a completed project should look like: Project-Structure - Best Practices .NET Core

      Environment Based Settings

      While we develop our application, that application is in the development environment. But as soon as we publish our application it is going to be in the production environment. Therefore having a separate configuration for each environment is always a good practice. In .NET Core, this is very easy to accomplish. As soon as we create the project, we are going to get the appsettings.json file and when we expand it we are going to see the appsetings.Development.json file: Appsettings-development - Best Practices All the settings inside this file are going to be used for the development environment. We should add another file appsettings.Production.json, to use it in a production environment: Appsettings - production - Best Practices The production file is going to be placed right beneath the development one. With this setup in place, we can store different settings in the different appsettings files, and depending on the environment our application is on, .NET Core will serve us the right settings. For more information about this topic, check out Multiple Environments in ASP.NET Core.

      Data Access Layer

      In many examples and different tutorials, we may see the DAL implemented inside the main project and instantiated in every controller. This is something we shouldn’t do. When we work with DAL we should always create it as a separate service. This is very important in the .NET Core project because when we have DAL as a separate service we can register it inside the IOC (Inversion of Control) container. The IOC is the .NET Core’s built-in feature and by registering a DAL as a service inside the IOC we are able to use it in any controller by simple constructor injection:
      public class OwnerController: Controller
      {
          private IRepository _repository;
      
          public OwnerController(IRepository repository)
          {
              _repository = repository;
          }
      }
      
      The repository logic should always be based on interfaces and if you want, making it generic will allow you reusability as well. Check out this post: .Net Core series – Part 4 to see how we implement the Generic Repository Pattern inside the .NET Core’s project.

      Controllers

      The controllers should always be as clean as possible. We shouldn't place any business logic inside it. So, our controllers should be responsible for accepting the service instances through the constructor injection and for organizing HTTP action methods (GET, POST, PUT, DELETE, PATCH…):
      public class OwnerController: Controller
      {
          private ILoggerManager _logger;
          private IRepository _repository;
      
          public OwnerController(ILoggerManager logger, IRepository repository)
          {
              _logger = logger;
              _repository = repository;
          }
      
          [HttpGet]
          public IActionResult GetAllOwners()
          {            
          }
      
          [HttpGet("{id}", Name = "OwnerById")]
          public IActionResult GetOwnerById(Guid id)
          {           
          }
      
          [HttpGet("{id}/account")]
          public IActionResult GetOwnerWithDetails(Guid id)
          {
          }
      
          [HttpPost]
          public IActionResult CreateOwner([FromBody]OwnerForCreationDto owner)
          {        
          }
      
          [HttpPut("{id}")]
          public IActionResult UpdateOwner(Guid id, [FromBody]OwnerForUpdateDto owner)
          {          
          }
      
          [HttpDelete("{id}")]
          public IActionResult DeleteOwner(Guid id)
          {         
          }
      }
      

      Actions

      Our actions should always be clean and simple. Their responsibilities include handling HTTP requests, validating models, catching errors and returning responses:
      [HttpPost]
      public IActionResult CreateOwner([FromBody]OwnerForCreationDto owner)
      {
          try
          {
              if (owner == null)
              {
                  return BadRequest("Owner object is null");
              }
      
              if (!ModelState.IsValid)
              {
                  return BadRequest("Invalid model object");
              }
      
              //additional code
              
              return CreatedAtRoute("OwnerById", new { id = createdOwner.Id }, createdOwner);
          }
          catch (Exception ex)
          {
              _logger.LogError($"Something went wrong inside the CreateOwner action: {ex}");
              return StatusCode(500, "Internal server error");
          }
      }
      
      Our actions should have IActionResult as a return type in most of the cases (sometimes we want to return a specific type or a JsonResult...). That way we can use all the methods inside .NET Core which returns results and the status codes as well. The most used methods are:
      • OK => returns the 200 status code
      • NotFound => returns the 404 status code
      • BadRequest => returns the 400 status code
      • NoContent => returns the 204 status code
      • Created, CreatedAtRoute, CreatedAtAction => returns the 201 status code
      • Unauthorized => returns the 401 status code
      • Forbid => returns the 403 status code
      • StatusCode => returns the status code we provide as input

      Handling Errors Globally

      In the example above, our action has its own try-catch block. This is very important because we need to handle all the errors (that in another way would be unhandled) in our action method. Many developers are using try-catch blocks in their actions and there is absolutely nothing wrong with that approach. But, we want our actions to be clean and simple, therefore, removing try-catch blocks from our actions and placing them in one centralized place would be an even better approach. .NET Core gives us an opportunity to implement exception handling globally with a little effort by using built-in and ready to use middleware. All we have to do is to add that middleware in the Startup class by modifying the Configure method:
       public void Configure(IApplicationBuilder app, IHostingEnvironment env)
       {
           app.UseExceptionHandler(config =>
           {
               config.Run(async context =>
               {
                   context.Response.StatusCode = 500;
                   context.Response.ContentType = "application/json";
      
                   var error = context.Features.Get<IExceptionHandlerFeature>();
                   if (error != null)
                   {
                       var ex = error.Error;
      
                       await context.Response.WriteAsync(new ErrorModel()
                       {
                           StatusCode = 500,
                           ErrorMessage = ex.Message 
                       }.ToString()); //ToString() is overridden to Serialize object
                   }
               });
           });
      
           app.UseMvc();
      }
      We can even write our own custom error handlers by creating custom middleware:
      public class CustomExceptionMiddleware
      {
          //constructor and service injection
       
          public async Task Invoke(HttpContext httpContext)
          {
              try
              {
                  await _next(httpContext);
              }
              catch (Exception ex)
              {
                  _logger.LogError("Unhandled exception ...", ex);
                  await HandleExceptionAsync(httpContext, ex);
              }
          }
          
          //additional methods
      }
      After that we need to register it and add it to applications pipeline:
      public static IApplicationBuilder UseCustomExceptionMiddleware(this IApplicationBuilder builder)
      {
          return builder.UseMiddleware<CustomExceptionMiddleware>();
      }
      app.UseCustomExceptionMiddleware();
      To read in more detail about this topic, visit Global Error Handling in ASP.NET Core Web API.

      Using ActionFilters to Remove Duplicated Code

      Filters in ASP.NET Core allows us to run some code prior to or after the specific stage in a request pipeline. Therefore, we can use them to execute validation actions that we need to repeat in our action methods. When we handle a PUT or POST request in our action methods, we need to validate our model object as we did in the Actions part of this article. As a result, that would cause the repetition of our validation code, and we want to avoid that (Basically we want to avoid any code repetition as much as we can). We can do that by using ActionFilters. Instead of validation code in our action:
      if (!ModelState.IsValid)
      {
        // bad request and logging logic
      }
      We can create our filter:
      public class ModelValidationAttribute : ActionFilterAttribute
      {
          public override void OnActionExecuting(ActionExecutingContext context)
          {
              if (!context.ModelState.IsValid)
              {
                  context.Result = new BadRequestObjectResult(context.ModelState); // returns 400 with error
              }
          }
      }
      And register it in the Startup class in the ConfigureServices method:
      services.AddScoped<ModelValidationAttribute>();
      Now, we can use that filter with our action methods. To read in more detail about using Action Filters, visit our post: Action Filters in .NET Core.

      Using DTOs to Return Results and to Accept Inputs

      Even though we can use the same model class to return results or accept parameters from the client, that is not a good practice. A much better practice is to separate entities that communicate with the database from the entities that communicate with the client. Yes, the answer is to use DTOs. The model class is a full representation of our database table and being like that, we are using it to fetch the data from the database. But once the data is fetched we should map the data to the DTO and return that result to the client. By doing so, if for some reason we have to change the database, we would have to change only the model class but not the DTO because the client may still want to have the same result. You can read more about the DTO's usage in the fifth part of the .NET Core series. We shouldn't be using DTOs only for the GET requests. We should use them for other actions as well. For example, if we have a POST or PUT action, we should use the DTOs as well. To read more about this topic, you can read the sixth part of the .NET Core series. Additionally, DTOs will prevent circular reference problems as well in our project.

      Routing

      In the .NET Core Web API projects, we should use Attribute Routing instead of Conventional Routing. That’s because Attribute Routing helps us match the route parameter names with the actual parameters inside the action methods. Another reason is the description of the route parameters. It is more readable when we see the parameter with the name “ownerId” than just “id”. We can use the [Route] attribute on top of the controller and on top of the action itself:
      [Route("api/[controller]")]
      public class OwnerController: Controller
      {
           [Route("{id}")]
           [HttpGet]
           public IActionResult GetOwnerById(Guid id)
           {
                  
           }  
      }
      
      There is another way to create routes for the controller and actions:
      [Route("api/owner")]
      public class OwnerController: Controller
      {
           [HttpGet("{id}")]
           public IActionResult GetOwnerById(Guid id)
           {
                  
           }  
      }
      
      There are different opinions which way is better, but we would always recommend the second way, and this is something we always use in our projects. When we talk about the routing we need to mention the route naming convention. We can use descriptive names for our actions, but for the routes/endpoints, we should use NOUNS and not VERBS. The few wrong examples:
      [Route("api/owner")]
      public class OwnerController : Controller
      {
          [HttpGet("getAllOwners")]
          public IActionResult GetAllOwners()
          { 
          }
      
          [HttpGet("getOwnerById/{id}"]
          public IActionResult GetOwnerById(Guid id)
          {        
          }
      }
      
      The good examples:
      [Route("api/owner")]
      public class OwnerController : Controller
      { 
          [HttpGet]
          public IActionResult GetAllOwners()
          { 
          }
      
          [HttpGet("{id}"]
          public IActionResult GetOwnerById(Guid id)
          {          
          }
      }
      
      For the more detailed explanation of the Restful practices checkout: Top REST API Best Practices.

      Logging

      If we plan to publish our application to production, we should have a logging mechanism in place. Log messages are very helpful when figuring out how our software behaves in production. .NET Core has its own logging implementation by using the ILoggerinterface. It is very easy to implement it by using Dependency Injection feature:
      public class TestController: Controller
      {
          private readonly ILogger _logger;
      
          public TestController(ILogger<TestController> logger)
          {
              _logger = logger;
          }
      }
      
      Then in our actions, we can utilize various logging levels by using the _logger object. .NET Core supports logging API that works with a variety of logging providers. Therefore, we may use different logging providers to implement our own logging logic inside our project. The NLog is the great library to use for implementing our own custom logging logic. It is extensible, supports structured logging and very easy to configure. We can log our messages in the console window, files or even database. To learn more about using this library inside the .NET Core check out: .NET Core series – Logging With NLog. The Serilog is the great library as well. It fits in with the .NET Core built-in logging system.

      CryptoHelper

      We won't talk about how we shouldn’t store the passwords in a database as a plain text and how we need to hash them due to security reasons. That's out of the scope of this article. There are various hashing algorithms all over the internet, and there are many different and great ways to hash a password. But if need the library that provides support to the .NET Core's application and that is easy to use, the CryptoHelper is quite a good library. The CryptoHelper is a standalone password hasher for .NET Core that uses a PBKDF2 implementation. The passwords are hashed using the new Data Protection stack. This library is available for installation through the NuGet and its usage is quite simple:
      using CryptoHelper;
      
      // Method for hashing the password
      public string HashPassword(string password)
      {
          return Crypto.HashPassword(password);
      }
      
      // Method to verify the password hash against the given password
      public bool VerifyPassword(string hash, string password)
      {
          return Crypto.VerifyHashedPassword(hash, password);
      }
      

      Content Negotiation

      By default .NET Core Web API returns a JSON formatted result. In most of cases, that’s all we need. But what if the consumer of our Web API wants another response format, like XML for example? For that, we need to create a server configuration to format our response in the desired way:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddMvc(config =>
          {
              // Add XML Content Negotiation
              config.RespectBrowserAcceptHeader = true;
          });
      }
      
      Sometimes the client may request a format that is not supported by our Web API and then the best practice is to respond with the status code 406 Not Acceptable. That can be configured inside our ConfigureServices method as well:
      config.ReturnHttpNotAcceptable = true;
      We can create our own custom format rules as well. The content negotiation is a pretty big topic so if you want to learn more about it, check out: Content Negotiation in .NET Core.

      Using JWT

      JSON Web Tokens (JWT) are becoming more popular by the day in web development. It is very easy to implement JWT Authentication due to the .NET Core’s built-in support. JWT is an open standard and it allows us to transmit the data between a client and a server as a JSON object in a secure way. We can configure the JWT Authentication in the ConfigureServices method:
      public void ConfigureServices(IServiceCollection services)
      {
           services.AddAuthentication(opt => {
              opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
              opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
          })
          .AddJwtBearer(options =>
             {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                   //Configuration in here
                };
             });
      }
      
      In order to use it inside the application, we need to invoke this code in the Configure method:
      app.UseAuthentication();
      app.UseAuthorization();
      We may use JWT for the Authorization part as well, by simply adding the role claims to the JWT configuration. To learn in more detail about JWT authentication and authorization in .NET Core, check out JWT with .NET Core and Angular Part 1 and Part 2 of the series.

      Conclusion

      In this article,  our main goal was to familiarize you with the best practices when developing a Web API project in .NET Core. Some of those could be used in other frameworks as well, therefore, having them in mind is always helpful. If you find that something is missing from the list, don't hesitate to add it in a comment section. Thank you for reading the article and I hope you found something useful in it. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      3106 0 0 0 206 https://www.alvinashcraft.com/2018/06/11/dew-drop-june-11-2018-2743/ 0 0 210 https://howtocode.net/2018/06/net-core-web-api-best-practices/ 0 0 211 https://chevenix.wordpress.com/2018/06/17/szumma-114-2018-24-het/ 0 0 212 0 0 213 212 0 217 212 0 219 217 0 220 0 0 221 220 0 239 0 0 240 239 0 485 0 0 496 http://meltdowninteractive.com 0 0 497 496 0 616 error CS1913: Member 'StatusCode' cannot be initialized. It is not a field or property. The same error is with ErrorMessage also.]]> 0 0 635 0 0 672 0 0 673 It is very easy to implement JWT Authentication is very easy to implement due to the .NET Core’s built-in support."]]> 0 0 677 673 0 678 0 0 694 616 0 705 635 0 727 672 0 729 678 0 786 0 0 787 786 0 1017 0 0 1018 1017 0 1291 0 0 1292 1291 0 1293 1292 0 1294 https://code-maze.com/ 1293 0 1435 0 0 1436 1435 0 1506 0 0 1606 0 0 1607 EF Core series, there you can learn more about EF Core and db context as well. Thank you one more time and all the best.]]> 1606 0 1638 0 0 1639 1638 0
      Continuous Integration with TeamCity and Docker https://code-maze.com/ci-aspnetcoreapp-docker/ Mon, 04 Jun 2018 06:00:13 +0000 https://code-maze.com/?p=3183 previous post, we've discussed why continuous integration is important, what makes a good CI tool and after that, we've set up a basic TeamCity project to build our application using Docker. But continuous integration is much more than that, so in this article, we are going to learn a bit about more advanced features of TeamCity. We are also going to write an integration test to make sure our application is communicating with the database every time we build it. [sc name="part_of_series" headline="This article is part of the series"] The starting point for this part is the docker-series-prepare-ci-end branch of the docker-series repo on our GitHub. Important note: This time around, if you haven't worked like that already, you need to fork the repo to follow along since we'll be using it in TeamCity builds. This is really important if you want to follow along with the steps of the article and get the most out of it since we will be doing incremental changes to the branch. Here's what we are going to learn this time: Let's get down to it.

      How to Add an Integration Test to the Asp.Net Core App

      First things first. We are going to add some integration tests to our application. We already have a unit test, but since we are using TeamCity now, this is a great opportunity to introduce integration tests. This is something every production-grade application should have because integration tests ensure that all of your application parts communicate properly. In our case, we are going to make sure that our app communicates with the database. For that purpose, we are going to create a new project for integration tests, and add some simple tests.

      Adding an Integration Tests Project

      Like we did with our unit tests project in part 1 of the series, we are going to add a new one for integration tests. With a little twist. So we start with navigating to the project root and adding a new xUnit project:
      dotnet new xunit -o Integration
      After that we need to add the newly created project to our solution:
      dotnet sln AccountOwnerServer.sln add .\Integration\Integration.csproj
      And like the previous time add and restore the required packages:
      cd Integration
      dotnet add package Microsoft.AspNetCore.TestHost --version 3.1.0
      dotnet restore
      As you can see instead of using the Moq library, we've added the Microsoft.AspNetCore.TestHost package because it provides a nice interface for writing integration tests in ASP.NET Core projects. We don't mock stuff in integration tests. This time around, we need to reference our main project AccountOwnerServer instead of just the Contracts project:
      dotnet add reference ..\AccountOwnerServer\AccountOwnerServer.csproj
      We need it because we have to use the real server and configuration. That's all we need to set up the project. You can quickly run the dotnet build to check if it compiles. Great! Let's proceed to the integration test. First, we should rename the UnitTests1.cs file to something more appropriate like IntegrationTests.cs. Then we can add our test:
      public class IntegrationTests
      {
      	[Fact]
      	public async Task GetAllOwners_ReturnsAListOfOwners()
      	{
      		// Arrange
      		var server = new TestServer(new WebHostBuilder()
      			.UseConfiguration(new ConfigurationBuilder()
      				.AddJsonFile("appsettings.json")
      				.Build())
      			.UseStartup<Startup>());
      		var client = server.CreateClient();
      
      		// Act
      		var response = await client.GetAsync("/api/owner");
      		response.EnsureSuccessStatusCode();
      		
      		// Assert
      		Assert.Equal(HttpStatusCode.OK, response.StatusCode);
      	}
      }
      Ok, so here we have the simplest test possible. The Microsoft.AspNetCore.TestHost library helps us create the test server and client. Then, we simply hit the /api/owner endpoint and assert if the HTTP response status was 200 OK, which it should be if we've set up our database correctly. Important note: If you get an error that states that appsettings.json or nlog.config while running the test, you can solve it by going to properties of those files and setting the Content property to Copy always or Copy if newer. Essentially your AccountServerOwner.csproj should contain:
      <ItemGroup>
      	<Content Update="appsettings.json">
      	  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      	</Content>
      	<Content Update="nlog.config">
      	  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      	</Content>
      </ItemGroup>

      Separating the Test Initialization

      Easy and simple as it is, this way of writing tests is plain wrong. Why? Because we would need to set up the test server and test client in every test. So let's make a helper class TestContext.cs, and extract the initialization logic to that class.:
      public class TestContext: IDisposable
      {
      	private TestServer _server;
      	public HttpClient Client { get; private set; }
      
      	public TestContext()
      	{
      		SetUpClient();
      	}
      
      	private void SetUpClient()
      	{
      		_server = new TestServer(new WebHostBuilder()
      			.UseConfiguration(new ConfigurationBuilder()
      				.AddJsonFile("appsettings.json")
      				.Build())
      			.UseStartup<Startup>());
      
      		Client = _server.CreateClient();
      	}
      
      	public void Dispose()
      	{
      		_server?.Dispose();
      		Client?.Dispose();
      	}
      }
      Now, this looks much better. We've implemented the logic, made the server part private, and the client part public. This makes more sense. We also implemented the IDisposable interface to make sure context is cleaned up after we've finished testing. Let's have a look how our IntegrationTests.cs class looks now:
      public class IntegrationTests
      {
      	private readonly TestContext _context;
      
      	public IntegrationTests()
      	{
      		_context = new TestContext();
      	}
      
      	[Fact]
      	public async Task GetAllOwners_ReturnsOkResponse()
      	{
      		// Act
      		var response = await _context.Client.GetAsync("/api/owner");
      		response.EnsureSuccessStatusCode();
      
      		// Assert
      		Assert.Equal(HttpStatusCode.OK, response.StatusCode);
      	}
      
      	[Fact]
      	public async Task GetAllOwners_ReturnsAListOfOwners()
      	{
      		// Act
      		var response = await _context.Client.GetAsync("/api/owner");
      		response.EnsureSuccessStatusCode();
      		var responseString = await response.Content.ReadAsStringAsync();
      		var owners = JsonConvert.DeserializeObject<List<Owner>>(responseString);
      
      		// Assert
      		Assert.NotEmpty(owners);
      	}
      }
      So, as you can see, we've initialized the context in the constructor. This helped us clean our methods (Facts) to the extent we don't even need the Arrange part in them. We've also separated our test logic into two different methods. The first one checks if the status code is 200 OK, and the second one is for testing if that particular endpoint returns the list of owners. So clean, much wow. For extensive testing, there are even better ways to do this, but for our purposes, this will do. Let's go on.

      Running Integration Tests Locally

      Now let's try out our integration tests by navigating to the Integration folder and running the dotnet test command. Both of our tests fail! integration tests failed Can you guess why? The reason is simple. We don't have a MySQL database locally, so the requests to the endpoint in the tests fail with the HTTP response 500 Internal Server Error. Up until now, we've run our application using the docker-compose up command like we've learned in part 4 of the series. And since Docker Compose creates both the containers and the network in which they reside, we need to include our integration tests into docker-compose.yml somehow. But, we don't want the integration tests to run with each build since they are much slower than unit tests. That being the case, we need to make a new Dockerfile and a new docker.compose.yml specifically for the integration tests. So let's start with that. Let's create a Dockerfile for the Integration project in the solution root and name it Dockerfile.Integration. This file will just be the modified version of our existing Dockerfile:
      FROM mcr.microsoft.com/dotnet/core/sdk:3.1
      
      WORKDIR /home/app
      
      COPY ./*.sln ./
      COPY ./*/*.csproj ./
      RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done
      
      RUN dotnet restore
      
      COPY . .
      
      WORKDIR /home/app/Integration/
      
      ENTRYPOINT ["dotnet", "test"]
      So we use mcr.microsoft.com/dotnet/core/sdk:3.1 as a base image to be able to run dotnet test, set the working directory to the Integration folder and made this image executable by setting the entry point to the dotnet test command. If any of these concepts are unfamiliar to you, go back to part 3 of the series where we go into detail on how to configure a Dockerfile. Next, we want to create a new and slightly modified docker compose file docker-compose.integration.yml in the solution root:
      version: '3.1'
      
      services:
        db:
          image: mysql:5.7
          environment:
            MYSQL_RANDOM_ROOT_PASSWORD: 1
            MYSQL_DATABASE: accountowner
            MYSQL_USER: dbuser
            MYSQL_PASSWORD: dbuserpassword
          volumes:
            - dbdata:/var/lib/mysql
            - ./_MySQL_Init_Script:/docker-entrypoint-initdb.d
          restart: always
        accountownerapp:
          depends_on:
            - db
          image: my-registry:50000/codemazeblog/accountownerapp:build-2
        integration:
          depends_on:
            - accountownerapp
          image: my-registry:50000/codemazeblog/accountownerapp:test
          build:
            context: .
            dockerfile: Dockerfile.Integration
            
      volumes:
          dbdata:
      This file is pretty similar to the docker-compose.yml we already used to build our project. We made a few important changes though. First one being the usage of the pre-built image for our ASP.NET Core app instead of building it, and the second one being the addition of the integration service which will actually be responsible for running the integration workflow defined in Dockerfile.Integration file. We've also made the integration service dependant on the accountownerapp service, to make sure the database and application are up and running before we start testing them. For now, we are going to use the fixed tag for our application image (build-2), but we'll learn how to change that to depend on the latest TeamCity build. To run the integration tests we simply need to run:
      docker-compose -f docker-compose.integration.yml up
      Since we want to run a non-default docker compose file, we can use the -f option to specify which file to run. After a while, you should see your integration service starting, and the tests should pass. tests passed Cool, ha? Both of our integration tests run locally, but let's see if that's the case with TeamCity too.

      Running Tests with TeamCity

      Let's modify our existing configuration to something more suitable for running docker tasks.

      Changing the Docker Build Step

      Before we add our Integration Build Configuration, let's change the existing Command Line step we prepared it the previous part to something more intuitive. TeamCity offers a Docker build step, which helps a lot while building Docker images. Let's select it instead of the command line and add our stuff: docker build step We've chosen the file we want to build and the image we want to produce. Now, let's add a Command-Line build step next to push our image to the local registry we created. docker push step It's a pretty simple build step that pushes the image we made in the previous step to the local registry. You can use this opportunity to change the VCS branch to your own in the Version Control Settings. (If you are not sure how to do it, refer to the previous part) Now, run the build once to check if everything works still. Thanks to the Docker caching, we should have a new image in a matter of seconds, and our registry should look like this:
      // 20191219190346
      // http://localhost:50000/v2/codemazeblog/accountowner/tags/list
      
      {
        "name": "codemazeblog/accountowner",
        "tags": [
          "build-4",
          "build-3",
          "runtime"
        ]
      }
      Awesome! Now that we got that out of the way, let's proceed to the main event.

      Adding the Integration Build Configuration

      Like with the build configuration for our docker images, we need to make a build configuration for our Integration Tests: create integration build configuration Make sure to use the forked repo branch in this build configuration too. And this time, we'll type the exact command we did while we tested our build locally, followed by the docker-compose down to dispose of our containers:
      docker-compose -f docker-compose.integration.yml up
      docker-compose down
      Now, let's boldly go where no man has gone before and run these tests. And would you believe it, our build fails and we get the two failed tests! tests error message tests failed tc And on top of that, the build is still hanging! Outrageous! :D Can you guess why this is happening? Well, it seems that volumes don't work the same way they do when we run the images locally, and our init.sql script fails to mount on MySQL image initialization (docker-entrypoint-initdb.d folder). So let's make some modifications to our command line build step.

      Fixing the MySQL Volumes Problem

      Part of the problem lies in our TeamCity agent configuration. We need to add two more volumes to make Docker daemon available inside our builds. So let's stop TeamCity for a moment again with docker-compose stop and add two more lines to docker-compose.yml in /Integration/TeamCity directory.
      teamcity-agent:
          image: codemazeblog/teamcity-agent-docker-compose:latest
          build: ./agent
          environment:
            SERVER_URL: http://teamcity:8111
          volumes:
            - teamcity-agent-conf:/data/teamcity_agent/conf
            - /opt/buildagent/work:/opt/buildagent/work
            - /opt/buildagent/temp:/opt/buildagent/temp
            - /var/run/docker.sock:/var/run/docker.sock
      After that just spin up TeamCity again with docker-compose up -d. Another part of the problem or rather an incorrect usage of commands is the docker-compose down. To release the resources properly, we need to stop the right services (containers) and add the -v option to make sure that even named volumes are removed after the build finishes. In our case, that's mainly because of the MySQL's dbdata volume.
      docker-compose -f docker-compose.integration.yml up
      docker-compose -f docker-compose.integration.yml down -v
      
      This should take care of our database initialization.

      Fixing the Hanging Build Problem

      To fix this problem and to ensure that our Integration Tests build from scratch each time, we are going to add a few flags to our docker-compose up command:
      docker-compose -f docker-compose.integration.yml up --force-recreate --abort-on-container-exit --build
      docker-compose -f docker-compose.integration.yml down -v
      What these flags do:
      • --force-recreate: Forces the recreation of the containers, even if their configuration hasn't changed
      • --abort-on-container-exit: Stops all containers if any container stops. Useful in our case, because once we finish the tests, we don't need other containers to run anymore
      • --build: Forces the build of images before the container starts
      These flags fix the hanging build problem.

      Running the Integration Tests Again

      Now let's run the tests again and see if they build this time.:

      tests passed tc

      And what do you know, they do! Hurray! You might need to run the build twice because the build hanged the first time and it needs to be shut down.

      Connecting Build Configurations

      So what is our endgame here? We want to build our main project, AccountOwner ASP.NET Core application, on every commit, and then use the newly built image to run integration tests. In order to do this, we need to connect our build configurations somehow. Currently, we have two build configurations: build configurations Let's connect them. Navigate to the Integration build configuration settings and in the Dependencies section add the dependency to the Build build configuration: create dependency Now, this dependency means that our Build build configuration will trigger everytime we trigger the Integration build configuration. TeamCity is even smart enough to resolve triggers, so we can safely remove a VCS trigger from our Build build configuration and when we commit our changes, TeamCity will actually figure out if it needs to trigger it or not depending on the changes we made to the project. How awesome is that? Now that our builds are connected let's figure out how to remove the hardcoded tags, we've been using so far.

      Removing Hardcoded Tags from Images

      The last but not the least important step is removing the hardcoded tags from our docker-compose.integration.yml file. We have already used TeamCity's %build.number% parameter to create a new image on every build. Now that our builds are connected, we can use that value and put it right into our docker-compose.integration.yml file so we can run integration tests on the newest image possible. Now if we run the Integration build, we can see how the builds are connected in the Dependencies tab: build chain So far, we've used my-registry:50000/codemazeblog/accountownerapp:build-2 as a base image. Let's change it so it dynamically adds the right tag using something called "Environment Variable Substitution". First, we need to find out how TeamCity resolves the build numbers of dependent projects. Since we connected the builds, we can find all sorts of useful information in the Parameters section of our Integration build. For example, we are particularly interested in the build number of the dependent build. So if we scroll down a bit to the Parameters from the Dependencies section within the Parameters tab we can see that information: dependency build number We are interested in build.number here, not the build.counter which is the increment. But to make it available to the current build, the Integration build, we need to promote it to the environment variable. We can do this in the in the Parameters section of our Integration build configuration: create env variable Now we can substitute the hardcoded value in the docker-compose.integration.yml file:
      accountownerapp:
          depends_on:
            - db
          image: my-registry:50000/codemazeblog/accountownerapp:build-${BUILD_NUMBER_ACCOUNTOWNER}
          build:
            context: .
      To test this out you can set the environment variable in PowerShell with:
      $env:BUILD_NUMBER_ACCOUNTOWNER = 10
      And check the configuration with:
      docker-compose -f docker-compose.integration.yml config
      The result should look like this: config result As our configuration seems to work, let's commit it, and see how TeamCity resolves everything now. build process finished And the build finished successfully! To make sure this is not a mistake let's make one of our integration tests fail by changing the Assert.NotEmpty to Assert.Empty.
      [Fact]
      public async Task GetAllOwners_ReturnsAListOfOwners()
      {
      	// Act
      	var response = await _context.Client.GetAsync("/api/owner");
      	response.EnsureSuccessStatusCode();
      	var responseString = await response.Content.ReadAsStringAsync();
      	var owners = JsonConvert.DeserializeObject<List<Owner>>(responseString);
      
      	// Assert
      	Assert.Empty(owners);
      }
      And run it again: build process failed And our test failed as expected. You can navigate to the build log to see the reason. But now that we made our tests pass, we don't want to open the build log to look for the test results. We want them to be clearly visible at the project overview screen. Here's a little trick that can help us with that.

      How to Make Test Results Visible

      TeamCity has a cool way of helping us display the test results when using xUnit. xUnit runner detects the presence of TeamCity by looking for the TEAMCITY_PROJECT_NAME environment variable. So, let's add it to our integration service in the docker-compose.integration.yml file.
      integration:
          depends_on:
            - accountownerapp
          image: my-registry:50000/codemazeblog/accountownerapp:test
          build:
            context: .
            dockerfile: Dockerfile.Integration
          environment:
            - TEAMCITY_PROJECT_NAME
      If you are wondering what this means, this is just short for TEAMCITY_PROJECT_NAME = ${TEAMCITY_PROJECT_NAME }. And there is one more thing we need to do to make it work. By default xUnit verbosity in TeamCity is set to minimal. We need to ramp it up by changing the entrypoint in our Dockerfie.Integration a bit:
      ENTRYPOINT ["dotnet", "test", "--verbosity=normal"]
      That's it, let's commit and wait for the build to finish. Once it finishes, we can check the build results either on the project page or the Dependencies tab of our Integration build configuration: tests failed tc visible Now we can clearly see that tests have failed instead of generic Success/Error message. And we can click on that message to go to the stack trace and see exactly why the tests failed. Isn't that just great? There is one thing remaining, and that's to configure our Unit Tests results to show up in a similar manner. In order to do that, we need to tweak the Dockerfile a bit.
      FROM microsoft/aspnetcore-build as build-image
      
      WORKDIR /home/app
      
      COPY ./*.sln ./
      COPY ./*/*.csproj ./
      RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done
      
      RUN dotnet restore
      
      COPY . .
      
      RUN dotnet test --verbosity=normal ./Tests/Tests.csproj
      
      RUN dotnet publish ./AccountOwnerServer/AccountOwnerServer.csproj -o /publish/
      
      FROM microsoft/aspnetcore
      
      WORKDIR /publish
      
      COPY --from=build-image /publish .
      
      ENV TEAMCITY_PROJECT_NAME = ${TEAMCITY_PROJECT_NAME}
      
      ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"]
      We ramped up the verbosity level to normal, and in a similar manner as before, added the environment variable that helps xUnit recognize that it is running inside TeamCity. Now our results look like this: tests passed tc visible Great stuff!

      Conclusion

      In this lengthy part, we've gone through a lot of concepts and tweaks. TeamCity is a powerful tool and becomes even more powerful when combined with Docker. Although we used it for a simple pipeline, TeamCity is flexible to support even the most complex projects. Add to that the cross-platform nature of Docker, and you get monster-like tooling for anything you might need. Ever. Although we used the ASP.NET Core application as our base app, these concepts and configurations are applicable to any other project type or language. Now that you can integrate Docker with TeamCity, there are no boundaries to what you can do with it. Although TeamCity is an on-premises tool, using these methods, you can make it cloud-like by hosting it on a remote machine, no matter which platform you choose. Full source code with the modifications we made throughout this article can be found on the docker-series-continuous-integration-end branch of our docker-series repo. Hopefully, you found this article useful. There are a lot of puzzle pieces in it, so don't hesitate to leave a comment or ask for help. [sc name="subscribe" formNumber="3820" contentType="CI/CD"]]]>
      3183 0 0 0 198 https://www.alvinashcraft.com/2018/06/04/dew-drop-june-4-2018-2738/ 0 0 204 https://chevenix.wordpress.com/2018/06/10/szumma-113-2018-23-het/ 0 0
      React with .NET Core and MySQL - Lazy Loading and HOC Component https://code-maze.com/react-net-core-lazy-loading/ Wed, 30 May 2018 06:00:08 +0000 https://code-maze.com/?p=3193 the following link: Introduction to the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the React series, check out: Introduction to the React series. For the previous part check out: HTTP, Axios, Redux The source code is available at GitHub: React series - react-series-part4-end branch This post is divided into several sections:

      OwnerList Component Creation, HOC, and Routes

      Let's create the following folder structure and the OwnerList.js file inside the containers folder:
      ownerlist structure - React Lazy Loading
      Prior to modifying our OwnerList component, let’s create one higher-order component (HOC). We are going to use it as a helper to wrap our content inside the return() block in a component. As you already know (from the previous posts) the return() block requires one root tag and all the content inside it. If we don’t want to use a div or a p tag that can mess up our stylings, we can create this auxiliary component. It is going to create a wrapper that returns all the children's content inside it without messing up the stylings of a project. So, inside the src folder let’s create a new folder and name it hoc. In that folder, we are going to create a new one and name it Auxiliary. Finally, let’s create a new file Auxiliary.js:
      auxiliary structure - React Lazy Loading
      Now, we need to modify the Auxiliary.js file:
      const auxiliary = (props) => props.children;
      
      export default auxiliary;
      
      All we are doing here is just returning the children's content which is going to be placed between open and close auxiliary tag. Now, we can modify the OwnerList.js file:
      import React, { Component } from 'react';
      import { Table, Col, Row } from 'react-bootstrap';
      import { Link } from 'react-router-dom';
      import Aux from '../../../hoc/Auxiliary/Auxiliary';
      
      class OwnerList extends Component {
          render() {
              let owners = [];
              return (
                  <Aux>
                      <Row>
                          <Col mdOffset={10} md={2}>
                              <Link to='/createOwner' >Create Owner</Link>
                          </Col>
                      </Row>
                      <br />
                      <Row>
                          <Col md={12}>
                              <Table responsive striped>
                                  <thead>
                                      <tr>
                                          <th>Name</th>
                                          <th>Date of birth</th>
                                          <th>Address</th>
                                          <th>Details</th>
                                          <th>Update</th>
                                          <th>Delete</th>
                                      </tr>
                                  </thead>
                                  <tbody>
                                      {owners}
                                  </tbody>
                              </Table>
                          </Col>
                      </Row>
                  </Aux>
              )
          }
      }
      
      export default OwnerList;
      

      OwnerList Component Explanation

      The logic behind this is pretty straightforward. We are creating an empty arrayowners which is going to hold all the owners of the database. Above the table, there is a link to the Create Component. We are using the Link component of the react-router-dom library. Then we need to create a table that will display all the data from the array, even though currently, it is just an empty array. Let’s continue by modifying the App.js file to add a route to this component:
      import OwnerList from './Owner/OwnerList/OwnerList';
      <Route path="/" exact component={Home} />
      <Route path="/owner-list" component={OwnerList} />
      <Route path="*" component={NotFound} />
      
      If we start our application with the npm start command, as a result, we will be able to navigate to the OwnerList component by clicking the Owner Actions menu:
      OwnerActions menu - React Lazy Load

      Implementing Redux

      Since we currently don’t have any data, let’s add some. First, we have to connect our component to our reducer. The reducer is going to set up the state and pass the data to this component as a property inside the props object. To do that, let’s import connect from the react-redux library and all the actions from the repositoryAction.js file:
      import { connect } from 'react-redux';
      import * as repositoryActions from '../../../store/actions/repositoryActions';
      
      Then below the closing bracket of our component and right above the export statement, we are going to add the following functions and then to modify that export statement:
      const mapStateToProps = (state) => {     
          return {  
             data: state.data     
          } 
      }
      const mapDispatchToProps = (dispatch) => {
           return {
               onGetData: (url, props) => dispatch(repositoryActions.getData(url, props))     
          } 
      } 
      export default connect(mapStateToProps, mapDispatchToProps)(OwnerList);
      The mapStateToProps function is mapping the data property from the repositoryReducer’s initialState object to the data property inside the OwnerList component. To access this data property, we just need to call it like this: this.props.data. The mapDispatchToProps function is creating additional property onGetData. We may call it with the: this.props.onGetData statement. Then, it is going to dispatch the action inside the repositoryActions.js file which is going to fetch the data from the server.

      Redux Roundup

      Right now if we look at the diagram from the previous post, everything makes perfect sense. From this component, we call the action inside the repositoryActions.js file. This action fetches the data from the server and triggers the reducer. Our reducer updates the state by modifying the data property inside the initialState object. Finally, the Central Store maps that data property to this component with the mapStateToProps function.

      Creating the Owner Component

      Since we want to display multiple owners inside the OwnerList component, let's create the Owner component and import it into the OwnerList component. In the components folder create the following structure:
      Owner component - React Lazy Loading
      To work with dates we need to install one more third-party libraryreact-moment:
      npm install --save react-moment
      React moment installation
      We need to install the moment library as well:
      npm install --save moment
      Now we can modify the Owner.js file:
      import React from 'react';
      import Aux from '../../../hoc/Auxiliary/Auxiliary';
      import Moment from 'react-moment';
      import { Button } from 'react-bootstrap';
      
      const redirectToOwnerDetails = (id, history) => {
          history.push('/ownerDetails/' + id);
      }
      
      const redirectToUpdateOwner = (id, history) => {
          history.push('/updateOwner/' + id);
      }
      
      const rediterctToDeleteOwner = (id, history) => {
          history.push('/deleteOwner/' + id);
      }
      
      const owner = (props) => {
          return (
              <Aux>
                  <tr>
                      <td>{props.owner.name}</td>
                      <td><Moment format="DD/MM/YYYY">{props.owner.dateOfBirth}</Moment></td>
                      <td>{props.owner.address}</td>
                      <td>
                          <Button onClick={() => redirectToOwnerDetails(props.owner.id, props.history)}>Details</Button>
                      </td>
                      <td>
                          <Button bsStyle="success" onClick={() => redirectToUpdateOwner(props.owner.id, props.history)}>Update</Button>
                      </td>
                      <td>
                          <Button bsStyle="danger" onClick={() => rediterctToDeleteOwner(props.owner.id, props.history)}>Delete</Button>
                      </td>
                  </tr>
              </Aux>
          )
      }
      
      export default owner;
      
      In this child component, we receive the owner’s data, through the props object. Then, we create a row with the data and a couple of buttons for navigation to different components. All buttons are referencing functions that enable redirection towards the details, update and delete components. The important thing to notice is that the props object has the “history” property which allows us to navigate programmatically. Furthermore, we are using the date format: “DD/MM/YYYY” just to show how easy is to work with formats by using the Moment library. For the create and update components, we are going to use “MM/DD/YYYY”. All that's left to do is to import this component inside the OwnerList component and display all the owners on the screen.

      Displaying Owners Result

      Let’s import the Owner component inside the OwnerList component:
      import Owner from '../../../components/OwnerComponents/Owner/Owner';
      Then above the render() part, we are going to create a new function. With this function, we are going to call the OnGetData property to fetch the data from the server:
      componentDidMount = () => {
          let url = '/api/owner';
          this.props.onGetData(url, { ...this.props });
      }
      
      The componentDidMount is a creation lifecycle hook and it is going to trigger as soon as a component mounts. Finally, inside the render function add a code to populate our “owners” array:
      let owners = [];
      if (this.props.data && this.props.data.length > 0) {
          owners = this.props.data.map((owner) => {
              return (
                  <Owner key={owner.id} owner={owner} {...this.props} />
              )
          })
      }
      
      In this function, we check if the data property is populated (because http requests are async requests) and if it is an array. Then for each of the array's elements, we populate the Owner component with the data. Pay attention to the key property which is mandatory when creating child components from the array of objects. Moreover, we pass the props object to have access to the “history” property of that object. In the same way, we pass a single owner object to a child component because we want it to be able to use the props.owner syntax. Excellent. Now if we navigate to the Owner Actions menu we are going to see the result with all the owners:
      OwnersList display - React Lazy Loading

      Lazy Content Loading

      Until now, we have been loading our OwnerList component eagerly and not lazily, which means that once the application starts all the resources are loaded as well. This is not the best practice because a user might never visit an Owner Actions page, therefore the resources for this page shouldn’t be loaded either. Let’s modify our project so we can utilize the lazy loading feature. Let’s create, inside the hoc folder, a new folder AsyncComponent and inside a new file AsyncComponent.js and modify it:
      import React, {Component} from 'react';
      
      const asyncComponent = (importComponent) => {
          return class extends Component{
              state = {
                  component: null
              }
      
              componentDidMount(){
                  importComponent()
                  .then(cmp => {
                      this.setState({component: cmp.default});
                  });
              }
              render(){
                  const C = this.state.component;
      
                  return C ? <C {...this.props} /> : null;
              }
          }
      }
      
      export default asyncComponent;
      
      With this component, we are going to load our component asynchronously. To complete this action, we need to modify the App.js file to load our OwnerList component in async mode:
      //import OwnerList from './Owner/OwnerList/OwnerList';
      import asyncComponent from '../hoc/AsyncComponent/AsyncComponent';
      
      const AsyncOwnerList = asyncComponent(() => {
        return import('./Owner/OwnerList/OwnerList');
      });
      
      class App extends Component {
        render() {
          return (
            <BrowserRouter>
              <Layout>
                <Switch>
                  <Route path="/" exact component={Home} />
                  <Route path="/owner-list" component={AsyncOwnerList} />
                  <Route pat="*" component={NotFound} />
                </Switch>
              </Layout>
            </BrowserRouter>
          );
        }
      }
      
      export default App;
      
      Now we import our Async component and inside it, we import our OwnerList component. Finally, we are not loading OwnerList inside the Route component anymore, but the AsyncOwnerList. There it is. Now if we navigate to the Owner Actions page after restarting our app, we are going to see additional chunk file loaded just for this page.
      React Lazy Loading Result

      Conclusion

      By reading this post, you've learned:
      • The way to create HOC components
      • How to implement Redux to fetch the data from the server
      • How to use the Moment library to format the dates
      • The way to load your components in an async manner by using the Lazy Loading feature
      Thank you for reading the article and I hope you found something useful in it. In the next part of the series, we are going to learn how to handle errors while sending our HTTP requests. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      3193 0 0 0 178 https://www.alvinashcraft.com/2018/05/30/dew-drop-may-30-2018-2735/ 0 0 1020 if (this.props.data && this.props.data.length > 0) {..., I navigate to the Owner Actions menu but I don't see the result with all the owners. Any pointers on what I'm maybe missing? Thanks again, Diego]]> 0 0 1021 1020 0 1030 1021 0 1031 1030 0
      Angular Development Best Practices https://code-maze.com/angular-best-practices/ Mon, 02 Jul 2018 06:03:42 +0000 https://code-maze.com/?p=3243
    • Angular Tutorial
    • Angular Material Tutorial
    • ASP.NET Core Authentication with JWT and Angular
    • Upload Files with .NET Core and Angular
    • SignalR with .NET Core and Angular
    • [sc name="part_of_series" headline="Recommended Articles"] Without further ado, let’s dive right into it. In this blog post, we are going to go through the following sections:

      Angular CLI

      We would always recommend using the Angular CLI tool while working with the Angular project. Why is that? Well, it will increase our productivity for sure. Angular CLI has its own set of commands for creating the Angular project, creating components, modules, services etc… Not only that we are creating our components faster with Angular CLI, but it will reference those components into their own modules and will comply to the naming convention as well, so we don’t have to worry about it. We can use a basic set of commands to generate our components but every command has its own options. For example, if we want to generate a component we would type:
      ng g component example_component
      It is going to generate 4 files for us. The component file, html file, css file, and the spec file. The last one is for testing purposes. If we don’t want to have that last one created we can just add the flag: --skipTests(don't use --spec false because it is depreciated). By doing this Angular CLI will not generate the spec file at all. This is just one of the options. We may find more about Angular CLI on https://github.com/angular/angular-cli

      Files and Folders in Angular Project

      Let's see how to name our files and classes and how to organize the folder structure in an Angular project.

      File Naming

      While creating our files, we should pay attention to the file names. Names should be consistent with the same pattern in which we mention the file’s feature first and then the type, dot separated. For example home.component.ts or home.component.html or auth.service.ts … If we want to add more descriptive names to our files we should use a dash(-) to separate the words in the name: menu-admin-user.component.ts

      Class Names

      When we add names to our classes, we should use upper camel case style with the added suffix that represents the type of our file:
      export class DatepickerDirective
      export class HomeComponent
      export class RepositoryService
      

      Folder Structure

      Our files should reside in the folders named by the feature they represent. This is very important because we can easily identify which business logic we have implemented in those files inside our descriptive folders: Folder structure - Angular Best Practices

      Angular Coding Practices

      In this part, we are going to explain how to organize components in a single file and what are the benefits of using interfaces, immutability and writing clean and small functions.

      Single Responsibility Principle

      It is very important not to create more than one component, service, directive… inside a single file. Every file should be responsible for a single functionality. By doing this, we are keeping our files clean, readable and maintainable.

      Using Interfaces

      If we want to create a contract for our class we should always use interfaces. By using them we can force the class to implement functions and properties declared inside the interface. Let’s take for example OnInit interface and its implementation:
      export interface OnInit {
          ngOnInit(): void;
      }
      
      export class HomeComponent implements OnInit {
      
        constructor() { }
      
        ngOnInit() {
          
      }
      
      Using interfaces is a perfect way of describing our object literals. If our object is of an interface type, it is obligated to implement all of the interface’s properties. We shouldn’t name our interfaces with the starting capital I letter as we do in some programming languages.
      export interface User {
          name: string;
          age: number;
          address: string;
      } 
      
      The TypeScript will show an error if an object doesn’t contain all of the interface’s properties, and light up intellisense for us while populating that object:
      public user: User;
      Interface not implemented Angular Best Practices Interface-intellisense Angular Best Practice We can specify optional properties, by using the question mark (?) inside an interface as well. We don’t need to populate those properties inside an object:
      additionalData?: string;

      Using Immutability

      Objects and arrays are the reference types in javascript. If we want to copy them into another object or an array and to modify them, the best practice is to do that in an immutable way. By modifying reference types immutably, we are preserving the original objects and arrays and modifying only their copies. The easiest way to modify objects and arrays immutably is by using the es6 spread operator (…) :
      this.user = {
        name: 'Dzon',
        age: 25,
        address: 'Sunny street 34'
      }
      
      let updatedUser = {
        ...this.user,
        name: 'Peter'
      }
      
      We are deep copying the user object and then just overriding the name property. Let’s take a look at how to use the spread operator with arrays:
      public results = [10, 12, 14];
      
      let newNumbers = [...this.numbers, 45, 56];
      
      Now if we remove or modify any of the numbers from the newNumbers array, the original array is going to be preserved with all of its values.

      Small Functions and Access Modifiers

      Whenever we create our functions to execute some business logic, we should keep them small and clean. Small functions are easier to test and reuse. Furthermore, it is much easier to understand and maintain functions with fewer lines of codes. If we notice that our function is becoming too crowded it is a time to create a new one. It is very important to distinguish the functions and properties that we are going to use only in our components from those that we are going to call from our template file. Those properties and functions, which we are going to reference from the template should always have the public access modifier. The private ones will be visible to the HTML template as well, but it is not a good practice due to AoT compilation failure.

      Constructor Usage

      We should use the constructor method to set up Dependency Injection for our services and that is pretty much it. We shouldn't be doing any work inside it, especially fetching the data from the server. For this type of actions, we have the lifecycle hooks in Angular. Even though we can use the service injection inside the constructor:
      private router: Router;
      
      constructor(routerParam: Router) {
        this.router = routerParam;
      }
      
      The much better and the recommended way is:
      constructor(private router: Router) {}

      Safe Navigation Operator (?) in HTML Template

      To be on the safe side we should always use the safe navigation operator while accessing a property from an object in a component’s template. If the object is null and we try to access a property, we are going to get an exception. But if we use the save navigation (?) operator, the template will ignore the null value and will access the property once the object is not the null anymore.
      <div class="col-md-3">
        {{user?.name}}
      </div>
      

      Module Organization and Lazy Loading

      Modules are very important in an Angular project, so let's talk about that and lazy loading and shared modal features.

      Multi Modules in Application

      Even though an Angular application is going to work just fine if we create just one module, the recommendation is to split our application into multi-modules. There are a lot of advantages to this approach. The project structure is better organized, it is more maintainable, readable and reusable and we are able to use the lazy-loading feature.

      Routing Module

      The best practice for Angular application is to use a separate routing module for the router:
      const appRoutes: Route[] = [
        { path: 'home', component: HomeComponent },
        { path: '404', component: NotFoundComponent }
      ]
      
      @NgModule({
        imports: [
          CommonModule,
          RouterModule.forRoot(appRoutes)
        ],
        exports: [RouterModule]
      })
      export class AppRoutingModule { }
      
      And then to register this new routing module in our app module:
      import { AppRoutingModule } from './app-routing.module';
      
      imports: [
        AppRoutingModule
      ]
      

      Lazy Loading

      If we have a multi-modular application, implementing a lazy loading feature is recommended. The great advantage of a lazy loading approach is that we can load our resources on demand and not all at once. This helps us in decreasing the startup time. Modules that we are loading in a lazy manner will be loaded as soon as a user navigates to their routes. Let’s show how to set up lazy loading in our module:
      const appRoutes: Route[] = [
        { path: 'home', component: HomeComponent },
        { path: 'owner', loadChildren: "./owner/owner.module#OwnerModule" },
      ]
      
      In this code example, the HomeComponent is loading eagerly but the OwnerModule and all the components registered in that module are loading in a lazy manner. If you want to learn more about the lazy loading feature you may read our blog post about lazy loading.

      Shared module

      If we have components, directives or pipes in our project, which we want to share through the entire project, the best way to do that is to register them inside the shared module file. Then, we need to register the shared module inside the app module. It is important not to register services, that we want to use globally,  in a shared module. We should register such services inside its own feature module or in the app module. But, if we need any service registered inside a shared module, the best way is to provide it through the static forRoot() method that returns the ModuleWithProviders interface:
      imports and @NgModule and exports part...
      
      export class SharedModule {
        static forRoot(): ModuleWithProviders {
          return {
            ngModule: SharedModule,
            providers: [ AnyService ]
          };
        }
      }
      Now, we can register this shared module in our app module with the following syntax: SharedModule.forRoot(). It is also important to export our components, directives, and pipes that we register in the shared module:
      @NgModule({
        imports: [
          CommonModule
        ],
        declarations: [
          ErrorModalComponent,
          SuccessModalComponent,
          DatepickerDirective
        ],
        exports: [
          ErrorModalComponent,
          DatepickerDirective,
          SuccessModalComponent
        ]
      })
      export class SharedModule { }
      

      Components, Decorators, Directives, and Lifecycle

      Let's explain how to structure the files in ou components and why to use directives and lifecycle hooks.

      Component’s Files Separation

      While creating our components, it is very important to separate the component, template and styling logic into the separate files. Even though we have the possibility to keep all of it inside the component file this is not a good idea. So, we shouldn’t be doing something like this:
      @Component({
        selector: 'app-home',
        template: `
        <div class="col-md-12">
          <p class="homeText">{{homeText}}</p\>
        </div>`,
        styles: [`
        .homeText{
          font-size: 35px;
          color: red;
          text-align: center;
          position: relative;
          top:30px;
          text-shadow: 2px 2px 2px gray;
      }
        `]
      })
      
      Instead, we should separate styles and HTML in their own files and import them:
      @Component({
        selector: 'app-home',
        templateUrl: './home.component.html',
        styleUrls: ['./home.component.css']
      })
      

      Reusable Components and Decorators

      Creating reusable components is one of the best techniques we can use while developing our project. We can reuse those types of components inside any parent component and pass the data through the @Input decorator. Those components can emit events by using the @Output decorator and EventEmmiter class. Here is an example of the @Input and @Output decorators in action:
      export class SuccessModalComponent implements OnInit {
        @Input() public modalHeaderText: string;
        @Input() public modalBodyText: string;
        @Input() public okButtonText: string;
        @Output() public redirectOnOK = new EventEmitter();
      
        constructor() { }
      
        ngOnInit() {
        }
      
        public emmitEvent(){
          this.redirectOnOK.emit();
        }
      
      To learn more about decorators in angular and how to use them to create reusable components, you may read Child components, @Input, @Output Decorators.

      Code Simplicity in Components

      To keep our components readable and easy maintainable we should write a clean code inside them. We should limit the logic in the component to satisfy the template needs and nothing more. No additional complicated business logic is required. If we need additional logic, we should extract it into service. That way our component remains clean and the extra code could be reused in other components. Furthermore, the logic inside a service can be easily tested and it hides an implementation of the component, thus making a component more readable.

      Using Directives

      Whenever we have a situation where multiple HTML elements have the same behavior (for example: when we hover over the element it receives the blue color), we should consider using attribute directives. We shouldn't repeat the hover logic every time we need it on some HTML element. A much better way would be to create a directive and then just reuse it on the particular element. Directive creation is quite simple:
      import { Directive, ElementRef, HostListener, Input } from '@angular/core';
      
      @Directive({
        selector: '[appHover]'
      })
      export class HoverDirective {
      
        @Input() public hoverColor: string;
      
        constructor(private element: ElementRef){}
      
        @HostListener('mouseenter') onMouseEnter(){
          this.highlightElement(this.hoverColor);
        }
      
        @HostListener('mouseleave') onMouseLeave(){
          this.highlightElement(null);
        }
      
        private highlightElement(color: string){
          this.element.nativeElement.style.backgroundColor = color;
        }
      }
      
      We need to register this directive in a required module and to call it in HTML template file:
      <p appHover [hoverColor]="'blue'">this is hoverable text</p>
      

      Using Lifecycle Hooks

      Lifecycle hooks play a very important part of Angular development. We should use them whenever we have an opportunity to. For example, if we need to fetch some data from a database as soon as our component is instantiated, we should use ngOnInit() lifecycle hook and not the constructor. If we have some logic inside child components and we want to execute that logic as soon as decorator parameters are modified, we can use ngOnChanges() lifecycle hook. If we need to clean up some resources as soon as our component is destroyed, we should use ngOnDestroy() lifecycle hook. Even though we don’t have to implement interfaces to use the lifecycle hooks, we are better off implementing them. So don’t do something like this:
      export class OwnerListComponent {
      
        constructor() { }
      
        ngOnInit() {
          this.getAllOwners();
        }
      
      Instead, implement it like this:
      export class OwnerListComponent implements OnInit {
      
        constructor() { }
      
        ngOnInit() {
          this.getAllOwners();
        }
      

      Services

      Let's talk a bit more about services, how to use them, why to use them and how to provide them.

      Service Usage

      Services provide a great way to connect two unrelated components. To configure services for this, the best way is to register them is as a singleton. Furthermore, services are a great way to extract the code from our components, thus making components more readable and maintainable. We may use a service to extract the code related to the component in which that service is provided or to share reusable code with that service between different components.

      Single Responsibility

      Services in Angular should be implemented with the single responsibility principle in mind. Service shouldn’t be responsible for multiple actions or features. Just the opposite. As soon as we notice that our service starts to exceed the singular purpose, we should create another service. Just a simple example. If we want to send the HTTP requests, we should create a service as a centralized place for that kind of actions. But we shouldn't handle errors inside the same service. We should create another one for that error handling. By creating services to concur to the single responsibility principle, we are making the code more readable, easier to test and to maintain.

      Service Providing

      If we want to use our service as a singleton, then we should provide that service at the root of our application. That way Angular creates a single, shared instance of our service, available for all components. Since Angular v6, if we create a service with the Angular CLI command: ng g service service_name, it will create a service with the providedIn metadata with the “root” value:
      import { Injectable } from '@angular/core';
      
      @Injectable({
        providedIn: 'root',
      })
      export class HeroService {
      
        constructor() { }
      
      }
      
      By providing such a service means that we don’t have to import it in the app.module file or to provide it in providers array inside the same file. Of course, if we need it, we can do it that way as well. We can register our service at different levels. For example in a component level:
      import { Component } from '@angular/core';
      import { ErrorHandlerService } from './shared/services/error-handler.service';
      
      @Component({
        selector: 'foo-root',
        templateUrl: './foo.component.html',
        styleUrls: ['./foo.component.css'],
        providers: [ErrorHandlerService]
      })
      
      Or in a module level:
      import { TestserviceService } from './testservice.service';
      
      @NgModule({
        declarations: [
          AppComponent,
          HomeComponent
        ],
        imports: [
          BrowserModule,
          HttpClientModule,
          AppRoutingModule
        ],
        providers: [
          TestserviceService
        ],
        bootstrap: [AppComponent]
      })
      export class AppModule { }
      

      HTTP

      Let's talk more about HttpClient Module and how to use HTTP requests in our services.

      Using the HttpClient Module

      Since Angular v4.3, we have a new and better way to handle HTTP requests with the HttpClient library. Even though the old HttpModule library is working just fine, it is a recommendation to switch to HttpClient. From Angular v6 is even more recommended to switch to the HttpClient library. The way of using the HttpClient library is the same as with the old one. First, we import it into your app module:
      import { HttpClientModule } from '@angular/common/http';
      Then register it in the imports array in the same file:
      imports: [
        HttpClientModule
      ]
      
      After that, we need to import it into the service we want to use HttpClient in:
      import { HttpClient } from '@angular/common/http';
      Finally, we need to register it inside the constructor method in that service:
      constructor(private http: HttpClient) { }
      With the HttpClient library, we receive additional benefits. Error handling is much easier by using HttpErrorResponse class. Moreover, we have the ability to listen to the progress events and we have interceptors which allow us to intercept the HTTP requests.

      Services to Work with HTTP Requests

      Even though we can work with the HTTP requests inside our components, the recommended way is to do that by using a service. Sending HTTP requests isn’t always that simple. Many times we have to set up the headers of the request to track the progress of the request etc… In such cases, we should transfer the HTTP handling responsibility from the component to the service. We may even create a service as a centralized place to handle the HTTP requests, and then to use that service in our application. For more details on how we do that, you can read: https://code-maze.com/net-core-web-development-part9/#angularRepository

      Environment Variables

      If we develop an Angular application which will be deployed in the production environment, the environment variables inside the Angular project can be very helpful. We can set up those variables to distinguish the end-points between development and production environment. For example, our endpoint in the development environment could be http://localhost:5000 and in production environment http://www.examplesite.com. Environment variables inside the Angular project help us with those endpoints while we work with the HTTP requests. Once we set up those environment variables, we can use them in our repository service, and never worry which endpoint will our HTTP requests use for which environment again. If you want to learn more about how to use and set up environment variables in Angular, you can read https://code-maze.com/net-core-web-development-part9/#environmentFiles.

      Conclusion

      In this article, we wanted to bring you closer to what the recommendations while developing our Angular project are. Of course, these are not all the Angular best practices but we tried to round up the most important ones in this article. Are these the best practices you already use? We are eager to hear if we missed some practice you consider to be important, so leave us a comment below. Thank you for reading the article and I hope you found something useful in it. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      3243 0 0 0 234 0 0 235 234 0 259 https://memext.wordpress.com/2018/07/21/angular-links/ 0 0 328 0 0 329 328 0 562 https://java-ways.com/angular-development-best-practices/ 0 0 566 https://kodummu.wordpress.com/2019/01/10/angular-and-service-workershttps-www-youtube-com-watchv8rmba9asbfw/ 0 0 665 0 0 724 665 0 1187 0 0 1188 Angular Tutorial where we consume the ASP.NET Core Web API, maybe you find something useful in there too. Best regards.]]> 1187 0 1458 0 0 1459 235 0 1460 1458 0
      React - How to Configure React Error Handling - Code Maze https://code-maze.com/react-net-core-error-handling/ Wed, 06 Jun 2018 08:00:32 +0000 https://code-maze.com/?p=3287 the following link: Introduction to the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the React series, check out: Introduction to the React series. For the previous part check out: Lazy Loading and HOC Component The source code is available at GitHub: React series - react-series-part5-end branch This post is divided into several sections:

      Creating the 500 (Internal Server Error) Component

      Let’s create a new InternalServer folder inside the ErrorPages folder, and in there let’s create two new files and name them InternalServer.js and InternalServer.css: Internal server error structure - React Error Handling We are going to modify the InternalServer.js file:
      import React from 'react';
      import './InternalServer.css';
      
      const internalServer = (props) => {
          return (
              <p className={'internalServer'}>{"500 SERVER ERROR, CONTACT ADMINISTRATOR!!!!"}</p>
          )
      }
      
      export default internalServer;
      
      Then, let’s modify the InternalServer.css file:
      .internalServer{
          font-weight: bold;
          font-size: 50px;
          text-align: center;
          color: #c72d2d;
      }
      
      Finally, let’s modify the App.js file:
      import InternalServer from '../components/ErrorPages/InternalServer/InternalServer';
      <Route path="/" exact component={Home} />
      <Route path="/owner-list" component={AsyncOwnerList} />
      <Route path="/500" component={InternalServer} />
      <Route path="*" component={NotFound} />
      
      Excellent. The Internal Server Error (500) component is ready and we may continue on.

      Implementing Redux for Error Handling

      As we did with the repository part of this application, we are going to create another redux instance to handle errors in a single place in our app. We are familiar with the redux flow so this part is going to be quite easy to implement. One additional thing is that after creating a new reducer file, we are going to have two reducer files and therefore, we need to make a different registration process inside the Index.js file. We can handle errors in a few different ways, and here we are going to do it by using multiple reducers in a project. Moreover, centralizing the error handling actions is a good practice overall. Let’s continue by modifying the ActionTypes.js file by adding three additional action types:
      export const HTTP_404_ERROR = 'HTTP_404_ERROR';
      export const HTTP_500_ERROR = 'HTTP_500_ERROR';
      export const HTTP_OTHER_ERROR = 'HTTP_OTHER_ERROR';
      
      Inside the actions folder, we have the repositoryActions.js file. Now, inside the same folder, we need to add one more action file errorHandlerActions.js.  So, let’s modify that file:
      import * as actionTypes from './actionTypes';
      
      const execute404Handler = (props) => {
          return {
              type: actionTypes.HTTP_404_ERROR,
              props: props
          }
      }
      
      const execute500Handler = (props) => {
          return {
              type: actionTypes.HTTP_500_ERROR,
              props: props
          }
      }
      
      const executeOtherErrorHandler = (error) => {
          return {
              type: actionTypes.HTTP_OTHER_ERROR,
              error: error
          }
      }
      
      export const handleHTTPError = (error, props) => {
          if (error.response.status === 404) {
              return execute404Handler(props);
          }
          else if (error.response.status === 500) {
              return execute500Handler(props);
          }
          else {
              return executeOtherErrorHandler(error);
          }
      }
      
      In the code above, we export the action handleHTTPError in which we check the error status code and execute the corresponding function. This is the same thing we did with the repositoryActions.js file. Inside the repositoryActions.js file, we need to import this errorHandlerActions.js file:
      import * as errorHandlerActions from './errorHandlerActions';
      And to replace all the comments in the catch block of every function:
      dispatch(errorHandlerActions.handleHTTPError(error, props));
      Let’s continue by creating a new reducer file errorHandlerReducer.js inside the reducers folder. Right now we have this folder structure: reducers folder structure - React Error Handling The errorHandlerReducer.js file should look like this:
      import * as actionTypes from '../actions/actionTypes';
      
      const initialState = {
          showErrorModal: false,
          errorMessage: ''
      }
      
      const execute404 = (state, action) => {
          action.props.history.push('/404');
          return { ...state };
      }
      
      const execute500 = (state, action) => {
          action.props.history.push('/500');
          return { ...state };
      }
      
      const executeOtherError = (state, action) => {
          return {
              ...state,
              showErrorModal: true,
              errorMessage: action.error.response.data
          };
      }
      
      const reducer = (state = initialState, action) => {
          switch (action.type) {
              case actionTypes.HTTP_404_ERROR:
                  return execute404(state, action);
              case actionTypes.HTTP_500_ERROR:
                  return execute500(state, action);
              case actionTypes.HTTP_OTHER_ERROR:
                  return executeOtherError(state, action);
              default:
                  return state;
          }
      }
      
      export default reducer;
      
      This logic is familiar as well. We create the state (initialState) object and then the function reducer which accepts the state and the action parameters. The reducer function is going to update the state based on typeproperty sent from the errorHandlerActions file.

      Combine Reducers Registration

      To finish the Redux setup let’s modify the Index.js file to register this reducer as well:
      import repositoryReducer from './store/reducers/repositoryReducer';
      import errorHandlerReducer from './store/reducers/errorHandlerReducer';
      import { Provider } from 'react-redux';
      import { createStore, applyMiddleware, combineReducers } from 'redux';
      import thunk from 'redux-thunk';
      
      const rootReducers = combineReducers({
          repository: repositoryReducer,
          errorHandler: errorHandlerReducer
      })
      
      const store = createStore(rootReducers, applyMiddleware(thunk));
      
      ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
      registerServiceWorker();
      
      That’s it. We import errorHandlerReducer file and the combineReducers function. Then with the help of the combineReducers function, we create the rootReducers object which contains all of our reducers. Finally, we just pass this object to the store creation.

      OwnerList Component Modification

      We have to do one additional thing. In the OwnerList component, we need to change the way to set up the data property from the repositoryReducer file. Let’s modify the mapStateToProps function:
      const mapStateToProps = (state) => {
          return {
              data: state.repository.data
          }
      }
      
      We must do this because we don’t have just one reducer file anymore. Both of our reducer files are registered inside one root object and therefore, we must specify which reducer we want to use. Excellent. We can try our error handling by modifying the code in the server’s method GetAllOwners. As the first line of code, we can add return NotFound() or return StatusCode(500, “Some message”), and we are going to be redirected to the right error page for sure.

      Implementation of the OwnerDetails Component

      In this component, we are going to show individual owner with all of its accounts. If you are thinking: “Well, we could split this up into two components”, you are totally right. We are going to do exactly that. The parent component is going to be the OwnerDetails component and the child component is going to be the OwnersAccounts component. So, let’s start by creating a child component first. For the OwnersAccounts component, we are going to create this structure: Owner accounts folder structure - React Error Handling Let’s modify the OwnersAccounts.js file:
      import React from 'react';
      import { Row, Col, Table } from 'react-bootstrap';
      import Moment from 'react-moment';
      
      const ownersAccounts = (props) => {
          let accounts = null;
          if (props.accounts) {
              accounts = props.accounts.map(account => {
                  return (
                      <tr key={account.id}>
                          <td>{account.accountType}</td>
                          <td><Moment format="DD/MM/YYYY">{account.dateCreated}</Moment></td>
                      </tr>
                  );
              })
          }
          return (
              <Row>
                  <Col md={12}>
                      <Table responsive striped>
                          <thead>
                              <tr>
                                  <th>Account type</th>
                                  <th>Date created</th>
                              </tr>
                          </thead>
                          <tbody>
                              {accounts}
                          </tbody>
                      </Table>
                  </Col>
              </Row>
          )
      }
      
      export default ownersAccounts;
      
      We are doing great. Let’s continue by creating a folder structure for the OwnerDetails component: Owner details Then, we are going to import all the necessary files inside the OwnerDetails component:
      import React, { Component } from 'react';
      import { connect } from 'react-redux';
      import { Well, Row, Col } from 'react-bootstrap';
      import * as repositoryActions from '../../../store/actions/repositoryActions';
      import Moment from 'react-moment';
      import OwnersAccounts from '../../../components/OwnerComponents/OwnersAccounts/OwnersAccounts';
      import Aux from '../../../hoc/Auxiliary/Auxiliary';
      
      After that, let’s add the component implementation:
      class OwnerDetails extends Component {
          render() {
             const owner = this.props.data;
      
              return (
                  <Aux>
                      <Well>
                          <Row>
                              <Col md={3}>
                                  <strong>Owner name:</strong>
                              </Col>
                              <Col md={3}>
                                  {owner.name}
                              </Col>
                          </Row>
                          <Row>
                              <Col md={3}>
                                  <strong>Date of birth:</strong>
                              </Col>
                              <Col md={3}>
                                  <Moment format="DD/MM/YYYY">{owner.dateOfBirth}</Moment>
                              </Col>
                          </Row>
                          {this.renderTypeOfUserConditionally(owner)}
                      </Well>
                      <OwnersAccounts accounts={owner.accounts} />
                  </Aux>
              )
          }
      }
      
      export default OwnerDetails;
      
      In the code above, we are using the this.props.data statement but we haven’t implemented a reducer yet. We are going to do that in a minute. Notice the renderTypeOfUserConditionally function call. In this function, we render the owner's data conditionally and return the JSX code to display.  We are going to implement that function in a minute as well. Bellow the owner’s data we are displaying all the accounts related to the owner.

      Conditional Rendering

      To implement renderTypeOfUserConditionally, we need to add the following code above the render function but still inside a class:
      renderTypeOfUserConditionally = (owner) => {
          let typeOfUser = null;
      
          if (owner.accounts && owner.accounts.length <= 2) {
              typeOfUser = (
                  <Row>
                      <Col md={3}>
                          <strong>Type of user:</strong>
                      </Col>
                      <Col md={3}>
                          <span className={'text-success'}>Beginner user.</span>
                      </Col>
                  </Row>
              );
          }
          else {
              typeOfUser = (
                  <Row>
                      <Col md={3}>
                          <strong>Type of user:</strong>
                      </Col>
                      <Col md={3}>
                          <span className={'text-info'}>Advanced user.</span>
                      </Col>
                  </Row>
              );
          }
      
          return typeOfUser;
      }
      

      Redux Connection

      Finally, let’s connect this component to the reducer file to fetch the owner’s data below the closing tag of the render function:
      const mapStateToProps = (state) => {
          return {
              data: state.repository.data
          }
      }
      
      const mapDispatchToProps = (dispatch) => {
          return {
              onGetData: (url, props) => dispatch(repositoryActions.getData(url, props))
          }
      }
      
      export default connect(mapStateToProps, mapDispatchToProps)(OwnerDetails);
      
      And above the renderTypeOfUserConditionally function add this lifecycle hook:
      componentDidMount = () => {
          let id = this.props.match.params.id;
          let url = '/api/owner/' + id + '/account';
          this.props.onGetData(url, { ...this.props })
      }
      
      We fetch the id from the url by calling the match.params.id statement from the props object. Then, we just create our url and call the onGetData property to fetch data from the server. Prior to inspecting our result, we have to add the route for this new component in the App.js file, right below the owner-list route:
      <Route path="/ownerDetails/:id" component={OwnerDetails} />
      Finally, we can inspect the result for the Beginner user: Begginer user Additionally, let's inspect the result for the Advanced user: Advanced user Great job. We may inspect these results and be sure that all is working as expected.

      Conclusion

      Right now, we are pretty sure that the Redux implementation is a piece of cake for you. As we've stated in the previous articles, after some practice, you'll be able to implement the Redux flow easily by using its own implementation pattern. We have used it to send HTTP requests and to handle HTTP errors, and now it is up to you to practice its implementation on whatever task you want. By reading this post, you've learned:
      • To centralize the error handling logic by using the Redux workflow
      • How to combine reducers into a single object
      • To fetch the data for the details view and how to create that view
      Thank you for reading the article and I hope you found something useful in it. In the next part of the series, we are going to learn how to use dynamic inputs to create forms. Moreover, we are going to introduce modal components and create a component for the POST actions. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      3287 0 0 0 500 0 0 501 500 0 506 , else button "Details" on the "Owner Actions" UI will not work.]]> 0 0 507 506 0 1047 this is doesn't work const execute404 = (state, action) => { throw Object.assign(...state, {}, action.error.response.data) }; // component this.props.actions.registrationCraftsman(craftsmanFormData) .then(() => { console.log("ok1") toastr.success(this.props.data.message); this.redirect(); }) .catch(() => { console.log("ok2") toastr.error(this.props.error.message); this.setState({analysing: false}); }); } ]]> 0 0 1048 { dispatch(registrationSuccess(response.data)); }).catch(error => { throw dispatch(handleHTTPError(error)); }); }; }]]> 1047 0
      Implementing Asynchronous Generic Repository in ASP.NET Core https://code-maze.com/async-generic-repository-pattern/ Mon, 25 Jun 2018 06:30:11 +0000 https://code-maze.com/?p=3339 .NET Core series and rewrite it in an async manner. In Part 4 of the series, we have created Generic Repository Pattern and in Part 5 and Part 6 the Controller with Actions that consumes that repository. We recommend reading those articles if you are not familiar with Generic Repository Pattern or if you find any of the concepts in this article hard to understand. Those articles will help you follow along with this article much easier because we won’t dive into its business logic. We are going to modify the code, step by step, to show you how easy is to convert the synchronous code to an asynchronous one. Hopefully, this will help you understand how asynchronous code works and how to write it from scratch in your applications. [sc name="part_of_series" headline="Recommended Articles"] To download the source code for our starting project, you can visit our GitHub repo for the starting project. For the finished project refer to our GitHub repo for the finished project. We are going to cover the following sections in this article:

      What is Asynchronous Programming

      Async programming is a parallel programming technique, which allows the working process to run separately from the main application thread. As soon as the work completes, it informs the main thread about the result, whether it was successful or not. By using async programming, we can avoid performance bottlenecks and enhance the responsiveness of our application. How so? Because we are not sending requests to the main thread and blocking it while waiting for the responses anymore (as long as it takes). Now, when we send a request to the main thread, it delegates a job to a background thread, thus freeing itself for another request. Eventually, a background thread finishes its job and returns it to the main thread. Then the main thread returns the result to the requester. It is very important to understand that if we send a request to an endpoint and it takes the application three or more seconds to process that request, we probably won't be able to execute this request any faster in async mode. It is going to take the same amount of time as the sync request. The only advantage is that in the async mode the main thread won’t be blocked three or more seconds, and thus it will be able to process other requests. Here is the visual representation of the asynchronous workflow: Asynchronous Generic Repository Now that we cleared that out we can learn how to implement the asynchronous code in .NET Core.

      Async, Await Keywords and Return Types

      The async and await keywords play a crucial part in the asynchronous programming. By using those keywords, we can easily write asynchronous methods without too much effort. For example, if we want to create a method in an asynchronous manner, we need to add the async keyword next to the method’s return type:
      async Task<IEnumerable<Owner>> GetAllOwnersAsync()
      By using the async keyword, we are enabling the await keyword and modifying the way of how method results are handled (from synchronous to asynchronous):
      await FindAllAsync();
      In asynchronous programming we have three return types:
      • Task<TResult>, for an async method that returns a value
      • Task, to use it for an async method that does not return a value
      • void, which we can use for an event handler
      What does this mean? Well, we can look at this through the synchronous programming glasses. If our sync method returns  int then in the async mode, it should return Task<int>, or if sync method returns IEnumerable<string> then the async method should return Task<IEnumerable<string>>. But if our sync method returns no value (has a void for the return type), then our async method should usually returnTask. This means that we can use the await keyword inside that method but without the return keyword. You may wonder, why not returning Task all the time? Well, we should use void only for the asynchronous event handlers which require a void return type. Other than that, we should always return a Task. From C# 7.0 onward, we can specify any other return type, if that type includes GetAwaiter method. Now, when we have all the information, let's do some refactoring in our completely synchronous code.

      Overview of the IRepositoryBase Interface and the RepositoryBase Class

      Our complete repository is interface based, so let's take a look at our base interface. In the Contracts project open the IRepositoryBase.cs file. We can see different method signatures:
      public interface IRepositoryBase<T>
      {
          IQueryable<T> FindAll();
          IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression);
          void Create(T entity);
          void Update(T entity);
          void Delete(T entity);
      }
      
      It is important to notice the FindAll and FindByCondition method signatures. Both of them return IQueryable that allows us to attach async calls to them. The Create, Update, and Delete methods  don’t modify any data, they just track changes to an entity and wait for the EF Core’s SaveChanges method to execute. So, they stay unchanged as well. Let's just take a look at the RepositoryBase class:
      public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class
      {
          protected RepositoryContext RepositoryContext { get; set; }
      
          public RepositoryBase(RepositoryContext repositoryContext)
          {
              this.RepositoryContext = repositoryContext;
          }
      
          public IQueryable<T> FindAll()
          {
              return this.RepositoryContext.Set<T>().AsNoTracking();
          }
      
          public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
          {
              return this.RepositoryContext.Set<T>()
                  .Where(expression).AsNoTracking();
          }
      
          public void Create(T entity)
          {
              this.RepositoryContext.Set<T>().Add(entity);
          }
      
          public void Update(T entity)
          {
              this.RepositoryContext.Set<T>().Update(entity);
          }
      
          public void Delete(T entity)
          {
              this.RepositoryContext.Set<T>().Remove(entity);
          }
      }
      
      This class is the implementation of the previous interface and it is a base class for accessing the data from the database.

      Modifying the IOwnerRepository Interface and the OwnerRepository Class

      Now, let’s continue on the other parts of our repository. In the Contracts project, there is also the IOwnerRepository interface with all the synchronous method signatures which we should change too. So let's do that:
      public interface IOwnerRepository : IRepositoryBase<Owner>
      {
          Task<IEnumerable<Owner>> GetAllOwnersAsync();
          Task<Owner> GetOwnerByIdAsync(Guid ownerId);
          Task<Owner> GetOwnerWithDetailsAsync(Guid ownerId);
          void CreateOwner(Owner owner);
          void UpdateOwner(Owner owner);
          void DeleteOwner(Owner owner);
      }
      
      So, we just change signatures of the first three GET methods by assigning the Task<TResult> to the return type. We don't have to change the other three methods because, as we said, they just change the state of the entity and wait for the SaveChanges to execute. Now, we have to implement this interface by using the async and await keywords. Using the await keyword is not mandatory though. Of course, if we don’t use it, our async methods will execute synchronously, and that is not our goal here. So, in accordance with the interface changes, let’s modify our OwnerRepository.cs class, that we may find in the Repository project:
      public class OwnerRepository : RepositoryBase<Owner>, IOwnerRepository 
      { 
          public OwnerRepository(RepositoryContext repositoryContext) 
              : base(repositoryContext) 
          { 
          }
      
          public async Task<IEnumerable<Owner>> GetAllOwnersAsync() 
          { 
               return await FindAll()
                  .OrderBy(ow => ow.Name)
                  .ToListAsync(); 
          }
      
          public async Task<Owner> GetOwnerByIdAsync(Guid ownerId)
          {
              return await FindByCondition(owner => owner.Id.Equals(ownerId))
                  .FirstOrDefaultAsync();
          }
      
          public async Task<Owner> GetOwnerWithDetailsAsync(Guid ownerId)
          {
              return await FindByCondition(owner => owner.Id.Equals(ownerId))
                  .Include(ac => ac.Accounts)
                  .FirstOrDefaultAsync();
          }
      
          public void CreateOwner(Owner owner)
          {
              Create(owner);
          }
      
          public void UpdateOwner(Owner owner)
          {
              Update(owner);
          }
      
          public void DeleteOwner(Owner owner)
          {
              Delete(owner);
          }
      }

      IRepositoryWrapper and RepositoryWrapper Changes

      We have to modify the Save method in the mentioned interface and the class as well:
      public interface IRepositoryWrapper 
      { 
          IOwnerRepository Owner { get; } 
          IAccountRepository Account { get; } 
          Task SaveAsync(); 
      }
      And let's just modify the Save method in the RepositoryWrapper class:
      public async Task SaveAsync() 
      {
          await _repoContext.SaveChangesAsync();
      }
      Now, we can continue to the controller modification.

      Controller Modification

      Finally, we need to modify all of our actions in the OwnerController to work asynchronously. So, let’s first start with the GetAllOwners method:
      [HttpGet] 
      public async Task<IActionResult> GetAllOwners() 
      { 
          try 
          { 
              var owners = await _repository.Owner.GetAllOwnersAsync(); 
              _logger.LogInfo($"Returned all owners from database.");
      
              var ownersResult = _mapper.Map<IEnumerable<OwnerDto>>(owners);
              return Ok(ownersResult); 
          } 
          catch (Exception ex) 
          { 
              _logger.LogError($"Something went wrong inside GetAllOwners action: {ex.Message}"); 
              return StatusCode(500, "Internal server error"); 
          } 
       }
      
      We haven't changed much in this action. We've just changed the return type and added the async keyword to the method signature. In the method body, we can now await the GetAllOwnersAsync() method. And that is pretty much what we should do in all the actions in our controller. So let’s modify all the other actions. GetOwnerById:
      [HttpGet("{id}", Name = "OwnerById")] 
      public async Task<IActionResult> GetOwnerById(Guid id) 
      { 
          try 
          { 
              var owner = await _repository.Owner.GetOwnerByIdAsync(id); 
              if (owner == null) 
              { 
                  _logger.LogError($"Owner with id: {id}, hasn't been found in db."); 
                  return NotFound(); 
              } 
              else 
              { 
                  _logger.LogInfo($"Returned owner with id: {id}");
      
                  var ownerResult = _mapper.Map<OwnerDto>(owner);
                  return Ok(ownerResult); 
              } 
          } 
          catch (Exception ex) 
          { 
              _logger.LogError($"Something went wrong inside GetOwnerById action: {ex.Message}"); 
              return StatusCode(500, "Internal server error"); 
          } 
      }
      
      GetOwnerWithDetails:
      [HttpGet("{id}/account")] 
      public async Task<IActionResult> GetOwnerWithDetails(Guid id) 
      { 
          try 
          { 
      	var owner = await _repository.Owner.GetOwnerWithDetailsAsync(id); 
      	if (owner == null) 
      	{ 
      	    _logger.LogError($"Owner with id: {id}, hasn't been found in db."); 
      	    return NotFound(); 
      	} 
      	else 
      	{ 
      	    _logger.LogInfo($"Returned owner with details for id: {id}");
      
      	    var ownerResult = _mapper.Map<OwnerDto>(owner);
      	    return Ok(ownerResult); 
      	} 
      } 
          catch (Exception ex) 
          { 
              _logger.LogError($"Something went wrong inside GetOwnerWithDetails action: {ex.Message}"); 
      	return StatusCode(500, "Internal server error"); 
          }
      }
      
      CreateOwner:
      [HttpPost]
      public async Task<IActionResult> CreateOwner([FromBody]OwnerForCreationDto owner)
      {
          try
          {
      	if (owner == null)
      	{
      	    _logger.LogError("Owner object sent from client is null.");
      	    return BadRequest("Owner object is null");
      	}
      
      	if (!ModelState.IsValid)
      	{
      	    _logger.LogError("Invalid owner object sent from client.");
      	    return BadRequest("Invalid model object");
      	}
      
      	var ownerEntity = _mapper.Map<Owner>(owner);
      
      	_repository.Owner.CreateOwner(ownerEntity);
      	await _repository.SaveAsync();
      
      	var createdOwner = _mapper.Map<OwnerDto>(ownerEntity);
      
      	return CreatedAtRoute("OwnerById", new { id = createdOwner.Id }, createdOwner);
          }
          catch (Exception ex)
          {
          	_logger.LogError($"Something went wrong inside CreateOwner action: {ex.Message}");
      	return StatusCode(500, "Internal server error");
          }
      }
      UpdateOwner:
      [HttpPut("{id}")]
      public async Task<IActionResult> UpdateOwner(Guid id, [FromBody]OwnerForUpdateDto owner)
      {
          try
          {
              if (owner == null)
      	{
                  _logger.LogError("Owner object sent from client is null.");
      	    return BadRequest("Owner object is null");
      	}
      
      	if (!ModelState.IsValid)
      	{
      	    _logger.LogError("Invalid owner object sent from client.");
      	    return BadRequest("Invalid model object");
      	}
      
      	var ownerEntity = await _repository.Owner.GetOwnerByIdAsync(id);
      	if (ownerEntity == null)
      	{
      	    _logger.LogError($"Owner with id: {id}, hasn't been found in db.");
      	    return NotFound();
      	}
      
      	_mapper.Map(owner, ownerEntity);
      
      	_repository.Owner.UpdateOwner(ownerEntity);
      	await _repository.SaveAsync();
      
      	return NoContent();
          }
          catch (Exception ex)
          {
        	_logger.LogError($"Something went wrong inside UpdateOwner action: {ex.Message}");
      	return StatusCode(500, "Internal server error");
          }
      }
      
      DeleteOwner:
      [HttpDelete("{id}")]
      public async Task<IActionResult> DeleteOwner(Guid id)
      {
          try
          {
              var owner = await _repository.Owner.GetOwnerByIdAsync(id);
              if (owner == null)
              {
                  _logger.LogError($"Owner with id: {id}, hasn't been found in db.");
                  return NotFound();
              }
      
              if (_repository.Account.AccountsByOwner(id).Any()) 
              {
                  _logger.LogError($"Cannot delete owner with id: {id}. It has related accounts. Delete those accounts first"); 
                  return BadRequest("Cannot delete owner. It has related accounts. Delete those accounts first"); 
              }
      
              _repository.Owner.DeleteOwner(owner);
              await _repository.SaveAsync();
      
              return NoContent();
          }
          catch (Exception ex)
          {
              _logger.LogError($"Something went wrong inside DeleteOwner action: {ex.Message}");
              return StatusCode(500, "Internal server error");
          }
      }
      
      Excellent. Now we are talking async :D We can make our actions even more maintainable and readable by implementing Global Error Handling to remove try-catch blocks and Action Filters as well to remove validation code repetitions.

      Conclusion

      There you go. We have seen how easy is to convert the synchronous repository to asynchronous and how easy is to write async code overall. With a couple of changes, we gave more breathing space to our API and created a more responsive application. It is a good practice to write async methods (when we have an opportunity) that handles the I/O actions because it is easy to write those methods and the benefits are indisputable. Thank you for reading the article and we hope you found something useful in it. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      3339 0 0 0 218 https://www.alvinashcraft.com/2018/06/25/dew-drop-june-25-2018-2752/ 0 0 241 0 0 271 0 0 272 271 0 274 272 0 396 0 0 397 396 0 491 490 0 490 0 0 493 491 0 494 491 0 495 494 0 905 0 0 906 905 0 1098 0 0 1099 1098 0 1118 _dbContext.Entry(dbClient).CurrentValues.SetValues(client); await _db.SaveChangesAsync(); Is that why you remove the notracking from async Find methods ? the Map method that you use do something like that?]]> 0 0 1119 241 0 1120 1118 0 1516 0 0 1518 1516 0 1325 0 0 1326 1325 0 1327 1326 0 1328 1327 0 1329 1328 0 1528 1518 0 1531 1528 0
      React - Dynamic Form Creation and Modal Components https://code-maze.com/react-dotnetcore-dynamic-forms-modals/ Wed, 13 Jun 2018 05:30:59 +0000 https://code-maze.com/?p=3407 the following link: Introduction to the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the React series, check out: Introduction to the React series. For the previous part check out: React Error Handling The source code is available at GitHub: React series - react-series-part6-end branch This post is divided into several sections:

      Success and Error Modal Window Components

      Let's start by creating the following folder structure in the component folder:
      Modal folder structre - Dynamic Form Creation
      First, we are going to modify the SuccessModal.js file:
      import React from 'react';
      import Aux from '../../../hoc/Auxiliary/Auxiliary';
      import { Modal, Button } from 'react-bootstrap';
      import '../ModalStyles.css';
      
      const successModal = (props) => {
          return (
              <Aux>
                  <Modal show={props.show} backdrop='static'>
                      <Modal.Header>
                          {props.modalHeaderText}
                      </Modal.Header>
                      <Modal.Body>
                          <p>{props.modalBodyText}</p>
                      </Modal.Body>
                      <Modal.Footer>
                          <Button bsStyle="success" onClick={props.successClick}>{props.okButtonText}</Button>
                      </Modal.Footer>
                  </Modal>
              </Aux>
          )
      }
       
      export default successModal;
      This code is pretty straightforward. We use the react-bootstrap components for the modal inside the functional component. Through the props object, we send different parameters to our modal and one event as well. This event is going to close this modal once we click on the button. Let’s modify the ErrorModal.js file in the same manner:
      import React from 'react';
      import { Modal, Button } from 'react-bootstrap';
      import Aux from '../../../hoc/Auxiliary/Auxiliary';
      import '../ModalStyles.css';
      
      const errorModal = (props) => {
          return (
              <Aux>
                  <Modal show={props.show} backdrop='static'>
                      <Modal.Header>
                          {props.modalHeaderText}
                      </Modal.Header>
                      <Modal.Body>
                          <p>{props.modalBodyText}</p>
                      </Modal.Body>
                      <Modal.Footer>
                          <Button bsStyle="danger" onClick={props.closeModal}>{props.okButtonText}</Button>
                      </Modal.Footer>
                  </Modal>
              </Aux>
          )
      }
       
      export default errorModal;
      
      Finally, we need to modify the ModalStyles.css file:
      .modal-header {
          margin: 0;
          line-height: 1.42857143;
          font-size: 30px;
          text-align: center;
      }
      
      .modal-body p {
          text-align: center;
          margin-top: 10px;
      }
      
      @media (min-width: 768px){
          .modal-dialog {
              width: 500px !important;
              margin: 20% auto !important;
          }
      }
      
      Excellent. Now we have our modal components which we can use for the Create, Update or any other component. We have deliberately created two modal components, even though they are almost the same because it is much more readable when we use modal components inside the parent components. You can distinguish right away what is the purpose of the modal just by looking at its name.

      Creating Input Configuration

      Inside the src folder, we are going to create the Utility folder and inside it a new file InputConfiguration.js. In this file, we are going to store all of our configuration settings for the input elements (name, address, dateOfBirth) which we are going to use in the create and update forms. We have already installed the moment library, in the previous post, but if you haven't done it, please do it (we need it for the datepicker control) with the following command:
      npm install --save moment
      Now let’s modify the InputConfiguration.js file:
      import moment from 'moment';
      
      export const returnInputConfiguration = () => {
          return {
              name: {
                  element: 'input', type: 'text', value: '', 
                  validation: { required: true }, valid: false, touched: false,
                  errorMessage: '', label: 'Name:'
              },
              address: {
                  element: 'input', type: 'text', value: '', 
                  validation: { required: true, maxLength: 60 }, valid: false, touched: false,
                  errorMessage: '', label: 'Address:'
              },
              dateOfBirth: {
                  element: 'datePicker', type: 'text', value: moment(), 
                  valid: true, touched: false,
                  errorMessage: '', label: 'Date of birth:'
              }
          }
      }
      
      Later on, in our create component, we are going to transform this object into an array of objects and send it to the Input component to create all of the input fields we need on the form. That array will consist of the objects (key-value pairs) where the key is going to be the name or the address or the dateOfBirth (properties from the above object) and the value is going to be the complete configuration part of the same object (type, value, element…).

      Creating Inputs Elements Dynamically

      The first thing we need to do is to install the react-datepicker library because we are going to use it for the dateOfBirth control. Let’s execute the command:
      npm install react-datepicker@1.4.0 --save
      React datepicker - Dynamic Form Creation
      In the src folder let’s create the UI folder. Inside it, we are going to create a new folder Input and inside it the Input.js and the Input.css files:
      UI input folder structure - Dymanic Form Creation
      The Input.js file is going to be a functional component so let’s modify it accordingly:
      import React from 'react';
      import Aux from '../../hoc/Auxiliary/Auxiliary';
      import { FormGroup, Col, FormControl, ControlLabel } from 'react-bootstrap';
      import DatePicker from 'react-datepicker';
      import 'react-datepicker/dist/react-datepicker.css';
      import './Input.css';
      
      const input = (props) => {
          let inputField = null;
          let errorMessage = null;
      
          if(props.invalid && props.shouldValidate && props.touched){
              errorMessage = (<em>{props.errorMessage}</em>);
          }
          
          return (
              <Aux>
                  {inputField}
              </Aux>
          )
      }
       
      export default input;
      
      For now, we are just going to import all the resources we need, initialize the input field and the error message and set up the value for that message if the control is invalid and if it should be validated and if it is touched. We don’t want to show the error if the user didn’t place the mouse cursor inside that component at all. Lastly, we are returning that input field (which is null for now) to the parent component. Below the if statement and above the return block, we are going to add the code for populating the inputField property:
      switch (props.elementType) {
          case 'input':
              inputField = (
                  <FormGroup controlId={props.id}>
                      <Col componentClass={ControlLabel} sm={2}>
                          {props.label}
                      </Col>
                      <Col sm={6}>
                          <FormControl type={props.type} value={props.value} onChange={props.changed} onBlur={props.blur} />
                      </Col>
                      <Col>
                          <em>{errorMessage}</em>
                      </Col>
                  </FormGroup>
              )
              break;
          case 'datePicker':
              inputField = (
                  <FormGroup controlId={props.id}>
                      <Col componentClass={ControlLabel} sm={2}>
                          {props.label}
                      </Col>
                      <Col sm={6}>
                          <DatePicker selected={props.value} dateFormat="MM/DD/YYYY" readOnly
                              onChange={props.changed} className='datePickerControl' 
                              showYearDropdown dropdownMode="select"/>
                      </Col>
                      <Col>
                          <em>{errorMessage}</em>
                      </Col>
                  </FormGroup>
              )
              break;
          default: inputField = null;
      }
      
      So, we switch through the element type and if it is the input type we create the input field with all the properties and events it needs. We are doing the same thing for the datePicker control. For our forms, those two input types are going to be enough, but if you for any of your projects need more controls, just simply add additional case statement. One thing left to do is to modify the Input.css file:
      em{
          color: red;
          margin: 5px 0;
      }
      
      .react-datepicker__day-name, .react-datepicker__day, .react-datepicker__time-name {
      
          display: inline-block;
          width: 30px!important;
          height: 30px;
          line-height: 30px;
          text-align: center;
          margin: 0.166rem;
          font-size: 14px;
      }
      
      .react-datepicker__month-container select {
          font-size: 12px!important;
      }
      
      .datePickerControl {
          display: block;
          width: 100%;
          height: 34px;
          padding: 6px 12px;
          font-size: 14px;
          line-height: 1.42857143;
          color: #555;
          background-color: #fff;
          background-image: none;
          border: 1px solid #ccc;
          border-radius: 4px;
      }
      
      .react-datepicker__input-container {
          position: relative;
          display: inline-block;
          width: 100%;
      }
      .react-datepicker-wrapper {
          display: inline-block;
          width: 100%;
      }
      
      In this class, we are overriding some of the datePicker native classes and adding one custom class (.datePickerControl). That’s it, we can now continue to the CreateOwner component.

      CreateOwner Component

      In the containers folder and then inside the Owner folder, let’s create a new folder and name it CreateOwner. Inside create a new file CreateOwner.js. Let’s start modifying that file:
      import React, { Component } from 'react';
      import Input from '../../../UI/Inputs/Input';
      import { Form, Well, Button, FormGroup, Col } from 'react-bootstrap';
      import { returnInputConfiguration } from '../../../Utility/InputConfiguration';
      
      class CreateOwner extends Component {
          state = {
              ownerForm: {},
              isFormValid: false
          }
      
          componentWillMount = () =>{
              this.setState({ ownerForm: returnInputConfiguration() });
          }
          
          render() { 
              return ( 
                  <Well>
      
                  </Well>
               )
          }
      }
       
      export default CreateOwner;
      
      In the code above, we import all the necessary react-bootstrap components and the returnInputConfiguration function from the InputConfiguration.js file. This component is a class component or a stateful component and in the componentWillMount lifecycle hook, we update our local state with all the form configuration. The compnentWillMount hook is going to trigger immediately before the component mounts. Let’s add another line of code between the render and return blocks:
      const formElementsArray = formUtilityActions.convertStateToArrayOfFormObjects({ ...this.state.ownerForm });
      In the convertStateToArrayOfFormObjectsfuncition, we want to convert the ownerForm object into an array of objects to send it to the Input component. So, let's add that function in a separate file. Inside the Utility folder, create a new file FormUtility.js. Modify that file by adding the function for the object transformation:
      export const convertStateToArrayOfFormObjects = (formObject) => {
          const formElementsArray = [];
          for (let key in formObject) {
              formElementsArray.push({
                  id: key,
                  config: formObject[key]
              });
          }
      
          return formElementsArray;
      }
      
      Now we need to import this function inside CreateOwner.js file:
      import * as formUtilityActions from '../../../Utility/FormUtility';
      We are going to have more actions inside FormUtility.js file therefore, we are importing all of those actions in the CreateOwner.js component. We have populated the formElementsArray, so let's use it to send all the properties towards the Input component.

      Form Elements Configuration Object

      Let’s add this code to the Well tag:
      <Form horizontal onSubmit={this.createOwner}>
          {
                formElementsArray.map(element => {
                      return <Input key={element.id} elementType={element.config.element} 
                      id={element.id} label={element.config.label}
                      type={element.config.type} value={element.config.value} 
                      changed={(event) => this.handleChangeEvent(event, element.id)}
                      errorMessage={element.config.errorMessage} 
                      invalid={!element.config.valid} shouldValidate={element.config.validation}
                      touched={element.config.touched} 
                      blur={(event) => this.handleChangeEvent(event, element.id)} />
              })
           }
          <br />
      </Form>
      
      In this code, we are looping through all the objects (input configurations) inside the formElementsArray and returning the Input component with all the necessary properties and events it requires. There is a function handleChangeEvent, and we are going to create this function to enable validation and two-way binding. But more on that a little bit later. To see the result of our current actions, let's modify the App.js file by adding another route to the CreateOwner component below the OwnerDetails route. We shouldn't forget the import statement as well:
      import CreateOwner from './Owner/CreateOwner/CreateOwner';
      <Route path="/createOwner" component={CreateOwner} />
      If we navigate to the CreateOwner page we are going to see this result:
      Create from started - Dynamic Form Creation
      Fantastic. We have created our controls, but we still can’t do anything with them yet. But as soon as we implement the handleChangeEvent function, we will be able to modify and validate our inputs.

      Finalizing the CreateOwner View

      Let’s add the buttons to our component, to finalize the view part. Inside the Form tag and below the <br> tag add this code:
      <FormGroup>
           <Col mdOffset={6} md={1}>
                <Button type='submit' bsStyle='info' disabled={!this.state.isFormValid}>Create</Button>
           </Col>
           <Col md={1}>
                 <Button bsStyle='danger' onClick={this.redirectToOwnerList}>Cancel</Button>
           </Col>
      </FormGroup>
      
      We add two buttons, and the Create button is disabled as long as the form is invalid. Excellent. Now our form looks like this:
      Create form completed - Dynamic Form Creation
      Everything is working as it's supposed to.

      Conclusion

      By reading this post, you've learned:
      • How to use the Bootstrap modal elements to create reusable modal window components
      • To use the simple configuration object for form creation and how to transfer it into an array of objects
      • How to transfer a configuration array by using a simple JSX code to a Form View
      Thank you for reading the article and I hope you found something useful in it. In the next part of the series, we are going to learn how to validate our input elements. Furthermore, we are going to use the CreateComponent to send a POST request towards our .NET Core Web API server. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      3407 0 0 0 207 https://www.alvinashcraft.com/2018/06/13/dew-drop-june-13-2018-2745/ 0 0
      React - Form Validation and Handling POST Request https://code-maze.com/react-dotnetcore-form-validation-post-request/ Wed, 20 Jun 2018 07:14:21 +0000 https://code-maze.com/?p=3475 handleChangeEvent() function to the Input component. We are going to use this function to enable two-way binding and to validate our input fields. Therefore, the implementation and sending POST request is what we aim for in this post. If you want to see all the basic instructions and complete navigation for the .NET Core series, check out the following link: Introduction to the .NET Core series. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the React series, check out: Introduction to the React series. For the previous part check out: Dynamic Form Configuration The source code is available at GitHub: React series - react-series-part7-end branch This post is divided into several sections:

      Handling OnChange Event with the handleChangeEvent Function

      Right now if we type something inside the input fields nothing is going to happen. That is because, in our InputConfiguration.js file all “value” properties are set to empty strings for all the inputs (except for the datePicker, it accepts the moment() function which always applies the current date). More important is that we never change the value property inside the configuration object, thus the empty input fields. We are going to fix this and to add a validation, by implementing the handleChangeEvent function. Let’s modify the CreateOwner.js file by adding the handleChangeEvent function below the componentWillMount lifecycle hook:
      handleChangeEvent = (event, id) => {
          const updatedOwnerForm = { ...this.state.ownerForm };
          updatedOwnerForm[id] = formUtilityActions.executeValidationAndReturnFormElement(event, updatedOwnerForm, id);
      
          const counter = formUtilityActions.countInvalidElements(updatedOwnerForm);
      
          this.setState({ ownerForm: updatedOwnerForm, isFormValid: counter === 0 })
      }
      
      This function accepts two parameters. The event parameter that holds the value from our input fields (not only the value but this one is important for us) and the id parameter to which we are going to pass one of the configuration object keys (name, address or dateOfBirth). We are deeply cloning the ownerForm from the state, and then updating the configuration of the currently modified input by calling the executeValidationAndReturnFormElement function. After the validation, we are counting how many fields are invalid, and if any, our complete form is going to be marked as invalid. Finally, we are going to update the state.

      Creating Additional Functions for React Form Validation

      We have executeValidationAndReturnFormElement and countInvalidElements functions but they are not created yet. So, let’s create them. Let’s modify the FormUtility.js file:
      export const executeValidationAndReturnFormElement = (event, updatedOwnerForm, id) => {
          let formElement = { ...updatedOwnerForm[id] };
          formElement.value = id === 'dateOfBirth' ? event : event.target.value;
          formElement.touched = true;
      
          const validationResponse = checkValidity(formElement.value, formElement.validation);
      
          formElement.valid = validationResponse.isValid;
          formElement.errorMessage = validationResponse.errorMessage;
      
          return formElement;
      }
      
      First, we are cloning the exact formElement which user currently modifies. Then populate the formElement's value by using a ternary operator (If a user modifies the dateOfBirth input, then the event is the value. But for any other input field, the event is the object that has the target property which holds the value property). Furthermore, we are going to set our control to touched and then send the value and the validation rules to the validation process. Once the process is done we store those results. Finally, we return formElement with all the changes.

      Implementing the CheckValidity Function

      So, let's implement the checkValidity function just above the executeValidationAndReturnFormElement function:
      const checkValidity = (value, validation) => {
          let validationObject = {
              isValid: true,
              errorMessage: ''
          };
      
          if (validation) {
              if (validation.required) {
                  validationObject.isValid = value.trim() !== '';
                  validationObject.errorMessage = validationObject.isValid ? '' : 'Field is required';
              }
      
              if (validationObject.isValid && validation.maxLength) {
                  validationObject.isValid = value.length <= 60;
                  validationObject.errorMessage = 'Not allowed more than 60 charactes';
              }
      
              return validationObject;
          }
          else {
              return validationObject;
          }
      }
      
      With the code above we check if we need to validate the input at all. If it needs to be validated then we perform validation but if it doesn’t (like dateOfBirth) we just return the same object back. In the validation process, we check does the field have the required validation rule and validate it. Then if it is still valid we validate against the maxLength rule. Finally, we just return the validationObject.

      Entire React Form Validation

      Inside the handleChangeEvent function, we are calling one additional function the countInvalidElements. So let’s implement that one just below the executeValidationAndReturnFormElement function in the FormUtility.js file:
      export const countInvalidElements = (ownerForm) => {
          let countInvalidElements = 0;
          for (let element in ownerForm) {
              if (!ownerForm[element].valid) {
                  countInvalidElements = countInvalidElements + 1;
                  break;
              }
          }
          return countInvalidElements;
      }
      This function is pretty straightforward. We loop through all the elements inside the ownerForm. And if we find any element which is invalid, we just increase the countInvalidElements variable and leave the loop. Finally, we return that variable. Excellent. Now we can inspect the results. Empty form (that has never been touched): Empty Form React Form Validation If we place our cursor inside our input fields and leave: Touched Form React Form Validation Another type of error message: React Form Validation Max Character Length Finally, the valid form: Create Form Valid React Form Validation

      Connect the CreateOwner Component With the Reducer

      First, let’s add all the necessary imports:
      import { connect } from 'react-redux';
      import * as repositoryActions from '../../../store/actions/repositoryActions';
      import * as errorHandlerActions from '../../../store/actions/errorHandlerActions';
      
      Next, let’s create mapStateToProps function below the component:
      const mapStateToProps = (state) => {
          return {
              showSuccessModal: state.repository.showSuccessModal,
              showErrorModal: state.errorHandler.showErrorModal,
              errorMessage: state.errorHandler.errorMessage
          }
      }
      
      After that, we need to create mapDispatchToProps function below the mapStateToProps:
      const mapDispatchToProps = (dispatch) => {
          return {
              onCreateOwner: (url, owner, props) => dispatch(repositoryActions.postData(url, owner, props)),
              onCloseSuccessModal: (url, props) => dispatch(repositoryActions.closeSuccessModal(props, url)),
              onCloseErrorModal: () => dispatch(errorHandlerActions.closeErrorModal())
          }
      }
      
      Then, let’s modify the export statement:
      export default connect(mapStateToProps, mapDispatchToProps)(CreateOwner);
      Finally, we are going to add our modal components below the FormGroup tag but still inside the Well tag:
      <SuccessModal show={this.props.showSuccessModal} 
             modalHeaderText={'Success message'} 
             modalBodyText={'Action completed successfully'}
             okButtonText={'OK'} 
             successClick={() => this.props.onCloseSuccessModal('/owner-List', { ...this.props })} />
      
      <ErrorModal show={this.props.showErrorModal} 
              modalHeaderText={'Error message'} 
              modalBodyText={this.props.errorMessage}
              okButtonText={'OK'} closeModal={() => this.props.onCloseErrorModal()} />
      
      And, add the import statements:
      import SuccessModal from '../../../components/Modals/SuccessModal/SuccessModal';
      import ErrorModal from '../../../components/Modals/ErrorModal/ErrorModal';
      
      The SuccessModal is calling the onCloseSuccessModal property from the mapDispatchToProps function. Moreover, the ErrorModal is calling the onCloseErrorModal property from the mapDispatchToProps function. Both of those properties are dispatching functions which are not implemented. So this is something we need to fix.

      Modifying the Action and the Reducer files

      We need to modify our files by adding additional functions for closing success and error modal components. Let’s add two more action types inside the ActionTypes.js file:
      export const CLOSE_ERROR_MODAL = 'CLOSE_ERROR_MODAL';
      export const CLOSE_SUCCESS_MODAL = 'CLOSE_SUCCESS_MODAL';
      
      Then let’s modify the repositoryActions.js file by adding the closeSuccessModal function:
      export const closeSuccessModal = (props, url) =>{
          return {
              type: actionTypes.CLOSE_SUCCESS_MODAL,
              props: props,
              url: url
          }
      }
      
      We need to modify the repositoryReducer.js file as well:
      case actionTypes.CLOSE_SUCCESS_MODAL:
                  return executeCloseSuccessModal(state, action)
      
      const executeCloseSuccessModal = (state, action) => {
          action.props.history.push(action.url);
          return {
              ...state,
              showSuccessModal: false
          }
      }
      
      We are done with the success modal, let’s do the similar thing for the error modal. First, let’s modify the errorHandlerActions.js file by adding a new function:
      export const closeErrorModal = () => {
          return {
              type: actionTypes.CLOSE_ERROR_MODAL
          }
      }
      
      Then, let’s modify the errorHandlerReducer.js file:
      case actionTypes.CLOSE_ERROR_MODAL:
                  return executeCloseErrorModal(state, action);
      
      const executeCloseErrorModal = (state, action) => {
          return{
              ...state,
              showErrorModal: false,
              errorMessage: ''
          }
      }
      
      Now, all we have to do is to send a POST request from the CreateOwner component.

      Handling POST Request

      There is onSubmit event inside the Form tag, which points to the createOwner function. Let’s implement that function below the handleChangeEvent function:
      createOwner = (event) => {
          event.preventDefault();
      
          const ownerToCreate = {
              name: this.state.ownerForm.name.value,
              address: this.state.ownerForm.address.value,
              dateOfBirth: this.state.ownerForm.dateOfBirth.value
          }
      
          const url = '/api/owner';
          this.props.onCreateOwner(url, ownerToCreate, { ...this.props });
      }
      
      Below this function we need to add one more function for the Cancel button functionality:
      redirectToOwnerList = () => {
          this.props.history.push('/owner-List');
      }
      
      Now we can check out the results: Success modal window If the error occurs, with the status code other than 404 or 500: error modal bad request

      Conclusion

      By reading this post you have learned:
      • How to handle the onChange event in an input field
      • To validate our dynamically created form
      • How to use previously created modal window components
      • The way to handle POST request
      Thank you for reading the article and I hope you found something useful in it. In the next part of the series, we are going to learn how to handle PUT requests. Furthermore, we are going to use different lifecycle hooks in React and learn how to update our state with the modified data from the server. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      3475 0 0 0 215 https://www.alvinashcraft.com/2018/06/21/dew-drop-june-21-2018-2750/ 0 0 216 http://andrey.moveax.ru/post/interesting-things-118-2018-06-22 0 0 1016 0 0 1395 0 0 1396 1395 0
      How to Easily Create a PDF Document in ASP.NET Core Web API https://code-maze.com/create-pdf-dotnetcore/ Mon, 18 Jun 2018 06:31:48 +0000 https://code-maze.com/?p=3498 DinkToPDF library to easily generate PDF documents while working on the .NET Core Web API project. So, without further ado, let’s dive right into the fun part. [sc name="part_of_series" headline="Recommended Articles"] You can download the source code for this article at Creating PDF Document Source Code. In this post, we are going to cover:

      Basic Project Preparations

      Let’s start, by creating a brand new .NET Core 3.0 Web API project named PDF_Generator: Creating project for .NET Core Web API PDF Document Creation After the project creation, we are going to modify the launchSettings.json file to disable our browser to start automatically:
      {
        "$schema": "http://json.schemastore.org/launchsettings.json",
        "iisSettings": {
          "windowsAuthentication": false,
          "anonymousAuthentication": true,
          "iisExpress": {
            "applicationUrl": "http://localhost:65151",
            "sslPort": 44381
          }
        },
        "profiles": {
          "PDF_Generator": {
            "commandName": "Project",
            "launchBrowser": false,
            "applicationUrl": "https://localhost:5001;http://localhost:5000",
            "environmentVariables": {
              "ASPNETCORE_ENVIRONMENT": "Development"
            }
          }
        }
      }
      

      DinkToPdf Library Configuration

      DinkToPdf is a cross-platform oriented library which is the wrapper for the Webkit HTML to PDF library. It uses the WebKit engine to convert HTML to PDF. It will allow us to create a PDF document from our HTML string that we generate in the .NET Core project, or to create a PDF document from an existing HTML page. Furthermore, we can download the created PDF document or save it on a certain location or return a new HTML page with the PDF content. We are going to cover all these features in this article. So, let’s install the DinkToPdf library first:
      PM> Install-Package DinkToPdf
      Or search for DinkToPdf inside the Nuget Package window: Nuget package .NET Core PDF Creation After the installation completes, we need to import native library files to our root project. We can find those files in our source project in the NativeLibrary folder. Inside we will find two folders 32bit and 64bit, so we need to choose the appropriate library for our OS. We are going to choose the files from the 64bit folder: Native library files Finally, we need to register this library with our IoC container in the StartUp class:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools()));
      
          services.AddControllers();
      }
      
      To learn in more detail about service registration in .NET Core and how to keep Startup methods cleaner, you can read the .NET Core Service Configuration. Excellent. We have everything in place and we are ready to proceed.

      Preparing Data for the PDF Document

      In a real-world project, we can collect data from the database or receive it from other API. But for the sake of simplicity, we are going to collect data for our PDF document from the local storage. Then we are going to create an HTML template and store it in the PDF document. So let’s first create a new folder Models and inside it the Employee.cs file:
      namespace PDF_Generator.Models
      {
          public class Employee
          {
              public string Name { get; set; }
              public string LastName { get; set; }
              public int Age { get; set; }
              public string Gender { get; set; }
          }
      }
      
      To continue, we are going to create a new folder Utility and two class files inside it DataStoage.cs and TemplateGenerator.cs. A complete structure should look like this: Utility Folder Structure Now, let’s modify the DataStorage.cs file:
      using PDF_Generator.Models;
      using System.Collections.Generic;
      
      namespace PDF_Generator.Utility
      {
          public static class DataStorage
          {
              public static List<Employee> GetAllEmployess() =>
                  new List<Employee>
                  {
                      new Employee { Name="Mike", LastName="Turner", Age=35, Gender="Male"},
                      new Employee { Name="Sonja", LastName="Markus", Age=22, Gender="Female"},
                      new Employee { Name="Luck", LastName="Martins", Age=40, Gender="Male"},
                      new Employee { Name="Sofia", LastName="Packner", Age=30, Gender="Female"},
                      new Employee { Name="John", LastName="Doe", Age=45, Gender="Male"}
                  };
          }
      }
      In this code, we just return a list of employees that will be displayed inside the HTML template.

      HTML Template Generation

      We want to generate an HTML template, so we need to modify the TemplateGenerator.cs file:
      using System.Text;
      
      namespace PDF_Generator.Utility
      {
          public static class TemplateGenerator
          {
              public static string GetHTMLString()
              {
                  var employees = DataStorage.GetAllEmployess();
      
                  var sb = new StringBuilder();
                  sb.Append(@"
                              <html>
                                  <head>
                                  </head>
                                  <body>
                                      <div class='header'><h1>This is the generated PDF report!!!</h1></div>
                                      <table align='center'>
                                          <tr>
                                              <th>Name</th>
                                              <th>LastName</th>
                                              <th>Age</th>
                                              <th>Gender</th>
                                          </tr>");
      
                  foreach (var emp in employees)
                  {
                      sb.AppendFormat(@"<tr>
                                          <td>{0}</td>
                                          <td>{1}</td>
                                          <td>{2}</td>
                                          <td>{3}</td>
                                        </tr>", emp.Name, emp.LastName, emp.Age, emp.Gender);
                  }
      
                  sb.Append(@"
                                      </table>
                                  </body>
                              </html>");
      
                  return sb.ToString();
              }
          }
      }
      
      So, we are fetching data from our static DataStorage class and fill our template with it. The HTML template is nothing more than a pure HTML code. But we want to style our table and h1 tag as well, so let’s create the new folder assets and inside it the new styles.css file and modify it:
      .header {
          text-align: center;
          color: green;
          padding-bottom: 35px;
      }
      
      table {
          width: 80%;
          border-collapse: collapse;
      }
      
      td, th {
          border: 1px solid gray;
          padding: 15px;
          font-size: 22px;
          text-align: center;
      }
      
      table th {
          background-color: green;
          color: white;
      }
      
      This CSS file is going to be loaded later in the Controller class. That is it, we have our HTML template to use for the PDF creation. Now, we can continue to the Controller logic.

      Saving the PDF Document on the Local Storage

      In the Controllers folder, we are going to create a new empty API controller PdfCreatorController:
      namespace PDF_Generator.Controllers
      {
          [Route("api/pdfcreator")]
          [ApiController]
          public class PdfCreatorController : ControllerBase
          {
          }
      }
      
      Now, let’s modify the PdfCreatorController class to support the creation and saving a PDF document to a local drive:
      using DinkToPdf;
      using DinkToPdf.Contracts;
      using Microsoft.AspNetCore.Mvc;
      using PDF_Generator.Utility;
      using System.IO;
      
      namespace PDF_Generator.Controllers
      {
          [Route("api/pdfcreator")]
          [ApiController]
          public class PdfCreatorController : ControllerBase
          {
              private IConverter _converter;
      
              public PdfCreatorController(IConverter converter)
              {
                  _converter = converter;
              }
      
              [HttpGet]
              public IActionResult CreatePDF()
              {
                  var globalSettings = new GlobalSettings
                  {
                      ColorMode = ColorMode.Color,
                      Orientation = Orientation.Portrait,
                      PaperSize = PaperKind.A4,
                      Margins = new MarginSettings { Top = 10 },
                      DocumentTitle = "PDF Report",
                      Out = @"D:\PDFCreator\Employee_Report.pdf"
                  };
      
                  var objectSettings = new ObjectSettings
                  {
                      PagesCount = true,
                      HtmlContent = TemplateGenerator.GetHTMLString(),
                      WebSettings = { DefaultEncoding = "utf-8", UserStyleSheet =  Path.Combine(Directory.GetCurrentDirectory(), "assets", "styles.css") },
                      HeaderSettings = { FontName = "Arial", FontSize = 9, Right = "Page [page] of [toPage]", Line = true },
                      FooterSettings = { FontName = "Arial", FontSize = 9, Line = true, Center = "Report Footer" }
                  };
      
                  var pdf = new HtmlToPdfDocument()
                  {
                      GlobalSettings = globalSettings,
                      Objects = { objectSettings }
                  };
      
                  _converter.Convert(pdf);
      
                  return Ok("Successfully created PDF document.");
              }
          }
      }
      

      Code Explanation

      In the code above we first inject our registered Converter with the Dependency Injection inside our constructor by using IConverter interface. Then we create two objects globalSettings and objectSettings and use them as a configuration in the HtmlToPdfDcoumentproperty. Finally, we convert our pdf configuration into a real PDF Document on our local machine. Now let’s talk about the GlobalSettings and ObjectSettings classes.

      About the GlobalSettings Class

      The GlobalSettings class consists of the overall configuration properties for the PDF document. We use just a couple of those properties to set up the color mode, orientation, paper size, document title, etc… but if we go to the implementation of the GlobalSettings class we can find more of those properties. The Out property is very important if we want to save our file on a local machine. So we need to set it to the path where we want our document to. If we set the Out property then we can use _converter.Convert(pdf); to convert our document. We will see how this will change once we try to show our PDF document inside a browser. One more important note is that all the folders from the Out path should be previously created or the conversion won’t work. So in our example where we create a PDF document in the D: drive in the PDFCreator folder, we had to create the PDFCreator folder prior to PDF document creation.

      About the ObjectSettings Class

      The ObjectSettings class consists of the properties related to the contents of the PDF document. So, we can configure the visibility of the page counter, formatting of headers and footers, the body content of our document (HtmlContent property) or the web settings for our document. Of course, these are not all of the configuration properties but that's all we need for this article. The HtmlContent property is the very important property of this class. It contains our generated HTML template and shows the main body of a PDF document. WebSettings is pretty important as well, especially if we have an external CSS file for the styling as we do. In this property, we can configure the encoding of our document and provide the path to our CSS file. If we inspect this property, we are going to find out more settings that we can configure like the background of a PDF document or if we should load images or what the minimum font size is, etc…

      Inspecting Results

      Let’s start our app, open our browser and send a simple request towards our PDF creator endpoint: Request for PDF creation As a result, we have our document created in the PDFCreator folder: Created document .NET Core PDF Creation And let’s inspect the content of the document: PDF Content .NET Core PDF Creation That is awesome. We can now continue on.

      Showing a PDF Document in a Browser

      If we want to show our document in a browser instead, we can configure that quite easily. First, we need to remove the Out property from the globalSettings object. Then instead of this type of conversion:
      _converter.Convert(pdf);
      We are going to use this type:
      var file = _converter.Convert(pdf);
      Why is that? Well as we said if we use the Out property than the file is sent to stdout and saved to our local machine. But without the Out property, our output will be stored in a buffer. While converting we need to create a byte array and to store it inside the file variable. Finally, we are using that file variable and return it to the requester with a content type. This is our CreatePDF() method after modification:
      [HttpGet]
      public IActionResult CreatePDF()
      {
          var globalSettings = new GlobalSettings
          {
              ColorMode = ColorMode.Color,
              Orientation = Orientation.Portrait,
              PaperSize = PaperKind.A4,
              Margins = new MarginSettings { Top = 10 },
              DocumentTitle = "PDF Report"
          };
      
          var objectSettings = new ObjectSettings
          {
              PagesCount = true,
              HtmlContent = TemplateGenerator.GetHTMLString(),
              WebSettings = { DefaultEncoding = "utf-8", UserStyleSheet =  Path.Combine(Directory.GetCurrentDirectory(), "assets", "styles.css") },
              HeaderSettings = { FontName = "Arial", FontSize = 9, Right = "Page [page] of [toPage]", Line = true },
              FooterSettings = { FontName = "Arial", FontSize = 9, Line = true, Center = "Report Footer" }
           };
      
           var pdf = new HtmlToPdfDocument()
           {
              GlobalSettings = globalSettings,
              Objects = { objectSettings }
           };
      
           var file = _converter.Convert(pdf);
           return File(file, "application/pdf");
      }
      
      And this is the result: PDF browser content .NET Core PDF Creation

      Using Existing HTML Page to Generate PDF Content

      We don’t have to use our custom HTML template to generate a PDF content, we can use an existing HTML page. The effort is minimal. All we have to do is to remove the HtmlContent property and add the Page property of the ObjectSettings class. So instead of this code:
      HtmlContent = TemplateGenerator.GetHTMLString()
      let’s add this code:
      Page = https://code-maze.com/
      And let’s inspect the result: PDF browser HTML page .NET Core PDF Creation

      Enabling Download Mode

      If we want to enable download feature for the PDF document we need to modify our return statement in our action method. All we have to do is to simply add the name of the file with its extension to the return statement:
      return File(file, "application/pdf", "EmployeeReport.pdf");
      As a result, we are going to have our file downloaded: PDF downloaded file And there it is. Everything is working as it supposed to.

      Update Project For Deployment

      If we want to deploy this application, we have to make some changes. Let's do that step by step. First, in the Utility folder we are going to add a new class CustomAssemblyLoadContext and modify it:
      internal class CustomAssemblyLoadContext : AssemblyLoadContext
      {
          public IntPtr LoadUnmanagedLibrary(string absolutePath)
          {
              return LoadUnmanagedDll(absolutePath);
          }
          protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
          {
              return LoadUnmanagedDllFromPath(unmanagedDllName);
          }
      
          protected override Assembly Load(AssemblyName assemblyName)
          {
              throw new NotImplementedException();
          }
      }
      After that, let's modify the ConfigureServices method in the StartUp class:
      public void ConfigureServices(IServiceCollection services)
      {
          var context = new CustomAssemblyLoadContext(); 
          context.LoadUnmanagedLibrary(Path.Combine(Directory.GetCurrentDirectory(), "libwkhtmltox.dll"));
      
          services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools()));
      
          services.AddControllers();
      }
      In here, we are creating an instance of the CustomAssemblyLoadContext class and just call the LoadUnmanagedLibrary method with the path of the libwkhtmltox.dll file. We need to do one more thing. When we publish our application, we need to have the libwkhtmltox.dll file and the styles.css file in the published directory. To ensure that, right-click on the dll file in Solution Explorer and choose properties.  For the Build Action we are going to choose Content and for the Copy to Output Directory, we are going to choose Copy always. We need to repeat these steps for the CSS file as well: Now, all we have to do is to publish our application by following one of these tutorials or both of them: .NET Core Application IIS Deployment .NET Core Application Linux Deployment This is the result of the IIS deployment:

      Conclusion

      In this article, we have used the DinkToPdf library to create PDF documents while working with the .NET Core Web API project. We have created our PDFs in different ways to show many different features of this library. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      3498 0 0 0 843 0 0 844 843 3 286 0 0 287 286 0 289 287 0 290 289 0 291 290 0 292 291 0 293 292 0 294 293 0 296 289 0 357 0 0 360 357 0 365 357 0 372 357 0 373 365 0 402 360 0 410 0 0 463 0 0 481 0 0 482 481 0 483 463 0 484 482 0 509 0 0 510 509 0 525 0 0 526 525 0 533 0 0 534 and all should be just fine.]]> 533 0 535 534 0 536 534 0 537 534 0 538 534 0 539 535 0 540 538 0 550 https://plus.google.com/104058200745344738922 var context = new CustomAssemblyLoadContext(); context.LoadUnmanagedLibrary(Path.Combine(Directory.GetCurrentDirectory(), "libwkhtmltox.dll")); services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools())); ]]> 0 0 617 0 0 637 0 0 639 637 0 641 0 0 644 641 0 667 0 0 679 550 0 681 localhost:5000/api/pdfcreator it works as expected, while if I type localhost:5000/api/pdfcreator/CreatePDF (i.e. appending the controller's action) it returns an error? The reason is: I would like to call the action (and possibly create different actions in the same controller as well) and provide parameters to them in order to generate different reports and formatting depending on the parameters. What would be the proper way of dealing with this scenario? Thanks]]> 0 0 682 481 0 685 0 0 715 644 0 723 667 0 730 685 0 731 682 0 732 681 0 746 0 0 752 solution that worked for me. All I did was install MS Visual C++ 2015 redistributable package on the server. The dependencies were part of this distribution.]]> 0 0 767 http://www.brandwebdirect.com/website-design-services 0 0 958 0 0 816 0 0 817 816 0 818 817 0 819 818 0 821 819 0 822 821 0 849 843 0 853 849 0 854 853 0 862 0 0 865 862 0 879 865 0 881 879 0 960 958 0 977 0 0 979 977 0 982 979 0 983 982 0 1202 0 0 1203 1202 0 1204 1203 0 1330 0 0 1331 var picAddress = Path.Combine(Directory.GetCurrentDirectory(), "Images", "CodeMaze.png"); sb.AppendFormat(@"code maze picture", picAddress); My test result was like this: https://uploads.disquscdn.com/images/514e2106b8830d60703b51436e321d2fce79c5f914ef73703177c826bfce2e1d.png As you can see, the picture is right below the table. Best regards.]]> 1330 0 1625 0 0 1892 0 0 1893 1892 0 1894 1893 0 1897 1894 0
      React - Handling PUT Request in React https://code-maze.com/handling-put-request/ Wed, 27 Jun 2018 06:32:38 +0000 https://code-maze.com/?p=3611
    • Preparing the Project and Creating Components
    • Navigation and Routing
    • HTTP, Axios, and Redux 
    • React Lazy Loading and HOC Component
    • Error Handling and Additional Components
    • Dynamic Form Creation and Modal Components
    • Form Validation and Sending the POST Requests
    • React PUT Requests (Current article)
    • React DELETE Requests
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the React series, check out: Introduction to the React series. The source code is available at GitHub: React series - react-series-part8-end branch This post is divided into several sections:

      Preparation for the Update Owner Component and the Routing Settings

      Inside the containers folder, we can find the Ownerfolder. Inside that folder, we are going to create a new one and name it UpdateOwner. Let’s create a new file UpdateOwner.js inside: UpdateOwner structure Put Request in React In this file, we are going to handle a business logic to update the owner entity by reusing the features we have already used in the CreateOwner component. Therefore, this business logic shouldn’t be too hard to understand because we already have all the necessary knowledge (from previous parts) about input fields, validation, and a state object. Let’s add the basic logic to the UpdateOwner.js file:
      import React, { Component } from 'react';
      import { Form, Well, Button, FormGroup, Col } from 'react-bootstrap';
      import { returnInputConfiguration } from '../../../Utility/InputConfiguration';
      
      class UpdateOwner extends Component {
          state = {
              ownerForm: {},
              isFormValid: true
          }
      
          componentWillMount = () => {
              this.setState({ ownerForm: returnInputConfiguration() });
          }
      
          render() { 
              return (  
                  <Well>
                      
                  </Well>
              )
          }
      }
       
      export default UpdateOwner;
      
      So, we have created the class component with its local state. This state has the same properties as the CreateOwner component: the ownerForm, and isFormValid. For the UpdateOwner component, we need to set the isFormValid property to true because all the fields on the form are going to be populated as soon as the component mounts. Therefore, our fields will not be invalid when form mounts as they were in the CreateOwner component. In the compnentWillMount lifecycle hook, we fetch all the input configuration and update the state. To enable navigation to this component we need to modify the App.js file:
      import UpdateOwner from './Owner/UpdateOwner/UpdateOwner';
      <Route path="/updateOwner/:id" component={UpdateOwner} />
      Now we are able to navigate to this component.

      Adding Input Elements, Validation, and Two-Way Binding

      Right below the render function and above the return block we are going to add this line of code to convert the ownerForm object to an array of objects:
      const formElementsArray = formUtilityActions.convertStateToArrayOfFormObjects({ ...this.state.ownerForm });
      We shouldn't forget to add the import statement for the formUtilityActions and the Input component as well:
      import * as formUtilityActions from '../../../Utility/FormUtility';
      import Input from '../../../UI/Inputs/Input';
      
      Then, inside the Well tag, we are going to add this code to display the input elements and buttons:
      <Form horizontal onSubmit={this.updateOwner}>
          {
      	formElementsArray.map(element => {
      		return <Input key={element.id} elementType={element.config.element} 
      			id={element.id} label={element.config.label}
      			type={element.config.type} value={element.config.value} 
      			changed={(event) => this.handleChangeEvent(event, element.id)}
      			errorMessage={element.config.errorMessage} invalid={!element.config.valid} 
      			shouldValidate={element.config.validation}
      			touched={element.config.touched} 
      			blur={(event) => this.handleChangeEvent(event, element.id)} />
      	})
          }
          <br />
          <FormGroup>
      	<Col mdOffset={6} md={1}>
      		<Button type='submit' bsStyle='info' disabled={!this.state.isFormValid}>Update</Button>
      	</Col>
      	<Col md={1}>
      		<Button bsStyle='danger' onClick={this.redirectToOwnerList}>Cancel</Button>
      	</Col>
          </FormGroup>
      </Form>
      This is code which we have used in one of the previous posts. Moreover, let’s add the handleChangeEvent function (below the componentWillMount lifecycle hook) to enable validation and two-way binding:
      handleChangeEvent = (event, id) => {
          const updatedOwnerForm = { ...this.state.ownerForm };
          updatedOwnerForm[id] = formUtilityActions.executeValidationAndReturnFormElement(event, updatedOwnerForm, id);
      
          const counter = formUtilityActions.countInvalidElements(updatedOwnerForm);
      
          this.setState({ ownerForm: updatedOwnerForm, isFormValid: counter === 0 })
      }
      Again this is the section where we can reuse the code for validation and two-way binding. At this moment when we click on the Update button in the OwnerList component, we are going to see the UpdateOwner view: UpdateOwner initial view Put Request in React This is fine, but we want to populate all the fields with correct owner’s data. So, let’s do exactly that.

      Connecting the Reducer With the Component

      Let’s add the necessary import statements in the UpdateOwner.js file:
      import * as repositoryActions from '../../../store/actions/repositoryActions';
      import * as errorHandlerActions from '../../../store/actions/errorHandlerActions';
      import { connect } from 'react-redux';
      import moment from 'moment';
      
      Then we are going to add the mapStateToProps function below the component:
      const mapStateToProps = (state) => {
          return {
              data: state.repository.data,
              showSuccessModal: state.repository.showSuccessModal,
              showErrorModal: state.errorHandler.showErrorModal,
              errorMessage: state.errorHandler.errorMessage
          }
      }
      
      Just below the mapStateToProps function, we need to add the mapDispatchToProps function:
      const mapDispatchToProps = (dispatch) => {
          return {
              onGetOwnerById: (url, props) => dispatch(repositoryActions.getData(url, props)),
              onUpdateOwner: (url, owner, props) => dispatch(repositoryActions.putData(url, owner, props)),
              onCloseSuccessModal: (url, props) => dispatch(repositoryActions.closeSuccessModal(props, url)),
              onCloseErrorModal: () => dispatch(errorHandlerActions.closeErrorModal())
          }
      }
      
      Finally, let’s modify the export statement:
      export default connect(mapStateToProps, mapDispatchToProps)(UpdateOwner);
      There it is. Now we have the connection between the reducers and our component and we can add our modal components to show success or error messages. First, let’s add the import statements:
      import SuccessModal from '../../../components/Modals/SuccessModal/SuccessModal';
      import ErrorModal from '../../../components/Modals/ErrorModal/ErrorModal';
      
      Then we need to add the components below the Form tag but inside the Well tag:
      <SuccessModal show={this.props.showSuccessModal} modalHeaderText={'Success message'} 
      	modalBodyText={'Action completed successfully'}
      	okButtonText={'OK'} 
      	successClick={() => this.props.onCloseSuccessModal('/owner-List', { ...this.props })} />
      <ErrorModal show={this.props.showErrorModal} modalHeaderText={'Error message'} 
      	modalBodyText={this.props.errorMessage}
      	okButtonText={'OK'} 
      	closeModal={() => this.props.onCloseErrorModal()} />

      Fetching Data From The Server

      Below the compnentWillMount lifecycle hook, we are going to add another hook to fetch the data from the server:
      componentDidMount = () => {
          const id = this.props.match.params.id;
          const url = '/api/owner/' + id;
          this.props.onGetOwnerById(url, { ...this.props });
      }
      
      In this function, we take the id of the owner we want to update and then we fetch that owner from the server. After that we need to update our local state (to be more precise the ownerForm object inside that state) to show the data from the owner object in the input fields. The componentWillMount and the componentDidMounthooks are the creation lifecycle hooks. But React has the update lifecycle hooks which triggers as soon as a new property arrives at the component or we update the state. So, if we take a look at the diagram where we explained the Redux flow, we can see that after the reducer updates the state, the central store is going to propagate that state as props inside the component. We can catch that change at that moment and update our ownerForm object inside our local state with the update lifecycle hook.

      Displaying Data on the Screen

      Therefore, let’s add a new update lifecycle hook below the componentDidMount function:
      componentWillReceiveProps = (nextProps) => {
      	const updatedOwnerForm = { ...this.state.ownerForm };
      	let nameObject = { ...updatedOwnerForm.name };
      	let dateObject = { ...updatedOwnerForm.dateOfBirth };
      	let addressObject = { ...updatedOwnerForm.address };
      
      	nameObject.value = nextProps.data.name;
      	nameObject.valid = true;
      	dateObject.value = moment(nextProps.data.dateOfBirth);
      	addressObject.value = nextProps.data.address;
      	addressObject.valid = true;
      
      	updatedOwnerForm['name'] = nameObject;
      	updatedOwnerForm['dateOfBirth'] = dateObject;
      	updatedOwnerForm['address'] = addressObject;
      	this.setState({ ownerForm: updatedOwnerForm });
      }
      
      The componentWillReceiveProps hook is called only when a component receives a new props object and not when we update the state. Why is this important ? Well, some of the update lifecycle hooks are going to be triggered when new props arrive and when we update the state as well, therefore if we update the state inside that hook and do not provide the exit condition (some type of if statement) we are going to end up with an infinite loop. But that’s not the case with the componentWillReceiveProps hook. Inside the componentWillReceiveProps hook, we are not using this.props statement but the nextProps parameter because it contains our new props object. So, all the actions in this function are dedicated to extracting the ownerForm from the state immutably and placing it in theupdatedOwnerform. Then from the updatedOwnerForm, we can extract all the other objects immutably as well (name, address, and dateOfBirth). After that action, we can change the property values inside those objects and return those objects inside the updatedOwnerForm. Finally, we can update the state with the new updatedOwnerFormobject. Now our form has a different look once we navigate to it: Populated Update Owner form

      Executing the Update Action

      At the end let’s add two functions to redirect to the OwnerList component if we click the Cancel button, and to update the entity:
      redirectToOwnerList = () => {
          this.props.history.push('/owner-List');
      }
      
      updateOwner = (event) => {
          event.preventDefault();
      
          const ownerToUpdate = {
       	name: this.state.ownerForm.name.value,
      	dateOfBirth: this.state.ownerForm.dateOfBirth.value,
      	address: this.state.ownerForm.address.value
          }
      
          const url = "/api/owner/" + this.props.data.id;
      
          this.props.onUpdateOwner(url, ownerToUpdate, {...this.props});
      }
      There we go. Now we can test the update functionality. We can modify the code on the Web API server to respond with the success and the error responses to test our modal components. Furthermore, we can test the form validation by emptying the input fields or typing more than 60 characters in the address input field.

      Conclusion

      By reading this post, you've learned:
      • How to use different lifecycle hooks to fetch data from the server and to update a local state
      • To handle PUT request in our project
      Thank you for reading the article and I hope you found something useful in it. In the next part of the series, where we finish our React series journey, we are going to learn how to handle Delete request in our application. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      3611 0 0 0 222 https://www.alvinashcraft.com/2018/06/27/dew-drop-june-27-2018-2754/ 0 0
      React - Handling Delete Request in React https://code-maze.com/react-netcore-delete-request/ Wed, 04 Jul 2018 06:04:37 +0000 https://code-maze.com/?p=3721 DeleteOwner component to delete an entity from the server. After we implement this component, we'll complete this series and have all the CRUD operations. Without further ado, let’s get into it. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the React series, check out: Introduction to the React series. The source code is available at GitHub: React series - react-series-part9-end branch This post is divided into several sections:

      Preparations for the Delete Owner Component and the Delete Route

      Inside the Owner folder, let’s create a new one and name it DeleteOwner. Inside we are going to create a new file DeleteOwner.js: Delete Request in React folder structure Now let’s modify the DeleteOwner.js file with basic setup code:
      import React, { Component } from 'react';
      import Aux from '../../../hoc/Auxiliary/Auxiliary';
      
      class DeleteOwner extends Component {
          render() { 
              return ( 
                  <Aux>
                      
                  </Aux>
               )
          }
      }
       
      export default DeleteOwner;
      
      To enable navigation to this component, we need to modify the App.js file:
      import DeleteOwner from './Owner/DeleteOwner/DeleteOwner';
      <Route path="/deleteOwner/:id" component={DeleteOwner} />
      Right now if we click on the Delete button in the OwnerList component, we will navigate to the DeleteOwner page (even though it has no content inside).

      Connecting Our Component with the Reducer

      First of all, we need to add the import statements:
      import * as repositoryActions from '../../../store/actions/repositoryActions';
      import * as errorHandlerActions from '../../../store/actions/errorHandlerActions';
      import { connect } from 'react-redux';
      
      The next thing we need to do is to add the mapStateToProps function right below the class:
      const mapStateToProps = (state) => {
        return {
           data: state.repository.data,
           showSuccessModal: state.repository.showSuccessModal,
           showErrorModal: state.errorHandler.showErrorModal,
           errorMessage: state.errorHandler.errorMessage
        }
      }
      
      And then, add the mapDispatchToProps function:
      const mapDispatchToProps = (dispatch) => {
          return {
              onGetOwnerById: (url, props) => dispatch(repositoryActions.getData(url, props)),
              onDeleteOwner: (url, props) => dispatch(repositoryActions.deleteData(url, props)),
              onCloseSuccessModal: (url, props) => dispatch(repositoryActions.closeSuccessModal(props, url)),
              onCloseErrorModal: () => dispatch(errorHandlerActions.closeErrorModal())
          }
      }
      
      So, all of this code is quite familiar (if you had read previous parts). Finally, let’s modify the export statement:
      export default connect(mapStateToProps, mapDispatchToProps)(DeleteOwner);

      Implementing JSX in the DeleteOwner Component

      To create our view for this component, we need to add the import statements first:
      import { Well, Button, Col, Row, ControlLabel } from 'react-bootstrap';
      import Moment from 'react-moment';
      
      Right between the render and return blocks, we need to add this code:
      let owner = {...this.props.data};
      Inside the Aux tag, let’s add a content that we are going to display on the page:
      <Row>
         <Col md={10}>
             <Well>
                 <Row>
                    <Col md={3}>
                       <ControlLabel htmlFor='name'>Owners name:</ControlLabel>
                    </Col>
                    <Col md={7}>
                        <span name='name'>{owner.name}</span>
                    </Col>
                 </Row>
                 <Row>
                    <Col md={3}>
                        <ControlLabel htmlFor='dateOfBirth'>Date of birth:</ControlLabel>
                    </Col>
                    <Col md={7}>
                        <span name='dateOfBirth'><Moment format="MM/DD/YYYY">{owner.dateOfBirth}</Moment></span>
                    </Col>
                 </Row>
                 <Row>
                    <Col md={3}>
                        <ControlLabel htmlFor='address'>Address:</ControlLabel>
                    </Col>
                    <Col md={7}>
                        <span name='address'>{owner.address}</span>
                    </Col>
                  </Row>
             </Well>
          </Col>
      </Row>
      
      Below the Row tag we need to display the buttons on the page:
      <Row>
         <Col mdOffset={8} md={1}>
             <Button type="submit" bsStyle="info" onClick={this.deleteOwner}>Delete</Button>
         </Col>
         <Col md={1}>
              <Button bsStyle='danger' onClick={this.redirectToOwnerList}>Cancel</Button>
         </Col>
      </Row>
      
      Finally, let’s add the import statements for the modal components:
      import SuccessModal from '../../../components/Modals/SuccessModal/SuccessModal';
      import ErrorModal from '../../../components/Modals/ErrorModal/ErrorModal';
      
      Right after that, below the Row tag and right above the Aux tag, let’s add the code for displaying our modal components:
      <SuccessModal show={this.props.showSuccessModal} modalHeaderText={'Success message'}
            modalBodyText={'Action completed successfylly'}
            okButtonText={'OK'}
            successClick={() => this.props.onCloseSuccessModal('/owner-List', { ...this.props })} />
      <ErrorModal show={this.props.showErrorModal} modalHeaderText={'Error message'}
            modalBodyText={this.props.errorMessage}
            okButtonText={'OK'}
            closeModal={() => this.props.onCloseErrorModal()} />
      
      Now, we can inspect the result: Result picture As we can see from the picture above, we don’t have all the data on the page, but we are about to fix that shortly.

      Implementing the Business Logic for the Delete Action

      To fetch the owner object from the server we are going to add the componentDidMount function right above the render function:
      componentDidMount = () => {
          const id = this.props.match.params.id;
          const url = '/api/owner/' + id;
          this.props.onGetOwnerById(url, { ...this.props });
      }
      
      Now if we navigate to the OwnerDelete component, we are going to see the page with the correct data: In order for the Cancel button to work, let’s add this function:
      redirectToOwnerList = () => {
          this.props.history.push('/owner-List');
      }
      
      Finally, let’s implement the delete action:
      deleteOwner = (event) => {
          event.preventDefault();
      
          const url = "/api/owner/" + this.props.data.id;
      
          this.props.onDeleteOwner(url, { ...this.props });
      }
      
      Great job. We can check the result: Delete action If we click on the OK button, we are going to be directed to the OwnerList component.

      Conclusion

      Finally, we have finished our journey for this React project. We hope you have enjoyed as much as we did. Thank you for reading the article and I hope you found something useful in it. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      3721 0 0 0 237 https://www.alvinashcraft.com/2018/07/05/dew-drop-july-5-2018-2759/ 0 0
      ASP.NET Core Authentication with JWT and Angular - Part 1 https://code-maze.com/authentication-aspnetcore-jwt-1/ Mon, 09 Jul 2018 06:00:21 +0000 https://code-maze.com/?p=2370  learn how to implement authentication with Angular on the front end side and ASP.Net Core on the server-side using the JSON web tokens (JWT). We are also going to learn how authentication works in general and how to utilize JSON web tokens to securely transmit the user’s credentials from the server to the client and vice versa. Therefore, we are going to divide this series into two parts. In the first part, we are going to implement backend service with ASP.NET Core and authentication with JWT (JSON web token) integration. In the second part, we are going to implement front-end features like login, logout, securing routes and role-based authorization with Angular. [sc name="part_of_series" headline="Recommended Articles"] The source code for this part can be found on this GitHub repo. This post is divided into several sections:

      The Big Picture

      Before we get into the implementation of authentication and authorization, let’s have a quick look at the big picture. There is an application that has a login form. A user enters its username, password and presses the login button. After pressing the login button, a client (eg web browser) sends the user’s data to the server's API endpoint.web authentication big picture When the server validates the user’s credentials and confirms that the user is valid, it's going to send an encoded JWT to the client. JSON web token is basically a JavaScript object that can contain some attributes of the logged-in user. It can contain a username, user subject, user roles or some other useful information. At the client-side, we store the JWT in the browser’s local storage to remember the user’s login session. We may also use the information from the JWT to enhance the security of our application as well.

      Introduction to JSON Web Tokens

      JSON web tokens enable a secure way to transmit data between two parties in the form of a JSON object. It’s an open standard and it's a popular mechanism for web authentication. In our case, we are going to use JSON web tokens to securely transfer a user’s data between the client and the server. JSON web tokens consist of three basic parts: the header, payload, and the signature. One real example of JSON web token: JWT example Every part of all three parts is shown in a different color:

      Header

      The first part of JWT is the Header, which is a JSON object encoded in the base64 format. The header is a standard part of JWT and we don’t have to worry about it. It contains information like the type of token and the name of the algorithm. Header Sample:
      {
        "alg": "HS256",
        "typ": "JWT"
      }

      Payload

      After the Header, we have a Payload which is also a JavaScript object encoded in the base64 format. The payload contains some attributes about the logged-in user. For example, it can contain user id, user subject, and the information about whether a user is an admin user or not. JSON web tokens are not encrypted and can be decoded with any base64 decoder so please never include sensitive information in the Payload. Payload sample:
      {
        "sub": "1234567890",
        "name": "John Doe",
        "iat": 1516239022
      }
      

      Signature

      Finally, we have the Signature part. Usually, the server uses the signature part to verify whether the token contains valid information, the information which the server is issuing. It is a digital signature that gets generated by combining the header and the payload together. Moreover, it's based on a secret key that only the server knows. JWT signature composition So, if malicious users try to modify the values in the payload, they have to recreate the signature and for that purpose, they need the secret key which the only server has. At the server-side, we can easily verify if the values are original or not by comparing the original signature with a new signature computed from the values coming from the client. So, we can easily verify the integrity of our data just by comparing the digital signatures. This is the reason why we use JWT.

      Creating ASP.NET Core Web Service

      Now let’s create a brand new ASP.NET Core project. We can create a new Web API project with .NET Core CLI or you can use Visual Studio 2019. We are going to use the CLI for this demo so if you don't want to install Visual Studio 2019, you don't have to. Once we create the project you can open it with any code editor like Visual Studio Code, Notepad++, WebStorm or even Visual Studio. To create a new Web API project with .NET Core CLI we need to:
      • Open a command-line terminal (cmd)
      • Navigate the terminal window to the directory where we want to create the project
      • Run the command:
      dotnet new webapi -n webapplication
      As a result, we are going to have a new .NET Core Web API project template in the current directory with some boilerplate code. Additionally, let's navigate the terminal window to the project’s root directory. We need to navigate to our project:
      cd webapplication
      And run the application locally:
      dotnet run
      As a result, this is going to spin up a web server locally and it will host our application at http://localhost:5000. If you type the same address in the browser’s address bar, you are going to get no response as there is no resource available at that endpoint. Now let's type http://localhost:5000/api/values to get some result: HTTP GET request response Awesome! So far so good. In the next step, we are going to configure JWT authentication in our application.

      Configuring JWT Authentication

      To configure JWT authentication in .NET Core, we need to modify Startup.csfile. It's a bootstrapper class that runs when our application starts. Inside the file, we have the ConfigureSerivces method that adds services to the IServiceCollection container, thus making them available for the constructor injection. JWT's support is built into ASP.NET Core 3.0 and we are going to configure an authentication middleware for JSON web tokens. For the sake of simplicity, we are going to add all the code inside the ConfigureServices method. But the better practice is to use Extension methods so we could free our ConfigureServices method from extra code lines. If you want to learn how to do that, and to learn more about configuring the .NET Core Web API project, check out: .NET Core Service Configuration. We need to modify the ConfigureServices method to add the JWT support:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddAuthentication(opt => {
              opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
              opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
          })
          .AddJwtBearer(options =>
          {
              options.TokenValidationParameters = new TokenValidationParameters
              {
                  ValidateIssuer = true,
                  ValidateAudience = true,
                  ValidateLifetime = true,
                  ValidateIssuerSigningKey = true,
      
                  ValidIssuer = "http://localhost:5000",
                  ValidAudience = "http://localhost:5000",
                  IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@345"))
              };
          });
      
          services.AddControllers();
      }
      We have to install the Microsoft.AspNetCore.Authentication.JwtBearer library in order for this to work.

      Code Explanation

      Firstly, we register the JWT authentication middleware by calling the method AddAuthentication on the ISerivceCollectioninterface. Next, we specify the authentication scheme JwtBearerDefaults.AuthenticationScheme as well as ChallengeScheme. We also provide some parameters that will be used while validating JWT. Excellent. We've successfully configured the JWT authentication. According to the configuration, the token is going to be valid if:
      • The issuer is the actual server that created the token (ValidateIssuer=true)
      • The receiver of the token is a valid recipient (ValidateAudience=true)
      • The token has not expired (ValidateLifetime=true)
      • The signing key is valid and is trusted by the server (ValidateIssuerSigningKey=true)
      Additionally, we are providing values for the issuer, audience and the secret key that the server uses to generate the signature for JWT. We are going to hardcode both username and password for the sake of simplicity. But, the best practice is to put the credentials in a database or a configuration file or to store the secret key into the environment variable. We need to do one more step to make our authentication middleware available to the application. Add the app.UseAuthentication() in the Configure method:
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
      
          app.UseHttpsRedirection();
      
          app.UseRouting();
      
          app.UseAuthentication();
          app.UseAuthorization();
      
          app.UseEndpoints(endpoints =>
          {
              endpoints.MapControllers();
          });
      }
      
      And that’s all we need to configure the JWT authentication in ASP.NET Core.

      Securing API Endpoints

      We already have an API endpoint /weatherforecast to get some example weather information and that endpoint is not secure. Anyone can send a request to http://localhost:5000/weatherforecast to fetch the values. So, in this section, we are going to add a new api/customers endpoint to serve a list of the customers.  This endpoint is going to be secure from anonymous users and only logged-in users can consume it. Now let's add an empty CustomersController in the Controllers folders. Inside the controller, we are going to add a Get action method that is going to return an array of customers. More importantly, we are going to add an extra security layer by decorating the action method with the[Authorize] attribute so only logged-in users can access the route. Let's modify the CustomersController class:
      [Route("api/[controller]")]
      [ApiController]
      public class CustomersController : ControllerBase
      {
          // GET api/values
          [HttpGet,Authorize]
          public IEnumerable<string> Get()
          {
       	return new string[] { "John Doe", "Jane Doe" };
          }
      
      }
      Authorize attribute on top of the GET method restricts access to only authorized users. Only users who are logged-in can access the list of customers. Therefore, this time if you make a request to http://localhost:5000/api/customers from the browser’s address bar, instead of getting a list of customers you are going to get a 401 Not Authorized response. HTTP 401 Not Authorized response

      Adding Login Endpoint

      To authenticate anonymous users, we have to provide a login endpoint so the users can log-in and access protected resources. A user is going to provide a username, password and if the credentials are valid we are going to issue a JSON web token for the requesting client. In addition, before we start implementing the authentication controller we need to add a LoginModel to hold user’s credentials on the server. LoginModel is a simple class that contains two properties: UserName and Password.  We are going to create a Models folder in the root directory and inside it a LoginModel class:
      public class LoginModel
      {
          public string UserName { get; set; }
          public string Password { get; set; }
      }
      
      Now let’s create the AuthController inside the Controllers folder. Inside the AuthControllerwe are going to validate the user’s credentials. If the credentials are valid, we are going to issue a JSON web token. For this demo, we are going to hardcode the username and password to implement a fake user. After validating the user’s credentials we are going to generate a JWT with a secret key. JWT uses the secret key to generate the signature. Let's implement the AuthController:
      [Route("api/auth")]
      [ApiController]
      public class AuthController : ControllerBase
      {
          // GET api/values
          [HttpPost, Route("login")]
          public IActionResult Login([FromBody]LoginModel user)
          {
              if (user == null)
              {
                  return BadRequest("Invalid client request");
              }
      
              if (user.UserName == "johndoe" && user.Password == "def@123")
              {
                  var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@345"));
                  var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
      
                  var tokeOptions = new JwtSecurityToken(
                      issuer: "http://localhost:5000",
                      audience: "http://localhost:5000",
                      claims: new List<Claim>(),
                      expires: DateTime.Now.AddMinutes(5),
                      signingCredentials: signinCredentials
                  );
      
                  var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
                  return Ok(new { Token = tokenString });
              }
              else
              {
                  return Unauthorized();
              }
          }
      }

      Code Explanation

      First of all, notice the use of the [HttpPost] attribute. After applying this attribute to action methods as a result the API endpoint only responds to HTTP POST requests. Inside the login method, we are creating the SymmetricSecretKey with the secret key value superSecretKey@345. Then, we are creating the objec SigningCredentials and as arguments, we provide a secret key and the name of the algorithm that we are going to use to encode the token. Here comes the interesting part. The first two steps are the standard steps that you don’t need to worry about. The third step is the one that we are interested in. In the third step we are creating the JwtSecurityToken object with some important parameters:
      • Issuer: The first parameter is a simple string representing the name of the web server that issues the token
      • Audience: The second parameter is a string value representing valid recipients
      • Claims: The third argument is a list of user roles, for example, the user can be an admin, manager or author (we are going to add roles in the next post)
      • Expires: The fifth argument is DateTime object that represents the date and time after which the token expires
      Finally, we create a string representation of JWT by calling the WriteToken method on JwtSecurityTokenHandler. Finally, we are returning JWT in a response. As a response, we have created an anonymous object that contains only the Token property.

      Testing the Login API

      So, let's save all the files, navigate to the root directory of the project and run the application with the .NET Core CLI command:
      dotnet run
      Let's launch any web proxy tool that is capable of composing and sending web requests over HTTP. (You can use your preferred REST client). We are going to use the Postman. In the request settings options, we are going to enter the URL: http://localhost:5000/api/auth/login, choose POST request option from the right side and paste  in the payload the JSON data:
      {
         "UserName":"johndoe",
         "Password": "def@123"
      } 
      
      And for headers, we have to select Content-Type for the key and application/json for the value. Authentication POST request for the JWT And press the send request button. In the response section, we are going to see a 200 OK response with the JWT string in the response body:Postam Jwt Response

      Conclusion

      By reading this post you have learned:
      • More about security  and how it is a crucial part of a web application development
      • What JWTs are and how they fit in the implementation of web security
      • How to create a web service with ASP.NET Core
      • How to secure API endpoints with Authorize attribute
      • And how to implement a login mechanism on the server
      In the next part, we are going to implement a front end side of our application with Angular. We are going to implement login, logout, authorization, secure the routes and much more. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      2370 0 0 0 242 https://www.alvinashcraft.com/2018/07/09/dew-drop-july-9-2018-2761/ 0 0 253 0 0 254 253 0 257 254 0 334 0 0 335 334 0 362 0 0 364 362 0 473 0 0 474 473 0 475 474 0 476 475 0 479 0 0 480 479 0 520 0 0 521 520 0 530 http://mebsa.info/problemas-al-enviar-peticion-con-header-authorization-en-angular-6-javascript-angular-angular6/ 0 0 669 Fed Up!!!!!! Finally Solve this by using the below line of code. IssuerSigningKeys = new [] { new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@345")) } Please update your Article. True Reader Thanks]]> 0 0 674 0 0 676 ConfigureServices() services.AddAuthentication() .AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters() { ValidIssuer = Configuration["JwtSecurityToken:Issuer"], ValidAudience = Configuration["JwtSecurityToken:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSecurityToken:Key"])) }; }); and in Startup.cs -> Configure() app.UseAuthentication(); then in one of my controllers I applied like this. [Authorize(Roles = "Agent")] [Route("artists")] public IActionResult Artists() { // some logic } Problem: in my Login method, I don't issue any token. So when the logged in user(with Agent role) tries to call this controller, surprisingly even without having a token from the client request, the authorization works. Again when I try to call this controller by logged in with different role it throws unauthorized access error which is as the expected. I don't have any idea on how the authorization process works without creating and exchanging the tokens. Have I missed something here...]]> 0 0 719 s and not singular IssuerSigningKey as stated in article. That was the reason for your error, which is pretty self explanatory "can not convert key into collection of keys". All the best and thank you for reading our articles. We appreciate it a lot.]]> 669 0 728 674 0 891 0 0 805 0 0 807 https://code-maze.com/ https://docs.microsoft.com/en-us/dotnet/standard/net-standard]]> 805 0 892 891 0 1146 0 0 1147 1146 0 1148 1147 0 1149 1148 0 1150 1149 0 1546 services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Host"], ValidAudience = Configuration["Host"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("yourKey")) }; }); And add Authorization too: app.UseAuthentication(); app.UseAuthorization(); ]]> 0 0 1547 1546 0 1913 Languages { get; set; } I wanted to load the JobName & Languages.]]> 0 0 1915 1913 0
      ASP.NET Core Authentication with JWT and Angular - Part 2 https://code-maze.com/authentication-aspnetcore-jwt-2/ Thu, 12 Jul 2018 06:50:17 +0000 https://code-maze.com/?p=2753  amazing web applications. Angular is one of those frameworks. It's both a popular and feature-rich framework that you can get to know and work with easily. [sc name="part_of_series" headline="Recommended Articles"] Creating a rich browser application is awesome but implementing the security is equally important if not even more. In this blog post, we are going to learn how we can implement authentication and authorization in Angular. We are going to see how we can securely transfer the user’s credentials from a browser to a server and vice versa. This part is the continuation of the previous part where we have briefly covered the JWT authentication backend side. In this part, we are going to consume that Web API in our Angular application. You can download the source code for the starting projects here and for the finished projects here. This blog is divided into the following sections:

      Enabling Cross-Origin Requests (CORS)

      By default, you can’t send an AJAX request over HTTP to servers on a different origin due to browser's security concerns. The HTTP protocol is not a secure protocol as it sends the requests in the plain text (at least HTTP v1 does) and anyone can potentially use a web proxy software to intercept modify the request. On the other hand, the HTTPS protocol is a secure way to transfer information and some browsers by default allow requests to cross-origin servers on HTTPS. So, what can we do? We need to configure the server to receive cross-origin requests. By default, the ASP.NET Core application will reject any request coming from the cross-origin clients. To enable CORS in .NET Core Web API, we need to implement a middleware in the Configure method of the Startup class. Let's open the webapplication-start project (you may find it in here) and add the code to the ConfigureServices method to configure the CORS policy:
      services.AddCors(options =>
      {
          options.AddPolicy("EnableCORS", builder =>
          {
             builder.AllowAnyOrigin()
                .AllowAnyHeader()
                .AllowAnyMethod();
          });
      });
      To make this middleware available for the application, add the following code in the Configure method:
       app.UseCors("EnableCORS");
      We can find out more about CORS and the additional options to configure it on this location: Enabling CORS in ASP.NET Core. Perfect. At this point, the server is ready to listen to the cross-origin requests.

      Implementing Login

      We have created the Angular starter application (available for download from here) which contains all the necessary code (basic Angular components, the routes, and basic form validation) we need for this post. With this project, it is going to be much easier for us to follow along with this post, because we can focus only on the parts important for the JWT authentication. To find out in detail how to work with Angular, visit our Angular Series. Let's download the starter Angular application, so we could easily follow all the coding parts. To implement the login, we are going to create the login method in the LoginComponent. Inside the login method, we are going to collect the username and the password from the login form. When a user presses the login button, we are going to collect the user's credentials with the Angular event binding. Once we have the credentials, we are going to send them to the server via the HTTP POST request to the login endpoint. The server is going to validate the data. If the username and password are valid, the server will issue a JSON web token and send it back to the browser. A valid username is "johndoe" and the valid password is "def@123", as you can read in part 1 of the JWT series.  Let's implement the login method:
      login(form: NgForm) {
          let credentials = JSON.stringify(form.value);
          this.http.post("http://localhost:5000/api/auth/login", credentials, {
            headers: new HttpHeaders({
              "Content-Type": "application/json"
            })
          }).subscribe(response => {
            let token = (<any>response).token;
            localStorage.setItem("jwt", token);
            this.invalidLogin = false;
            this.router.navigate(["/"]);
          }, err => {
            this.invalidLogin = true;
          });
        }
      In the code above, we send the HTTP POST request towards the server, by using an HttpClient service defined in the@angular/common/http module. The HttpClient service exposes some helper methods equivalent to HTTP verbs. For example, if we need to send the HTTP POST request to the server, we have the post method in the HttpClient service. The call to each method returns an Observable which is a JavaScript way of making the asynchronous calls.

      The Observable Callbacks

      The Observable provides three callbacks for us to subscribe to while waiting for the response. The first callback is called when Observable receives a successful response. The second callback is called when there is an error. The third callback is called whether there is a response or an error, it's a way of signaling that the request is completed. In the response callback, we get the token from the server and we save the token in the browser’s local storage. The browser's local storage is a collection of the key-value pairs that browser stores per website. Once we have the token persisted in storage, we can use it for future calls to access the protected resources on the server. You can think of a token as a special identity card that you may use to access the secret resources in your organization. Let's continue with the login form implementation in the login.component.html file:
      <form class="form-signin" #loginForm="ngForm" (ngSubmit)="login(loginForm)">
        <div class="container-fluid">
          <h2 class="form-signin-heading">Login</h2>
          <div *ngIf="invalidLogin" class="alert alert-danger">Invalid username or password.</div>
          <br/>
          <label for="username" class="sr-only">Email address</label>
          <input type="email" id="username" name="username" ngModel class="form-control" placeholder="User Name" required autofocus>
          <br/>
          <label for="password" class="sr-only">Password</label>
          <input type="password" id="password" name="password" ngModel class="form-control" placeholder="Password" required>
          <br/>
          <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
        </div>
      </form>

      Implementing Logout

      Implementing the logout function is very simple. It’s similar to losing the identity card. If we don’t have the card, we are not able to access the secretly protected resources. To log out the user, we are simply going to delete the token stored in the local storage which is the only key to access protected resources. If we don’t have the token, the server simply doesn’t know our identity and it's going to reject our calls to the protected resources. To implement log out we are going to create the logout method in the HomeComponent. Inside the logout method, we are going to remove the token from the local storage and that’s all.
      logOut() {
         localStorage.removeItem("jwt");
      }
      To perform the logout, we need to press the log out link on the Home page. So far so good. At this point our logout functionality is complete.

      Installing Angular Jwt Library

      To install the angular2-jwt library, let's run the following command in the terminal window:
      npm install @auth0/angular-jwt --save
      This library is going to help us work with the jwt token in Angular. Let's configure it by modifying the app.module.ts file:
      import { AuthGuard } from './guards/auth-guard.service';
      import { BrowserModule } from '@angular/platform-browser';
      import { NgModule } from '@angular/core';
      import { FormsModule } from '@angular/forms';
      import { HttpClientModule } from '@angular/common/http';
      import { RouterModule } from '@angular/router';
      import { JwtModule } from "@auth0/angular-jwt";
      
      import { HomeComponent } from './home/home.component';
      import { LoginComponent } from './login/login.component';
      import { CustomersComponent } from './customers/customers.component';
      import { AppComponent } from './app.component';
      
      export function tokenGetter() {
        return localStorage.getItem("jwt");
      }
      
      @NgModule({
        declarations: [
          HomeComponent,
          LoginComponent,
          CustomersComponent,
          AppComponent,
        ],
        imports: [
          BrowserModule,
          FormsModule,
          HttpClientModule,
          RouterModule.forRoot([
            { path: '', component: HomeComponent },
            { path: 'login', component: LoginComponent },
            { path: 'customers', component: CustomersComponent },
          ]),
          JwtModule.forRoot({
            config: {
              tokenGetter: tokenGetter,
              whitelistedDomains: ["localhost:5000"],
              blacklistedRoutes: []
            }
          })
        ],
        providers: [AuthGuard],
        bootstrap: [AppComponent]
      })
      export class AppModule { }
      
      We inject the JwtModule and configure it to use the tokenGetter function to retrieve the token from the local storage and to include it into any Http request executed by the HttpClientModule. Additionally, we add the servers URI in the white domain list required by JwtModule. For additional information about this library, you can visit @auth0/angular-jwt.

      Protecting Angular Routes

      Protecting the Angular routes is the crucial part of implementing security in the Angular application. To protect the routes, Angular provides the CanActivate interface. This interface exposes the canActivate method which we can implement to provide a route guard. The canActivate method triggers before the Angular route activates, it acts as a guard to the Angular routes. Inside the canActivate method, we can write any custom logic to protect our routes. In the folder named guards, we can find the auth-guard.service.ts file. Let's implement the AuthGuard logic which provides a custom implementation of the CanActivate interface:
      @Injectable()
      export class AuthGuard implements CanActivate {
        constructor(private jwtHelper: JwtHelper, private router: Router) {
        }
        canActivate() {
          var token = localStorage.getItem("jwt");
      
          if (token && !this.jwtHelper.isTokenExpired(token)){
            return true;
          }
          this.router.navigate(["login"]);
          return false;
        }
      }
      Inside the canActivate method, we are going to check if the token expired. To check the validity of a token, we are using the JwtHelper service. The JwtHelper service is defined in the @auth0-angular-jwt library which is a lightweight library that provides some helper services to easily work with JSON web tokens in Angular. Now, we are going to apply the AuthGuard service to the CustomersComponent route in the AppModule. To apply the AuthGuard, we simply provide the service name in the CustomersComponent's route object by using thecanActivate attribute. Following is the code snippet to activate the AuthGuardservice for the CustomerComponent route in the app.module.ts file:
      RouterModule.forRoot([
            { path: '', component: HomeComponent },
            { path: 'login', component: LoginComponent },
            { path: 'customers', component: CustomersComponent, canActivate: [AuthGuard] },
      ])
      The canActivate property in the Route object is an array. That means you can specify more than one service. Before we continue, let's add a few changes to the home.component.ts file:
      import { Component } from '@angular/core';
      import { Router } from '@angular/router';
      import { JwtHelperService } from '@auth0/angular-jwt';
      
      @Component({
        selector: 'app-home',
        templateUrl: './home.component.html',
        styleUrls: []
      })
      export class HomeComponent {
      
        constructor(private jwtHelper: JwtHelperService, private router: Router) {}
      
        isUserAuthenticated() {
          let token: string = localStorage.getItem("jwt");
          if (token && !this.jwtHelper.isTokenExpired(token)) {
            return true;
          }
          else {
            return false;
          }
        }
      
        public logOut = () => {
          localStorage.removeItem("jwt");
        }
      }
      We have added an additional check for the login/logout buttons.

      Accessing Protected Resources

      To access the protected resources we need to send the JWT token in the Authorization header with each request. The server is going to verify the token and grant access to protected resources. In our CustomerComponent, on the component initialization, we are going to send a request to the server to access a list of customers.
      import { Component, OnInit } from '@angular/core';
      import { HttpClient, HttpHeaders } from '@angular/common/http';
      
      @Component({
        selector: 'app-customers',
        templateUrl: './customers.component.html',
        styles: [``]
      })
      export class CustomersComponent implements OnInit  {
        customers: any;
       
        constructor(private http: HttpClient) { }
       
        ngOnInit() {
          let token = localStorage.getItem("jwt");
          this.http.get("http://localhost:5000/api/customers", {
            headers: new HttpHeaders({
              "Content-Type": "application/json"
            })
          }).subscribe(response => {
            this.customers = response;
          }, err => {
            console.log(err)
          });
        }
      }
      In the code snippet above, we are sending an HTTP GET request to the endpoint http://localhost:5000/api/customers with the JSON web token in the Authorization header. Yes, you can't see the authorization header in the request but it will be there due to the auth-jwt library configuration: authorization header If the token is invalid the server is going to reply with the 401 Unauthorized response. If the token is valid, then we are going to see a list of customers.

      Role-Based Authorization

      Right now, we have a fully functional application (the backend and the frontend part) which uses the JWT features for the user authentication. But, because we have only the [Authorize] attribute on top of the Customers controller's GET action, all the authenticated users have access to that endpoint. What if we don't want this type of behavior? What if we want only Managers to have access to that endpoint? Well, to accomplish that, we need to make a couple of changes on our Web API part. First, let's modify the [Authorize]attribute to give access only to a user with the Manager role:
      [HttpGet,Authorize(Roles = "Manager")]
      public IEnumerable<string> Get()
      {
          return new string[] { "John Doe", "Jane Doe" };
      }
      Excellent. Additionally, let's modify the Login method in the AuthController to set up the user claims:
      [HttpPost, Route("login")]
      public IActionResult Login([FromBody]LoginModel user)
      {
          if (user == null)
          {
              return BadRequest("Invalid client request");
          }
      
          if (user.UserName == "johndoe" && user.Password == "def@123")
          {
              var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@345"));
      
              var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
      
              var claims = new List<Claim>
              {
                  new Claim(ClaimTypes.Name, user.UserName),
                  new Claim(ClaimTypes.Role, "Manager")
              };
      
               var tokeOptions = new JwtSecurityToken(
                  issuer: "http://localhost:5000",
                  audience: "http://localhost:5000",
                  claims: claims,
                  expires: DateTime.Now.AddMinutes(5),
                  signingCredentials: signinCredentials
              );
      
              var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
              return Ok(new { Token = tokenString });
          }
          else
          {
              return Unauthorized();
          }
      }
      In the changed parts of the code, we create claims by adding the username and the role claim to the Claim list. Now, our "johndoe" user is a Manager and it should have access to the Customer's GET action. These claims are going to be included in our token. If we try to login with the Angular application, everything should work as before without any problems. All the JWT related logic is inside our Login method for the sake of simplicity. But we encourage you to create a new class (JwtConfigurator or use any other name) and transfer all the SymmetricSecurityKey, SigninCredentials, Claims and JWtSecurityToken logic to a new class. Finally, let's check what is going to happen if the "johndoe" has the Operator role and not the Manager role. To simulate this, we need to modify the role claim from the Manager to the Operator:
      var claims = new List<Claim>
      {
           new Claim(ClaimTypes.Name, user.UserName),
           new Claim(ClaimTypes.Role, "Operator")
      };
      Now, we still are able to log in but once we try to access the Customer's GET action, we are going to get the 403 Forbidden response: jwt-forbidden in Angular JWT Awesome, our authorization part works like a charm.

      JwtHelper DecodeToken

      One more thing though. Let's see how we can extract the data from the token on the client side. The jwtHelper service has the decodeToken function which can decode our token into the JSON object. Because we have already injected the JwtHelper service into the AuthGuard service, let's modify that service a bit just to see how the decodeToken function works. We are going to add one line of code that checks if the token exists and if it hasn't expired:
      if (token && !this.jwtHelper.isTokenExpired(token)){
        console.log(this.jwtHelper.decodeToken(token));
        return true;
      }
      Here is the result:
      {
        http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: "johndoe", 
        http://schemas.microsoft.com/ws/2008/06/identity/claims/role: "Manager", 
        exp: 1525510870, 
        iss: "http://localhost:5000", 
        aud: "http://localhost:5000"
      }
      

      Conclusion

      By reading this post you have learned:
      • How to configure cross-origin requests in the browser and on server
      • To login users in AngularStore save JWT in local storage.
      • How to remove JWT from local storage and how to log out a user in Angular
      • To protect Angular routes with the CanActivate interface
      • How to access protected resources on the server by sending tokens in the Authorization header
      For any suggestions or questions, don’t hesitate to comment below. [sc name="subscribe" formNumber="3785" contentType="Angular"]]]>
      2753 0 0 0 275 http://daeamon.brusee.ru/ 0 0 276 275 0 277 http://daeamon.brusee.ru/ 276 0 278 http://daeamon.brusee.ru/ 276 0 279 277 0 280 http://daeamon.brusee.ru/ 279 0 281 http://daeamon.brusee.ru/ 279 0 282 http://daeamon.brusee.ru/ 279 0 283 0 0 284 283 0 285 284 0 300 0 0 301 300 0 349 0 0 422 0 0 423 422 0 440 0 0 441 440 0 486 0 0 792 0 0 963 0 0 866 0 0 867 866 0 895 0 0 896 895 0 897 896 0 898 897 0 964 486 0 966 963 0 1208 0 0 1211 1208 0 1212 1211 0 1236 InjectionToken JWT_OPTIONS]: StaticInjectorError(Platform: core)[JwtHelperService -> InjectionToken JWT_OPTIONS]: NullInjectorError: No provider for InjectionToken JWT_OPTIONS! NullInjectorError: StaticInjectorError(AppModule)[JwtHelperService -> InjectionToken JWT_OPTIONS]: StaticInjectorError(Platform: core)[JwtHelperService -> InjectionToken JWT_OPTIONS]: NullInjectorError: No provider for InjectionToken JWT_OPTIONS! at NullInjector.get (core.js:1354) at resolveToken (core.js:1681) at tryResolveToken (core.js:1607) at StaticInjector.get (core.js:1470) at resolveToken (core.js:1681) at tryResolveToken (core.js:1607) at StaticInjector.get (core.js:1470) at resolveNgModuleDep (core.js:23118) at _createClass (core.js:23186) at _createProviderInstance (core.js:23151) at resolvePromise (zone-evergreen.js:797) at resolvePromise (zone-evergreen.js:754) at zone-evergreen.js:858 at ZoneDelegate.invokeTask (zone-evergreen.js:391) at Object.onInvokeTask (core.js:30885) at ZoneDelegate.invokeTask (zone-evergreen.js:390) at Zone.runTask (zone-evergreen.js:168) at drainMicroTaskQueue (zone-evergreen.js:559) at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:469) at invokeTask (zone-evergreen.js:1603)]]> 0 0 1237 1236 0 1238 1237 0 1239 1237 0 1240 | boolean { var token = localStorage.getItem('jwt'); if (token && !this.jwtHelper.isTokenExpired(token)) { console.log(this.jwtHelper.decodeToken(token)); return true; } this.router.navigate(['/']); return false; } }]]> 1238 0 1241 1240 0 1242 1241 0 1243 1242 0 1247 1243 0 1375 0 0 1378 1375 0 1383 https://www.facebook.com/app_scoped_user_id/YXNpZADpBWEVMZA3NDNXhINVVBNmxYVnBidmcxRHQxQ2VkSmUzTzdrUkVQSkNoWkxJd1NYd0JycGRoWV9VclNlX3BVdTYxMTQ3VUx2WDNwUElLV3hIbjJUNGFRYXUzZATFvLUdlOWtUSlkZD/ 0 0 1385 1383 0
      Global Error Handling in ASP.NET Core Web API https://code-maze.com/global-error-handling-aspnetcore/ Mon, 23 Jul 2018 05:14:22 +0000 https://code-maze.com/?p=3666 the try-catch block in our code as well as finally keyword to clean up resources afterward. Even though there is nothing wrong with the try-catch blocks in our Actions in Web API project, we can extract all the exception handling logic into a single centralized place. By doing that, we make our actions more readable and the error handling process more maintainable. If we want to make our actions even more readable and maintainable, we can implement Action Filters. We won't talk about action filters in this article but we strongly recommend reading our post Action Filters in .NET Core. [sc name="part_of_series" headline="Recommended Articles"] In this article, we are going to handle errors by using a try-catch block first and then rewrite our code by using built-in middleware and our custom middleware for global error handling to demonstrate the benefits of this approach. We are going to use an ASP.NET Core Web API project to explain these features and if you want to learn more about creating ASP.NET Core Web API (which we strongly recommend), you can read our ASP.NET Core Web API Tutorial. To download the source code for our starting project, you can visit the Global error handling start project. For the finished project refer to Global error handling end project In this article, we are going to talk about:

      Error Handling with Try-Catch Block

      To start off with this example, let’s open the Values Controller from the starting project (Global-Error-Handling-Start project). In this project, we can find a single Get() method and an injected Logger service. It is a common practice to include the log messages while handling errors, therefore we have created the LoggerManager service. It logs all the messages to the C drive, but you can change that by modifying the path in the nlog.config file. For more information about how to use Nlog in .NET Core, you can visit Logging with NLog. Now, let's modify our action method to return a result and log some messages:
      using System;
      using LoggerService;
      using Microsoft.AspNetCore.Mvc;
      
      namespace GlobalErrorHandling.Controllers
      {
          [Route("api/[controller]")]
          [ApiController]
          public class ValuesController : ControllerBase
          {
              private ILoggerManager _logger;
      
              public ValuesController(ILoggerManager logger)
              {
                  _logger = logger;
              }
      
              [HttpGet]
              public IActionResult Get()
              {
                  try
                  {
                      _logger.LogInfo("Fetching all the Students from the storage");
      
                      var students = DataManager.GetAllStudents(); //simulation for the data base access
      
                      _logger.LogInfo($"Returning {students.Count} students.");
      
                      return Ok(students);
                  }
                  catch (Exception ex)
                  {
                      _logger.LogError($"Something went wrong: {ex}");
                      return StatusCode(500, "Internal server error");
                  }
              }
          }
      }
      
      When we send a request at this endpoint, we will get this result: Basic request - Global Error Handling And the log messages: log basic request - Global Error Handling We see that everything is working as expected. Now let’s modify our code, right below the GetAllStudents() method call, to force an exception:
      throw new Exception("Exception while fetching all the students from the storage.");
      Now, if we send a request: try catche error - Global Error Handling And the log messages: log try catch error So, this works just fine. But the downside of this approach is that we need to repeat our try-catch blocks in all the actions in which we want to catch unhandled exceptions. Well, there is a better approach to do that.

      Handling Errors Globally with the Built-In Middleware

      The UseExceptionHandler middleware is a built-in middleware that we can use to handle exceptions. So, let’s dive into the code to see this middleware in action. First, we are going to add a new class ErrorDetails in the Models folder:
      using Newtonsoft.Json;
      
      namespace GlobalErrorHandling.Models
      {
          public class ErrorDetails
          {
              public int StatusCode { get; set; }
              public string Message { get; set; }
      
      
              public override string ToString()
              {
                  return JsonConvert.SerializeObject(this);
              }
          }
      }
      
      We are going to use this class for the details of our error message. To continue, let's create a new folder Extensions and a new static class ExceptionMiddlewareExtensions.cs inside it. Now, we need to modify it:
      using GlobalErrorHandling.Models;
      using LoggerService;
      using Microsoft.AspNetCore.Builder;
      using Microsoft.AspNetCore.Diagnostics;
      using Microsoft.AspNetCore.Http;
      using System.Net;
      
      namespace GlobalErrorHandling.Extensions
      {
          public static class ExceptionMiddlewareExtensions
          {
              public static void ConfigureExceptionHandler(this IApplicationBuilder app, ILoggerManager logger)
              {
                  app.UseExceptionHandler(appError =>
                  {
                      appError.Run(async context =>
                      {
                          context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                          context.Response.ContentType = "application/json";
      
                          var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
                          if(contextFeature != null)
                          { 
                              logger.LogError($"Something went wrong: {contextFeature.Error}");
      
                              await context.Response.WriteAsync(new ErrorDetails()
                              {
                                  StatusCode = context.Response.StatusCode,
                                  Message = "Internal Server Error."
                              }.ToString());
                          }
                      });
                  });
              }
          }
      }
      
      In the code above, we've created an extension method in which we've registered the UseExceptionHandler middleware. Then, we've populated the status code and the content type of our response, logged the error message, and finally returned the response with the custom created object. To be able to use this extension method, let’s modify the Configure method inside the Startup class:
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerManager) 
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
      
          app.ConfigureExceptionHandler(logger);
      
          app.UseHttpsRedirection();
      
          app.UseRouting();
      
          app.UseAuthorization();
      
          app.UseEndpoints(endpoints =>
          {
              endpoints.MapControllers();
          });
      }
      Finally, let’s remove the try-catch block from our code:
      public IActionResult Get()
      {
          _logger.LogInfo("Fetching all the Students from the storage");
      
           var students = DataManager.GetAllStudents(); //simulation for the data base access
      
           throw new Exception("Exception while fetching all the students from the storage.");
      
           _logger.LogInfo($"Returning {students.Count} students.");
      
           return Ok(students);
      }
      And there you go. Our action method is much cleaner now and what’s more important we can reuse this functionality to write more readable actions in the future. So let’s inspect the result: Global Handler Middleware And the log messages: log global handler middleware Excellent. Now, we are going to use a custom middleware for global error handling.

      Handling Errors Globally with the Custom Middleware

      Let’s create a new folder named CustomExceptionMiddleware and a class ExceptionMiddleware.cs inside it. We are going to modify that class:
      public class ExceptionMiddleware
      {
          private readonly RequestDelegate _next;
          private readonly ILoggerManager _logger;
      
          public ExceptionMiddleware(RequestDelegate next, ILoggerManager logger)
          {
              _logger = logger;
              _next = next;
          }
      
          public async Task InvokeAsync(HttpContext httpContext)
          {
              try
              {
                  await _next(httpContext);
              }
              catch (Exception ex)
              {
                  _logger.LogError($"Something went wrong: {ex}");
                  await HandleExceptionAsync(httpContext, ex);
              }
          }
      
          private Task HandleExceptionAsync(HttpContext context, Exception exception)
          {
              context.Response.ContentType = "application/json";
              context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
      
              return context.Response.WriteAsync(new ErrorDetails()
              {
                  StatusCode = context.Response.StatusCode,
                  Message = "Internal Server Error from the custom middleware."
              }.ToString());
          }
      }
      
      
      The first thing we need to do is to register our IloggerManager service and RequestDelegate through the dependency injection. The _next parameter of RequestDeleagate type is a function delegate which can process our HTTP requests. After the registration process, we need to create the InvokeAsync() method. RequestDelegate can't process requests without it. If everything goes well, the _next delegate should process the request and the Get action from our controller should generate the successful response. But if a request is unsuccessful (and it is, because we are forcing exception), our middleware will trigger the catch block and call the HandleExceptionAsync method. In that method, we just set up the response status code and content type and return a response. Now let’s modify our ExceptionMiddlewareExtensions class with another static method:
      public static void ConfigureCustomExceptionMiddleware(this IApplicationBuilder app)
      {
          app.UseMiddleware<ExceptionMiddleware>();
      }
      
      Finally, let’s use this method in the Configure method in the Startup class:
      //app.ConfigureExceptionHandler(logger);
      app.ConfigureCustomExceptionMiddleware();
      
      Great. Now let's inspect the result again: custom handler middleware There we go. Our custom middleware is implemented in a couple of steps.

      Conclusion

      That was awesome. We have learned, how to handle errors in a more sophisticated way and cleaner as well. The code is much more readable and our exception handling logic is now reusable for the entire project. Thank you for reading this article. We hope you have learned new useful things. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      3666 0 0 0 1156 0 0 1157 1156 0 314 0 0 315 async Task InvokeAsync method. Well inside you see a simple try catch block. So if you want your error to be more specific, all you have to do is to add another specific catch block (FileNotFoundException or SqlException...etc) before the global catch block. Also you can check for the error code of your exception variable. Thank you for that suggestion. All the best.]]> 314 0 353 0 0 379 0 0 380 379 0 390 0 0 391 0 0 392 390 0 393 391 0 394 393 0 395 (); services.AddMvc(); } As you can see, ILoggerManager is registered inside the IOC as singleton. And all you have to do is to inject it inside constructor, as we did in the ExceptionMiddleware class.]]> 394 0 398 392 0 448 0 0 449 448 0 450 449 0 451 450 0 487 0 0 488 487 0 559 http://huyenpk.com/create-asp-net-core-web-api-for-ethereum-dapps%e2%80%8a-%e2%80%8apart-3-restful-api/ 0 0 560 https://tech-mint.com/create-asp-net-core-web-api-for-ethereum-dapps%e2%80%8a-%e2%80%8apart-3-restful-api/ 0 0 615 Handling Errors Globally with the Built-In Middleware" you modify the Configure method inside the Startup class adding the line app.ConfigureCustomExceptionMiddleware();. Shouldn't it be app.ConfigureExceptionHandler()? ConfigureCustomExceptionMiddleware isn't created until the section "Handling Errors Globally with the Custom Middleware".]]> 0 0 622 0 0 627 0 0 646 0 0 661 How can I handle the exception to an action method for creating a user friendly message? I don't want to show to users only a simple message(content of JSON file)!]]> 0 0 670 0 0 689 622 0 690 615 0 712 646 0 713 712 0 721 661 0 725 670 0 737 https://wakeupandcode.com/handling-errors-in-asp-net-core/ 0 0 802 0 0 857 0 0 858 857 0 889 public async Task ActionMethodA( object a){ // if happens a special exception I want to send some information(JsonResult object) to the client. For example: " First Message!" // for another exception, I want to have default behavior } public async Task ActionMethodB( object b){ // if happens a special exception I want to send some information(JsonResult object) to the client. For example: " Second Message!" // for another exception, I want to have default behavior }]]> 0 0 890 889 0 974 0 0 975 974 0 984 0 0 985 984 0 1001 0 0 1002 974 0 1003 1001 0 1038 http://www.herlitz.nu/ app.ConfigureExceptionHandler() is sufficient, also this is GLOBAL error handling for asp.net core, it does affect page controllers as well as api controllers.]]> 0 0 1039 1038 0 1200 0 0 1201 1200 0 1386 HandleExceptionAsync method made as static?]]> 0 0 1387 1386 0 1614 NOT mark HandleExceptionAsync as static? It doesn't use any class member and thus should be marked static.]]> 1387 0 1615 1038 0 1616 basically this will only be used for any error that is not caught and handled elsewhere within the application. That is the entire point. This is only to catch unhandled exceptions. Any method with a try/catch where the catch does not re-throw will not bubble the exception up to the middleware (since it was handled in the method with the try/catch)]]> 627 0 1617 received from an external API vs. the status code you return to the client.]]> 670 0 1602 0 0 1603 1602 0 1618 1614 0 1812 0 0 1813 1812 0 1844 1813 0 1864 1844 0
      Continuous Integration with Jenkins and Docker https://code-maze.com/ci-jenkins-docker/ Mon, 30 Jul 2018 05:00:07 +0000 https://code-maze.com/?p=3834 how to use TeamCity to build, test and deploy a “dockerized” .NET Core Application. As Jenkins is one of the most popular CI tools on the market with over a thousand plugins, in this article, we are going to set up a CI pipeline for the same .NET Core Application. [sc name="part_of_series" headline="This article is part of the series"] So before we get right into it here are few highlights of what’s to come ahead. We are going to use Pipeline as a code plugin to create our Jenkins job. The cool part of using this plugin is that our entire Jenkins job configuration can be created, updated and version-controlled along with the rest of our source code. Along with that, we will be using the magical powers of docker and docker-compose to set up our entire CI infrastructure out of thin air! Exciting, right? We recommend that you follow along with us and get your hands dirty as its much more fun! Go ahead and fork the docker-series repo and switch over to docker-series-continuous-integration-jenkins-end branch. Here’s what we are going to learn this time: NOTE: Unlike other parts of this series, this part is using .NET Core 2.0 SDK. Make sure to change the base images according to your needs. Let's dive right into it.

      The High-Level Flowchart of CI Pipeline Using Jenkins

      Let's see what a “very” high-level view of our Continuous Integration (CI) Pipeline using Jenkins looks like: [caption id="attachment_3929" align="aligncenter" width="803"] High-Level CI flow using Jenkins[/caption] The flow diagram is easy to understand however, there are few things to note,
      • You may wonder as to where the code compilation and unit-test steps are? Well, the docker run step performs both the code compile and the application publish!
      • The dotted line marks the boundary of our CI tool, Jenkins, in this example
      • Having the docker push after a successful run of the “Integration Tests” ensures that only tested application is promoted to the next region, another very important rule of Continuous Integration (CI)

      Setting up CI Infrastructure-As-Code Using Docker

      Let's get into the fun stuff now. Setting up our Jenkins infrastructure! Although there are many things to love about the Jenkins docker image there is something that is..… Let's say “a little inconvenient” ? Let's see what that little thing is. When we run the following docker command:
      docker run -d -p 8080:8080 -p 50000:50000 jenkins/jenkins
      [caption id="attachment_3944" align="alignnone" width="1489"]Initial Setup Wizard Initial Setup Wizard[/caption] Jenkins welcomes us with the “Setup Wizard Screen” which requires us to enter the “InitialAdminPassword” located under the Jenkins_Home directory defaulted to /var/jenkins_home/secrets/initialAdminPassword. The password is displayed in the start-up logs as well: [caption id="attachment_3945" align="alignnone" width="1231"]Initial Admin Password Initial Admin Password[/caption] It's highly recommended to follow these steps to secure the Jenkins Server, however, in this example, we will be skipping this step by disabling it and creating a new user during run time. Let's get to it, shall we?

      Setting up a Custom Docker Image for Our Jenkins Master

      Let’s look at the below Dockerfile:
      # Starting off with the Jenkins base Image
      FROM jenkins/jenkins:latest
      
      # Installing the plugins we need using the in-built install-plugins.sh script
      RUN /usr/local/bin/install-plugins.sh git matrix-auth workflow-aggregator docker-workflow blueocean credentials-binding
      
      # Setting up environment variables for Jenkins admin user
      ENV JENKINS_USER admin
      ENV JENKINS_PASS admin
      
      # Skip the initial setup wizard
      ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false
      
      # Start-up scripts to set number of executors and creating the admin user
      COPY executors.groovy /usr/share/jenkins/ref/init.groovy.d/
      COPY default-user.groovy /usr/share/jenkins/ref/init.groovy.d/
      
      VOLUME /var/jenkins_home
      We start with the Jenkins/Jenkins base image and install the plugins that we require. Line# 12 is the run-time JVM parameter that needs to be passed in to disable the “Setup Wizard” Line# 15 and 16 is to provide the container with initial start-up scripts to set the Jenkins executors and for creating the Jenkins admin user. Let's build this image and keep it ready. We will get back to it right after we build our agent!
      docker build -t jenkins-master .

      Configuring a “Dockerized” Build Agent for Compiling Our Code

      As for the Jenkins build agent, we will make it “auto-attaching” to the Jenkins master using JLNP. Here is what the agent's Dockerfile looks like:
      FROM ubuntu:16.04
      
      # Install Docker CLI in the agent
      RUN apt-get update && apt-get install -y apt-transport-https ca-certificates
      RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
      RUN echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable" > /etc/apt/sources.list.d/docker.list
      RUN apt-get update && apt-get install -y docker-ce --allow-unauthenticated
      
      RUN apt-get update && apt-get install -y openjdk-8-jre curl python python-pip git
      RUN easy_install jenkins-webapi
      
      # Get docker-compose in the agent container
      RUN curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose
      RUN mkdir -p /home/jenkins
      RUN mkdir -p /var/lib/jenkins
      
      # Start-up script to attach the slave to the master
      ADD slave.py /var/lib/jenkins/slave.py
      
      WORKDIR /home/jenkins
      
      ENV JENKINS_URL "http://jenkins"
      ENV JENKINS_SLAVE_ADDRESS ""
      ENV JENKINS_USER "admin"
      ENV JENKINS_PASS "admin"
      ENV SLAVE_NAME ""
      ENV SLAVE_SECRET ""
      ENV SLAVE_EXECUTORS "1"
      ENV SLAVE_LABELS "docker"
      ENV SLAVE_WORING_DIR ""
      ENV CLEAN_WORKING_DIR "true"
      
      CMD [ "python", "-u", "/var/lib/jenkins/slave.py" ]
      Let's look at some important steps in the above file, Line# 13 takes care of downloading and installing “docker-compose” in our build agent Line# 18 takes care of adding the magical start-up python script responsible for attaching to our master as a build agent! Now, who likes to go through a 90 line python script? Not me ?! To make things easier let’s look at this simple flowchart to understand this! [caption id="attachment_3956" align="aligncenter" width="154"]Slave start-up script Slave start-up script[/caption] The “wait for the master” logic is going to come in very handy when we wrap the master and slave into a docker-compose file. The depends_on: tag in docker-compose doesn't serve as well, as the jenkins master takes more time to be fully up and running than what docker-compose estimates it to be. Thus, it's added intelligence to our slave. Let's build this container and warp our Jenkins infrastructure into a docker-compose file!

      Building an Image and Exposing the Docker Daemon to the Agent

      docker build -t jenkins-slave .
      version: '3.1'
      services:
          jenkins:
              container_name: jenkins
              ports:
                  - '8080:8080'
                  - '50000:50000'
              image: localhost:5000/jenkins
          jenkins-slave:
              container_name: jenkins-slave
              restart: always
              environment:
                  - 'JENKINS_URL=http://jenkins:8080'
              image: localhost:5000/jenkins-slave
              volumes:
                  - /var/run/docker.sock:/var/run/docker.sock  # Expose the docker daemon in the container
                  - /home/jenkins:/home/jenkins # Avoid mysql volume mount issue
              depends_on:
                  - jenkins
      The Line# 17 is significant here as the mounted volume will help fix the docker in docker volume mount issue. Its covered well in our previous article here Great! Now its time to launch our Jenkins Infrastructure with the docker-compose up command
      docker-compose -f .\docker-compose.ci.yml up
      Here are the docker-compose logs for the container start-up: [caption id="attachment_3963" align="alignnone" width="1082"]docker-compose startup logs docker-compose startup logs[/caption] Label 1 indicates that Jenkins container has started however, it's not fully up and running. Thus, our wait logic coming in handy. [caption id="attachment_3964" align="aligncenter" width="1067"]Agent Connected Agent Connected[/caption] And finally, our agent has successfully connected to our Jenkins master. We should be able to access the Jenkins web UI at port 8080 of our localhost! And guess what, no initial startup wizard too! [caption id="attachment_3966" align="aligncenter" width="1920"]Jenkins Logon Jenkins Logon[/caption] Here, we log in with the initial admin user credentials which are admin/admin. After that Jenkins takes us to the dashboard where we can see our docker build agent ready to take on some build tasks! [caption id="attachment_3968" align="aligncenter" width="1076"]Jenkins dashboard Jenkins dashboard[/caption]

      Creating a Pipeline-As-A-Code Job in Jenkins

      Let's start by creating a "New Item" and save it as a pipeline job as shown below: Next step is to update the job configurations with, Job description, SCM url and the branch as shown below: [caption id="attachment_4069" align="alignnone" width="1348"]Configure Project Configure Project[/caption]

      Writing a Jenkins File

      The Jenkins file is a groovy based script that lists the different stages and steps of the Jenkins build. The benefits of this approach over using a freestyle job revolves mainly around flexibility and the ability to be able to version control. Let’s discuss a little more about these 2 points, Flexibility: Usually, a freestyle job will be created to accomplish a specific task in your CI pipeline, it could be to compile our code, run integration tests or deploy our application. However, a true CI includes all those 3 steps and chains them together in a sequential or parallel manner. This is what we call a “Pipeline”. It is possible to achieve chaining by using freestyle jobs but at the end of the day, it's not very convenient for a single Application. The pipeline would consist of a bunch of freestyle jobs connected in an upstream-downstream fashion. Communicating amongst these jobs for eg: sharing variables, custom statuses can be a nightmare. All these problems go away with Pipeline Jobs! Version Control your job configurations: As previously mentioned, the Jenkinsfile is just a groovy script thus, it can be stored, edited and version-controlled along with the rest of the application code! Before we go ahead and start writing our Jenkinsfile, let's visualize the steps we need to build and publish this application: [caption id="attachment_3979" align="aligncenter" width="561"]Build Steps Build Steps[/caption] The Jenkins pipeline syntax generator helps us a lot in building our Jenkinsfile line by line. Here are some of the examples: [caption id="attachment_3983" align="aligncenter" width="1920"]Pipline Syntax Generator Pipeline Syntax Generator[/caption] Here is what our Jenkinsfile looks like:
      node('docker') {
      
          stage 'Checkout'
              checkout scm
          stage 'Build & UnitTest'
              sh "docker build -t accountownerapp:B${BUILD_NUMBER} -f Dockerfile ."
              sh "docker build -t accountownerapp:test-B${BUILD_NUMBER} -f Dockerfile.Integration ."
        
          stage 'Integration Test'
              sh "docker-compose -f docker-compose.integration.yml up --force-recreate --abort-on-container-exit"
              sh "docker-compose -f docker-compose.integration.yml down -v"
      }
      Let's break it break it down here: Line#1: The node keyword is used to select the build agent Line 4, 5, 9: The stage keyword is used to define the stages in our build

      Dynamic Build Versions

      One of the things that we have fast-forwarded is "tokenizing" image versions with the Jenkins build number. Jenkins exposes BUILD_NUMBER as an environment variable amongst others :) Each new build, auto-increments the version. To support this, the docker-compose.integration.yml file is also "tokenized" in the same fashion.
      version: '3.1'
      
      services:
        db:
          image: mysql:5.7
          environment:
            MYSQL_RANDOM_ROOT_PASSWORD: 1
            MYSQL_DATABASE: accountowner
            MYSQL_USER: dbuser
            MYSQL_PASSWORD: dbuserpassword
            DEBUG: 1
          volumes:
            - dbdata:/var/lib/mysql
            - ./_MySQL_Init_Script:/docker-entrypoint-initdb.d
          restart: always
        accountownerapp:
          depends_on:
            - db
          image: "accountownerapp:B${BUILD_NUMBER}"
          build:
            context: .
        integration:
          depends_on:
            - accountownerapp
          image: "accountownerapp:test-B${BUILD_NUMBER}"
          build:
            context: .
            dockerfile: Dockerfile.Integration
          environment:
            - TEAMCITY_PROJECT_NAME
      volumes:
          dbdata:
      The following link shows all the Jenkins Environment Variables: http:///env-vars.html/ The rest of the steps are just shell scripts. Let's build our Jenkins job!

      Building the Application

      The "Build Now" link triggers a new build: [caption id="attachment_3986" align="aligncenter" width="1111"]Build Progress Build Progress[/caption] And here we have our first successful job! The following two snapshots show the successful job and the logs of the Build & UnitTest stage: [caption id="attachment_3988" align="aligncenter" width="662"]Successful Build Successful Build![/caption] [caption id="attachment_3987" align="aligncenter" width="818"]Stage Logs Stage Logs[/caption]

      Running Tests and Publishing Reports in Jenkins

      We have successfully executed tests in the previous build. One of the benefits of using Jenkins is the post-build report publishing feature which helps us collate our test results and publish them as HTML reports! We will be using the MS Test plugin to publish reports but, before that, there are two problems we need to address. Problem 1: The container stores the results of the tests that it executes within itself. Problem 2: Even if we publish the report, how are we going to make it available outside the application container and for Jenkins MS test plugin to read from? Let's tackle them one by one, Solution 1: let's update the Dockerfile to publish the results and store them in a folder within the container
      FROM microsoft/aspnetcore-build as build-image
      
      WORKDIR /home/app
      
      COPY ./*.sln ./
      COPY ./*/*.csproj ./
      RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done
      
      RUN dotnet restore
      
      COPY . .
      
      RUN dotnet test --verbosity=normal --results-directory /TestResults/ --logger "trx;LogFileName=test_results.xml" ./Tests/Tests.csproj
      
      RUN dotnet publish ./AccountOwnerServer/AccountOwnerServer.csproj -o /publish/
      
      FROM microsoft/aspnetcore
      
      WORKDIR /publish
      
      COPY --from=build-image /publish .
      
      COPY --from=build-image /TestResults /TestResults
      
      ENV TEAMCITY_PROJECT_NAME = ${TEAMCITY_PROJECT_NAME}
      
      ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"]
      Line # 13 Additional command line parameters are added to publish the logs and to store it under the /TestResults folder. Note that the test results folder is still within the container Solution 2: Here we will be using some docker magic to copy the results out of the container We can effectively use the docker cp command to copy content out of the container, however, it requires the running container. Not a big deal, we can use some shell script to tackle that. Here is the updated Jenkinsfile with a dedicated stage to Publish test results:
      node('docker') {
      
          stage 'Checkout'
              checkout scm
          stage 'Build & UnitTest'
          sh "docker build -t accountownerapp:B${BUILD_NUMBER} -f Dockerfile ."
          sh "docker build -t accountownerapp:test-B${BUILD_NUMBER} -f Dockerfile.Integration ."
          
          stage 'Pusblish UT Reports'
              containerID = sh (
                  script: "docker run -d accountownerapp:B${BUILD_NUMBER}", 
              returnStdout: true
              ).trim()
              echo "Container ID is ==> ${containerID}"
              sh "docker cp ${containerID}:/TestResults/test_results.xml test_results.xml"
              sh "docker stop ${containerID}"
              sh "docker rm ${containerID}"
              step([$class: 'MSTestPublisher', failOnError: false, testResultsFile: 'test_results.xml'])    
            
          stage 'Integration Test'
              sh "docker-compose -f docker-compose.integration.yml up --force-recreate --abort-on-container-exit"
              sh "docker-compose -f docker-compose.integration.yml down -v"
      }
      The new stage consists of shell steps to run the container, copy the test results back to the build agent and publish the report. Let's go ahead and execute this! [caption id="attachment_3992" align="aligncenter" width="892"]Build Status Build Status[/caption] The Jenkins build page shows the published test-results, [caption id="attachment_3994" align="aligncenter" width="1370"]Test Results Test Results[/caption]

      Conclusion

      Wow! Here we are at the conclusion of our article. Integrating Jenkins is almost seamless with any existing project-lifecycle due to the abundant library of plugins and free documentation all over the internet. What we've seen here is just a small portion of what Jenkins has to offer as a CI tool. In this article, we focused mostly on Jenkins as a CI tool, we haven't changed our application code much except the docker file update to accommodate the Test Results. The Continuous Integration with TeamCity and Docker article covers the addition of integration tests in greater detail. Do read it to get the complete picture. The entire project is available under docker-series GitHub repository under the docker-series-continuous-integration-jenkins-end branch. Feel free to go through it and ask for any help under the comments section. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      3834 0 0 0 docker-series-continuous-integration-jenkins-end branch

      We’ve discussed why continuous integration is important, what makes a good CI tool and we've learned how to use TeamCity to build, test and deploy a “dockerized” .NET Core Application.

      As Jenkins is one of the most popular CI tools in the market with over a thousand plugins, in this article, we are going to set up a CI pipeline for the same .NET Core Application.

      So before we get right into it here are few highlights of what’s to come ahead.

      We are going to use Pipeline as a code plugin to create our Jenkins job. The cool part of using this plugin is that our entire Jenkins job configuration can be created, updated and version controlled along with the rest of our source code. Along with that, we will be using the magical powers of docker and docker-compose to set up our entire CI infrastructure out of thin air!

      Exciting, right?

      We recommend that you follow along with us and get your hands dirty as its much more fun!

      Go ahead and fork the docker-series repo and switch over to docker-series-continuous-integration-jenkins-end branch.

      Here’s what we are going to learn this time:

      Let's dive right into it.

      The High-Level Flowchart of CI Pipeline Using Jenkins

      Let's see what a “very” high-level view of our Continuous Integration (CI) Pipeline using Jenkins looks like:

      [caption id="attachment_3929" align="aligncenter" width="803"] High-Level CI flow using Jenkins[/caption]

      The flow diagram is easy to understand however, there are few things to note,

      • You may wonder as to where the code compilation and unit-test steps are? Well, the docker run step performs both the code compile and the application publish!
      • The dotted line marks the boundary of our CI tool, Jenkins, in this example
      • Having the docker push after a successful run of the “Integration Tests” ensures that only tested application is promoted to the next region, another very important rule of Continuous Integration (CI)

      Setting up CI Infrastructure-As-Code Using Docker

      Let's get into the fun stuff now. Setting up our Jenkins infrastructure!

      Although there are many things to love about the Jenkins docker image there is something that is..… Let's say “a little inconvenient” ?

      Let's see what that little thing is.

      When we run the following docker command:

      docker run -d -p 8080:8080 -p 50000:50000 jenkins/jenkins
      [caption id="attachment_3944" align="alignnone" width="1489"]Initial Setup Wizard Initial Setup Wizard[/caption]

      Jenkins welcomes us with the “Setup Wizard Screen” which requires us to enter the “InitialAdminPassword” located under the Jenkins_Home directory defaulted to /var/jenkins_home/secrets/initialAdminPassword. The password is displayed in the start-up logs as well:

      [caption id="attachment_3945" align="alignnone" width="1231"]Initial Admin Password Initial Admin Password[/caption]

      It's highly recommended to follow these steps to secure the Jenkins Server, however, in this example, we will be skipping this step by disabling it and creating a new user during run time.

      Let's get to it, shall we?

      Setting up a Custom Docker Image for Our Jenkins Master

      Let’s look at the below Dockerfile:

      # Starting off with the Jenkins base Image
      FROM jenkins/jenkins:latest
      
      # Installing the plugins we need using the in-built install-plugins.sh script
      RUN /usr/local/bin/install-plugins.sh git matrix-auth workflow-aggregator docker-workflow blueocean credentials-binding
      
      # Setting up environment variables for Jenkins admin user
      ENV JENKINS_USER admin
      ENV JENKINS_PASS admin
      
      # Skip the initial setup wizard
      ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false
      
      # Start-up scripts to set number of executors and creating the admin user
      COPY executors.groovy /usr/share/jenkins/ref/init.groovy.d/
      COPY default-user.groovy /usr/share/jenkins/ref/init.groovy.d/
      
      VOLUME /var/jenkins_home

      We start with the Jenkins/Jenkins base image and install the plugins that we require.

      Line# 12 is the run-time JVM parameter that needs to be passed in to disable the “Setup Wizard”

      Line# 15 and 16 is to provide the container with initial start-up scripts to set the Jenkins executors and for creating the jenkins admin user.

      Let's build this image and keep it ready. We will get back to it right after we build our agent!

      docker build -t jenkins-master .

      Configuring a “Dockerized” Build Agent for Compiling Our Code

      As for the Jenkins build agent, we will make it “auto-attaching” to the Jenkins master using JLNP.

      Here is what the agent's Dockerfile looks like:

      FROM ubuntu:16.04
      
      # Install Docker CLI in the agent
      RUN apt-get update && apt-get install -y apt-transport-https ca-certificates
      RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
      RUN echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable" > /etc/apt/sources.list.d/docker.list
      RUN apt-get update && apt-get install -y docker-ce --allow-unauthenticated
      
      RUN apt-get update && apt-get install -y openjdk-8-jre curl python python-pip git
      RUN easy_install jenkins-webapi
      
      # Get docker-compose in the agent container
      RUN curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose
      RUN mkdir -p /home/jenkins
      RUN mkdir -p /var/lib/jenkins
      
      # Start-up script to attach the slave to the master
      ADD slave.py /var/lib/jenkins/slave.py
      
      WORKDIR /home/jenkins
      
      ENV JENKINS_URL "http://jenkins"
      ENV JENKINS_SLAVE_ADDRESS ""
      ENV JENKINS_USER "admin"
      ENV JENKINS_PASS "admin"
      ENV SLAVE_NAME ""
      ENV SLAVE_SECRET ""
      ENV SLAVE_EXECUTORS "1"
      ENV SLAVE_LABELS "docker"
      ENV SLAVE_WORING_DIR ""
      ENV CLEAN_WORKING_DIR "true"
      
      CMD [ "python", "-u", "/var/lib/jenkins/slave.py" ]

      Let's look at some important steps in the above file,

      Line# 13 takes care of downloading and installing “docker-compose” in our build agent

      Line# 18 takes care of adding the magical start-up python script responsible for attaching to our master as a build agent!

      Now, who likes to go through a 90 line python script? Not me ?! To make things easier let’s look at this simple flowchart to understand this!

      [caption id="attachment_3956" align="aligncenter" width="154"]Slave start-up script Slave start-up script[/caption]

      The “wait for the master” logic is going to come in very handy when we wrap the master and slave into a docker-compose file. The depends_on: tag in docker-compose doesn't serve as well, as the jenkins master takes more time to be fully up and running than what docker-compose estimates it to be. Thus, it's added intelligence to our slave.

      Let's build this container and warp our Jenkins infrastructure into a docker-compose file!

      Building an Image and Exposing the Docker Daemon to the Agent

      docker build -t jenkins-slave .
      version: '3.1'
      services:
          jenkins:
              container_name: jenkins
              ports:
                  - '8080:8080'
                  - '50000:50000'
              image: localhost:5000/jenkins
          jenkins-slave:
              container_name: jenkins-slave
              restart: always
              environment:
                  - 'JENKINS_URL=http://jenkins:8080'
              image: localhost:5000/jenkins-slave
              volumes:
                  - /var/run/docker.sock:/var/run/docker.sock  # Expose the docker daemon in the container
                  - /home/jenkins:/home/jenkins # Avoid mysql volume mount issue
              depends_on:
                  - jenkins

      The Line# 17 is significant here as the mounted volume will help fix the docker in docker volume mount issue. Its covered well in our previous article here

      Great!

      Now its time to launch our Jenkins Infrastructure with the docker-compose up command

      docker-compose -f .\docker-compose.ci.yml up

      Here are the docker-compose logs for the container start-up:

      [caption id="attachment_3963" align="alignnone" width="1082"]docker-compose startup logs docker-compose startup logs[/caption]

      Label 1 indicates that Jenkins container has started however, it's not fully up and running. Thus, our wait logic coming in handy.

      [caption id="attachment_3964" align="aligncenter" width="1067"]Agent Connected Agent Connected[/caption]

      And finally, our agent has successfully connected to our Jenkins master. We should be able to access the Jenkins web UI at port 8080 of our localhost!

      And guess what, no initial startup wizard too!

      [caption id="attachment_3966" align="aligncenter" width="1920"]Jenkins Logon Jenkins Logon[/caption]

      Here, we log in with the initial admin user credentials which are admin/admin. After that Jenkins takes us to the dashboard where we can see our docker build agent ready to take on some build tasks!

      [caption id="attachment_3968" align="aligncenter" width="1076"]Jenkins dashboard Jenkins dashboard[/caption]

      Creating a Pipeline-As-A-Code Job in Jenkins

      Let's start by creating a "New Item" and save it as a pipeline job as shown below:

      Next step is to update the job configurations with, Job description, SCM url and the branch as shown below:

      [caption id="attachment_4069" align="alignnone" width="1348"]Configure Project Configure Project[/caption]

      Writing a Jenkins File

      The Jenkins file is a groovy based script which lists the different stages and steps of the Jenkins build. The benefits of this approach over using a freestyle job revolves mainly around flexibility and the ability to be able to version control.

      Let’s discuss a little more about these 2 points,

      Flexibility:

      Usually, a freestyle job will be created to accomplish a specific task in your CI pipeline, it could be to compile our code, run integration tests or deploy our application.

      However, a true CI includes all those 3 steps and chains them together in a sequential or parallel manner. This is what we call a “Pipeline”.

      It is possible to achieve chaining by using the freestyle jobs but at end of the day, it's not very convenient for a single Application. The pipeline would consist of a bunch of freestyle jobs connected in an upstream-downstream fashion. Communicating amongst these jobs for eg: sharing variables, custom statuses can be a nightmare.

      All these problems go away with Pipeline Jobs!

      Version Control your job configurations:

      As previously mentioned, the Jenkinsfile is just a groovy script thus, it can be stored, edited and version controlled along with the rest of the application code!

      Before we go ahead and start writing our Jenkinsfile, let's visualize the steps we need to build and publish this application:

      [caption id="attachment_3979" align="aligncenter" width="561"]Build Steps Build Steps[/caption]

      The Jenkins pipeline syntax generator helps us a lot in building our Jenkinsfile line by line. Here are some of the examples:

      [caption id="attachment_3983" align="aligncenter" width="1920"]Pipline Syntax Generator Pipline Syntax Generator[/caption]

      Here is what our Jenkinsfile looks like:

      node('docker') {
      
          stage 'Checkout'
              checkout scm
          stage 'Build & UnitTest'
              sh "docker build -t accountownerapp:B${BUILD_NUMBER} -f Dockerfile ."
              sh "docker build -t accountownerapp:test-B${BUILD_NUMBER} -f Dockerfile.Integration ."
        
          stage 'Integration Test'
              sh "docker-compose -f docker-compose.integration.yml up --force-recreate --abort-on-container-exit"
              sh "docker-compose -f docker-compose.integration.yml down -v"
      }

      Let's break it break it down here:

      Line#1: The node keyword is used to select the build agent

      Line 4, 5, 9: The stage keyword is used to define the stages in our build

      Dynamic Build Versions

      One of the things that we have fast-forwarded is "tokenizing" image versions with the Jenkins build number. Jenkins exposes BUILD_NUMBER as an environment variable amongst others :)

      Each new build, auto-increments the version. To support this, the docker-compose.integration.yml file is also "tokenized" in the same fashion.

      version: '3.1'
      
      services:
        db:
          image: mysql:5.7
          environment:
            MYSQL_RANDOM_ROOT_PASSWORD: 1
            MYSQL_DATABASE: accountowner
            MYSQL_USER: dbuser
            MYSQL_PASSWORD: dbuserpassword
            DEBUG: 1
          volumes:
            - dbdata:/var/lib/mysql
            - ./_MySQL_Init_Script:/docker-entrypoint-initdb.d
          restart: always
        accountownerapp:
          depends_on:
            - db
          image: "accountownerapp:B${BUILD_NUMBER}"
          build:
            context: .
        integration:
          depends_on:
            - accountownerapp
          image: "accountownerapp:test-B${BUILD_NUMBER}"
          build:
            context: .
            dockerfile: Dockerfile.Integration
          environment:
            - TEAMCITY_PROJECT_NAME
      volumes:
          dbdata:

      The following link shows all the Jenkins Environment Variables: http:///env-vars.html/

      The rest of the steps are just shell scripts.

      Let's build our Jenkins job!

      Building the Application

      The "Build Now" link triggers a new build:

      [caption id="attachment_3986" align="aligncenter" width="1111"]Build Progress Build Progress[/caption]

      And here we have our first successful job! The following two snapshots show the successful job and the logs of the Build & UnitTest stage:

      [caption id="attachment_3988" align="aligncenter" width="662"]Successful Build Successful Build![/caption][caption id="attachment_3987" align="aligncenter" width="818"]Stage Logs Stage Logs[/caption]

      Running Tests and Publishing Reports in Jenkins

      We have successfully executed tests in the previous build. One of the benefits of using Jenkins is the post-build report publishing feature which helps us collate our test results and publish them as HTML reports!

      We will be using the MS Test plugin to publish reports but, before that, there are two problems we need to address.

      Problem 1: The container stores the results of the tests that it executes within itself.

      Problem 2: Even if we publish the report, how are we going to make it available outside the application container and for Jenkins MS test plugin to read from?

      Let's tackle them one by one,

      Solution 1: let's update the Dockerfile to publish the results and store them in a folder within the container

      FROM microsoft/aspnetcore-build as build-image
      
      WORKDIR /home/app
      
      COPY ./*.sln ./
      COPY ./*/*.csproj ./
      RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done
      
      RUN dotnet restore
      
      COPY . .
      
      RUN dotnet test --verbosity=normal --results-directory /TestResults/ --logger "trx;LogFileName=test_results.xml" ./Tests/Tests.csproj
      
      RUN dotnet publish ./AccountOwnerServer/AccountOwnerServer.csproj -o /publish/
      
      FROM microsoft/aspnetcore
      
      WORKDIR /publish
      
      COPY --from=build-image /publish .
      
      COPY --from=build-image /TestResults /TestResults
      
      ENV TEAMCITY_PROJECT_NAME = ${TEAMCITY_PROJECT_NAME}
      
      ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"]

      Line # 13 Additional command line parameters are added to publish the logs and to store it under the /TestResults folder.

      Note that the test results folder is still within the container

      Solution 2: Here we will be using some docker magic to copy the results out of the container

      We can effectively use the docker cp command to copy content out of the container, however, it requires the running container. Not a big deal, we can use some shell script to tackle that.

      Here is the updated Jenkinsfile with a dedicated stage to Publish test results:

      node('docker') {
      
          stage 'Checkout'
              checkout scm
          stage 'Build & UnitTest'
          sh "docker build -t accountownerapp:B${BUILD_NUMBER} -f Dockerfile ."
          sh "docker build -t accountownerapp:test-B${BUILD_NUMBER} -f Dockerfile.Integration ."
          
          stage 'Pusblish UT Reports'
              containerID = sh (
                  script: "docker run -d accountownerapp:B${BUILD_NUMBER}", 
              returnStdout: true
              ).trim()
              echo "Container ID is ==> ${containerID}"
              sh "docker cp ${containerID}:/TestResults/test_results.xml test_results.xml"
              sh "docker stop ${containerID}"
              sh "docker rm ${containerID}"
              step([$class: 'MSTestPublisher', failOnError: false, testResultsFile: 'test_results.xml'])    
            
          stage 'Integration Test'
              sh "docker-compose -f docker-compose.integration.yml up --force-recreate --abort-on-container-exit"
              sh "docker-compose -f docker-compose.integration.yml down -v"
      }

      The new stage consists of shell steps to run the container, copy the test results back to the build agent and publish the report. Let's go ahead and execute this!

      [caption id="attachment_3992" align="aligncenter" width="892"]Build Status Build Status[/caption]

      The Jenkins build page shows the published test-results,

      [caption id="attachment_3994" align="aligncenter" width="1370"]Test Results Test Results[/caption]

      Conclusion

      Wow!

      Here we are at the conclusion of our article. Integrating Jenkins is almost seamless with any existing project-lifecycle due to the abundant library of plugins and free documentation all over the internet. What we've seen here is just a small portion of what Jenkins has to offer as a CI tool.

      In this article, we focused mostly on Jenkins as a CI tool, we haven't changed our application code much except the docker file update to accommodate the Test Results. The Continuous Integration with TeamCity and Docker article covers the addition of integration tests in greater details. Do read it to get the complete picture.

      The entire project is available under docker-series GitHub repository under the docker-series-continuous-integration-jenkins-end branch. Feel free to go through it and ask for any help under the comments section.

      [sc name="subscribe" formNumber="2797" contentType=".NET Core"]

      ]]>
      288 https://www.alvinashcraft.com/2018/07/30/dew-drop-july-30-2018-2776/ 0 0 322 0 0 326 322 0 419 https://sudhakaryblog.wordpress.com/2018/10/04/docker-for-windows-series-articles/ 0 0 513 0 0 514 513 0 591 https://www.baeldung.com/java-weekly-240 0 0 650 COPY failed: stat /var/snap/docker/common/var-lib-docker/tmp/docker-builder653425281/executors.groovy: no such file or directory ]]> 0 0 655 0 0 658 jenkins-slave | slave_create(slave_name, os.getcwd(), os.environ['SLAVE_EXECUTORS'], os.environ['SLAVE_LABELS']) jenkins-slave | File "/var/lib/jenkins/slave.py", line 26, in slave_create jenkins-slave | j = Jenkins(os.environ['JENKINS_URL'], os.environ['JENKINS_USER'], os.environ['JENKINS_PASS']) jenkins-slave | File "build/bdist.linux-x86_64/egg/jenkins.py", line 574, in __init__ jenkins-slave | File "build/bdist.linux-x86_64/egg/jenkins.py", line 616, in crumb_header jenkins-slave | File "build/bdist.linux-x86_64/egg/jenkins.py", line 605, in crumb jenkins-slave | File "build/bdist.linux-x86_64/egg/jenkins.py", line 559, in json jenkins-slave | File "/usr/local/lib/python2.7/dist-packages/requests-2.21.0-py2.7.egg/requests/models.py", line 940, in raise_for_status jenkins-slave | raise HTTPError(http_error_msg, response=self) jenkins-slave | requests.exceptions.HTTPError: 401 Client Error: Invalid password/token for user: admin for url: http://jenkins:8080/crumbIssuer/api/json jenkins-slave exited with code 1 as I understand Jenkins initial password can not be read meanwile it's properly generated ************************************************************* jenkins-master | jenkins-master | Jenkins initial setup is required. An admin user has been created and a password generated. jenkins-master | Please use the following password to proceed to installation: jenkins-master | jenkins-master | b0b71579d894481c92ba5865292db953 jenkins-master | jenkins-master | This may also be found at: /var/jenkins_home/secrets/initialAdminPassword jenkins-master | jenkins-master | ************************************************************* Could you please advise how the issue might be resolved? Thank you in advance and best regards Albert]]> 0 0 662 link : As per line 5 and 6 the admin user credentials must be set to be referred by default-user.groovy for creating the initial admin user As per this line, the initial setup wizard must not appear but, looking at the logs that you have shared that doesn't seem to be the case(Thus confirming my suspicion) Here, is my recommended steps for further troubleshooting, perform a docker exec containerID sh to inspect the environment variables and check if they are set correctly. Let me know what you find :)]]> 658 0 709 docker build makes sure that you provide the right context, which mean you build from the right folder. In this example where you are building docker master, run it from the following folder`project root/jenkins-docker/master. The executors.groovy is at the same level as the docker file. Let me know how that goes.]]> 650 0 710 655 0 720 8080/tcp, 0.0.0.0:50000->50000/tcp jenkins-master And in container "jenkins-master" that is running image "jenkins" these parameter are not exist. At this point I've noticed that image "jenkins" was not mentione and build before. So in line "8 image: localhost:5000/jenkins" instead of "lenkins" I used "jenkins-master" image. And now it woks properly). Thank you very much for such helpful and really useful post and assistance! Best regards, Albert]]> 662 0 745 0 0 1205 709 0 1229 1205 0 1521 jenkins-slave | slave_create(slave_name, os.getcwd(), os.environ['SLAVE_EXECUTORS'], os.environ['SLAVE_LABELS']) jenkins-slave | File "/var/lib/jenkins/slave.py", line 27, in slave_create jenkins-slave | j.node_create(node_name, working_dir, num_executors = int(executors), labels = labels, launcher = NodeLaunchMethod.JNLP) jenkins-slave | File "build/bdist.linux-x86_64/egg/jenkins.py", line 785, in node_create jenkins-slave | File "build/bdist.linux-x86_64/egg/jenkins.py", line 436, in create jenkins-slave | jenkins.JenkinsError: create "docker-slave-b0b3b5654850" failed jenkins-slave exited with code 1]]> 0 0 1530 jenkins-slave | slave_create(slave_name, os.getcwd(), os.environ['SLAVE_EXECUTORS'], os.environ['SLAVE_LABELS']) jenkins-slave | File "/var/lib/jenkins/slave.py", line 26, in slave_create jenkins-slave | j = Jenkins(os.environ['JENKINS_URL'], os.environ['JENKINS_USER'], os.environ['JENKINS_PASS']) jenkins-slave | File "build/bdist.linux-x86_64/egg/jenkins.py", line 574, in __init__ jenkins-slave | File "build/bdist.linux-x86_64/egg/jenkins.py", line 616, in crumb_header jenkins-slave | File "build/bdist.linux-x86_64/egg/jenkins.py", line 605, in crumb jenkins-slave | File "build/bdist.linux-x86_64/egg/jenkins.py", line 559, in json jenkins-slave | File "/usr/local/lib/python2.7/dist-packages/requests-2.22.0-py2.7.egg/requests/models.py", line 940, in raise_for_status jenkins-slave | raise HTTPError(http_error_msg, response=self) jenkins-slave | requests.exceptions.HTTPError: 401 Client Error: Invalid password/token for user: admin for url: http://jenkins:8080/crumbIssuer/api/json Any help much appreciated. Thanks]]> 0 0 1537 0 0
      Unit Testing in ASP.NET Core Web API https://code-maze.com/unit-testing-aspnetcore-web-api/ Mon, 16 Jul 2018 06:00:01 +0000 https://code-maze.com/?p=3847 Unit testing plays a very important role in making the software more maintainable. Our intention in this post is to make an intro to unit testing of ASP.NET Core Web API application. [sc name="part_of_series" headline="Recommended Articles"] You can download the source code from the repo on GitHub. In this article, we are going to talk about different segments of unit testing:

      About Unit Testing in General

      What is unit testing in the first place? It begins by defining what a „unit“ is and although this is not strictly defined, the unit represents a unit of work - usually a single method in our code. We test these units individually, making sure that each of them is doing exactly that what it is written for. Nothing more, nothing less. What is important to understand is that we are not testing the behavior of the dependencies of that method. That is what the integration tests are for. [learn_more caption="Recommendation for the additional testing knowledge" state="open"] We have a great series of articles dedicated to Testing ASP.NET Core Application. So, if you want to learn even more about the testing, we strongly recommend reading ASP.NET Core MVC Testing Series.[/learn_more]

      Preparing the Example Project

      We will use Visual Studio 2019 to create our example project and it will be ASP.NET Core Web API application. Let’s start by creating a new ASP.NET Core Web Application: starting project - unit testing with .NET Core After choosing the name for our solution and the project which is web-api, we get to chose which type of web application we want to create. In our case that would be the API project: choosing project - unit testing with .NET Core When we create ASP.NET Core API application initially, it comes with the default controller class. Let’s delete that one and create our own example controller named ShoppingCartController. Here we can define CRUD operations you would typically have on an entity based controller:
      [Route("api/[controller]")]
      [ApiController]
      public class ShoppingCartController : ControllerBase
      {
          private readonly IShoppingCartService _service;
      
          public ShoppingCartController(IShoppingCartService service)
          {
              _service = service;
          }
      
          // GET api/shoppingcart
          [HttpGet]
          public ActionResult<IEnumerable<ShoppingItem>> Get()
          {
              var items = _service.GetAllItems();
              return Ok(items);
          }
      
          // GET api/shoppingcart/5
          [HttpGet("{id}")]
          public ActionResult<ShoppingItem> Get(Guid id)
          {
              var item = _service.GetById(id);
      
              if (item == null)
              {
                  return NotFound();
              }
      
              return Ok(item);
          }
      
          // POST api/shoppingcart
          [HttpPost]
          public ActionResult Post([FromBody] ShoppingItem value)
          {
              if (!ModelState.IsValid)
              {
                  return BadRequest(ModelState);
              }
      
              var item = _service.Add(value);
              return CreatedAtAction("Get", new { id = item.Id }, item);
          }
      
          // DELETE api/shoppingcart/5
          [HttpDelete("{id}")]
          public ActionResult Remove(Guid id)
          {
              var existingItem = _service.GetById(id);
      
              if (existingItem == null)
              {
                  return NotFound();
              }
      
              _service.Remove(id);
              return Ok();
          }
      }
      
      Nothing special about the code here, we’ve got a simple example of a shopping cart controller where we have methods to get, add and remove items from the cart.

      ShoppingCartService Explanation

      To access the data source, we are using ShoppingService class which implements IShoppingService interface. This allows us to follow the dependency injection principle, which is used heavily for the purpose of unit testing. Using dependency injection, we can inject whatever implementation of IShoppingCart interface we want into our test class. Please note that methods of the service are not implemented in the example project, because we are not focusing on the service implementation here, testing the controller is the main goal. In the real project, you would probably use some data access logic in your service methods:
      public class ShoppingCartService : IShoppingCartService
      {
          public ShoppingItem Add(ShoppingItem newItem)
          {
              throw new NotImplementedException();
          }
      
          public IEnumerable<ShoppingItem> GetAllItems()
          {
              throw new NotImplementedException();
          }
      
          public ShoppingItem GetById(Guid id)
          {
              throw new NotImplementedException();
          }
      
          public void Remove(Guid id)
          {
              throw new NotImplementedException();
          }
      }
      
      IShoppingService contains signatures of all the methods seen in the ShoppingCartService:
      public interface IShoppingCartService
      {
          IEnumerable<ShoppingItem> GetAllItems();
          ShoppingItem Add(ShoppingItem newItem);
          ShoppingItem GetById(Guid id);
          void Remove(Guid id);
      }
      
      ShoppingItem is our main (and only :) ) entity with just a few fields:
      public class ShoppingItem
      {
          public Guid Id { get; set; }
          [Required]
          public string Name { get; set; }
          public decimal Price { get; set; }
          public string Manufacturer { get; set; }
      }
      
      As we are using dependency injection to create instances of our services, make sure not to forget to register the service in the Startup class:
      services.AddScoped<IShoppingCartService, ShoppingCartService>();

      Creating a Testing Project

      Finally, we come to the point when we need to create a new project where our tests are going to be. Conveniently for us, there is a xUnit testing project template out-of-the-box when using visual studio 2019, so we are going to make use of that. The xUnit is an open-source unit testing tool for the .NET framework that simplifies the testing process and allows us to spend more time focusing on writing our tests: xUnit project - unit testing with .NET Core We are going to name it web-api-tests. Now we have a new project in our solution named web-api-tests. Next thing we should do is to add the reference to the project we are about to write tests for: reference project - Unit testing with .NET Core At this time we should create our fake implementation of the IShoppingCartService interface, which we are going to inject to our controller at the time of testing. It has an in-memory collection which we are going to fill up with our dummy data:
      public class ShoppingCartServiceFake: IShoppingCartService
      {
          private readonly List<ShoppingItem> _shoppingCart;
      
          public ShoppingCartServiceFake()
          {
              _shoppingCart = new List<ShoppingItem>()
                  {
                      new ShoppingItem() { Id = new Guid("ab2bd817-98cd-4cf3-a80a-53ea0cd9c200"),
                          Name = "Orange Juice", Manufacturer="Orange Tree", Price = 5.00M },
                      new ShoppingItem() { Id = new Guid("815accac-fd5b-478a-a9d6-f171a2f6ae7f"),
                          Name = "Diary Milk", Manufacturer="Cow", Price = 4.00M },
                      new ShoppingItem() { Id = new Guid("33704c4a-5b87-464c-bfb6-51971b4d18ad"),
                          Name = "Frozen Pizza", Manufacturer="Uncle Mickey", Price = 12.00M }
                  };
          }
      
          public IEnumerable<ShoppingItem> GetAllItems()
          {
              return _shoppingCart;
          }
      
          public ShoppingItem Add(ShoppingItem newItem)
          {
              newItem.Id = Guid.NewGuid();
              _shoppingCart.Add(newItem);
              return newItem;
          }
      
          public ShoppingItem GetById(Guid id)
          {
              return _shoppingCart.Where(a => a.Id == id)
                  .FirstOrDefault();
          }
      
          public void Remove(Guid id)
          {
              var existing = _shoppingCart.First(a => a.Id == id);
              _shoppingCart.Remove(existing);
          }
      }
      [learn_more caption="Recommendation for the Moq" state="open"] Instead of creating fake service manually, we could’ve used one of the many mocking frameworks available. One of those frameworks is called Moq. You can get more information about it in the Testing MVC Controllers article, where we use the Moq library to isolate our dependences. [/learn_more]

      Let’s write some unit tests!

      Now we are all set and ready to write tests for our first unit of work – the Get method in our ShoppingCartController. We will decorate test methods with the [Fact] attribute, which is used by the xUnit framework, marking them as the actual testing methods. Besides the test methods, we can have any number of helper methods in the test class as well. When writing unit tests it is usually the practice to follow the AAA principle (Arrange, Act and Assert): Arrange – this is where you would typically prepare everything for the test, in other words, prepare the scene for testing (creating the objects and setting them up as necessary) Act – this is where the method we are testing is executed Assert – this is the final part of the test where we compare what we expect to happen with the actual result of the test method execution Test method names should be as descriptive as possible. In most of the cases, it is possible to name the method so that it is not even necessary to read the actual code to understand what is being tested. In the example we use the naming convention in which the first part represents the name of the method being tested, the second part tells us more about the testing scenario and the last part is the expected result. Generally, the logic inside our controllers should be minimal and not so focused on business logic or infrastructure (ec. data access). We want to test the controller logic and not the frameworks we are using. We need to test how the controller behaves based on the validity of the inputs and controller responses based on the result of the operation it performs. yoda - Unit testing with .NET Core

      Testing Our Actions

      The first method we are testing is the Get method and there we will want to verify the following:
      • Whether the method returns the OkObjectResult which represents 200 HTTP code response
      • Whether returned object contains our list of ShoppingItems and all of our items

      Testing the Get Method

      So let’s see how we go about testing our method:
      public class ShoppingCartControllerTest
      {
          ShoppingCartController _controller;
          IShoppingCartService _service;
      
          public ShoppingCartControllerTest()
          {
              _service = new ShoppingCartServiceFake();
              _controller = new ShoppingCartController(_service);
          }
      
          [Fact]
          public void Get_WhenCalled_ReturnsOkResult()
          {
              // Act
              var okResult = _controller.Get();
      
              // Assert
              Assert.IsType<OkObjectResult>(okResult.Result);
          }
      
          [Fact]
          public void Get_WhenCalled_ReturnsAllItems()
          {
              // Act
              var okResult = _controller.Get().Result as OkObjectResult;
      
              // Assert
              var items = Assert.IsType<List<ShoppingItem>>(okResult.Value);
              Assert.Equal(3, items.Count);
          }
      }
      We create an instance of the ShoppingCartController object in the test class and that is the class we want to test. It is important to note here that this constructor is called before each test method, meaning that we are always resetting the controller state and performing the test on the fresh object. This is important because the test methods should not be dependant on one another and we should get the same testing results, no matter how many times we run the tests and in which order we run them.

      Testing the GetById method

      Now let’s see how we can test the GetById method:
      [Fact]
      public void GetById_UnknownGuidPassed_ReturnsNotFoundResult()
      {
          // Act
          var notFoundResult = _controller.Get(Guid.NewGuid());
      
          // Assert
          Assert.IsType<NotFoundResult>(notFoundResult.Result);
      }
      
      [Fact]
      public void GetById_ExistingGuidPassed_ReturnsOkResult()
      {
          // Arrange
          var testGuid = new Guid("ab2bd817-98cd-4cf3-a80a-53ea0cd9c200");
      
          // Act
          var okResult = _controller.Get(testGuid);
      
          // Assert
          Assert.IsType<OkObjectResult>(okResult.Result);
      }
      
      [Fact]
      public void GetById_ExistingGuidPassed_ReturnsRightItem()
      {
          // Arrange
          var testGuid = new Guid("ab2bd817-98cd-4cf3-a80a-53ea0cd9c200");
      
          // Act
          var okResult = _controller.Get(testGuid).Result as OkObjectResult;
      
          // Assert
          Assert.IsType<ShoppingItem>(okResult.Value);
          Assert.Equal(testGuid, (okResult.Value as ShoppingItem).Id);
      }
      
      Firstly we verify that the controller will return 404 status code (Not Found) if someone asks for the non-existing ShoppingItem. Secondly, we test if 200 code is returned when the existing object is asked for and lastly we check if the right object is returned.

      Testing the Add Method

      Let’s see how we can deal with the Add method:
      [Fact]
      public void Add_InvalidObjectPassed_ReturnsBadRequest()
      {
          // Arrange
          var nameMissingItem = new ShoppingItem()
          {
              Manufacturer = "Guinness",
              Price = 12.00M
          };
          _controller.ModelState.AddModelError("Name", "Required");
      
          // Act
          var badResponse = _controller.Post(nameMissingItem);
      
          // Assert
          Assert.IsType<BadRequestObjectResult>(badResponse);
      }
      
      
      [Fact]
      public void Add_ValidObjectPassed_ReturnsCreatedResponse()
      {
          // Arrange
          ShoppingItem testItem = new ShoppingItem()
          {
              Name = "Guinness Original 6 Pack",
              Manufacturer = "Guinness",
              Price = 12.00M
          };
      
          // Act
          var createdResponse = _controller.Post(testItem);
      
          // Assert
          Assert.IsType<CreatedAtActionResult>(createdResponse);
      }
      
      
      [Fact]
      public void Add_ValidObjectPassed_ReturnedResponseHasCreatedItem()
      {
          // Arrange
          var testItem = new ShoppingItem()
          {
              Name = "Guinness Original 6 Pack",
              Manufacturer = "Guinness",
              Price = 12.00M
          };
      
          // Act
          var createdResponse = _controller.Post(testItem) as CreatedAtActionResult;
          var item = createdResponse.Value as ShoppingItem;
      
          // Assert
          Assert.IsType<ShoppingItem>(item);
          Assert.Equal("Guinness Original 6 Pack", item.Name);
      }
      
      Once again we are testing that the right objects are returned when someone calls the method, but there is something important to note here. Among other things, we are testing if ModelState is validated and the proper response is returned in the case that the model is not valid. But to achieve this, it is not enough to just pass the invalid object to the Add method. That wouldn't work anyway since model state validation is only triggered during runtime. It is up to integration tests to check if the model binding works properly. What we are going to do here instead is add the ModelError object explicitly to the ModelState and then assert on the response of the called method.

      Remove method

      Testing the remove method is pretty straightforward:
      [Fact]
      public void Remove_NotExistingGuidPassed_ReturnsNotFoundResponse()
      {
          // Arrange
          var notExistingGuid = Guid.NewGuid();
      
          // Act
          var badResponse = _controller.Remove(notExistingGuid);
      
          // Assert
          Assert.IsType<NotFoundResult>(badResponse);
      }
      
      [Fact]
      public void Remove_ExistingGuidPassed_ReturnsOkResult()
      {
          // Arrange
          var existingGuid = new Guid("ab2bd817-98cd-4cf3-a80a-53ea0cd9c200");
      
          // Act
          var okResponse = _controller.Remove(existingGuid);
      
          // Assert
          Assert.IsType<OkResult>(okResponse);
      }
      [Fact]
      public void Remove_ExistingGuidPassed_RemovesOneItem()
      {
          // Arrange
          var existingGuid = new Guid("ab2bd817-98cd-4cf3-a80a-53ea0cd9c200");
      
          // Act
          var okResponse = _controller.Remove(existingGuid);
      
          // Assert
          Assert.Equal(2, _service.GetAllItems().Count());
      }
      
      Remove method tests take care that valid response is returned and that object is indeed removed from the list.

      Summary

      This concludes the tests scenarios for our ShoppingCartController and we just want to summarize the general advice about unit testing. There are few guidelines or best practices you should strive for when writing unit tests. Respecting these practices will certainly make your (and life of your fellow developer) easier.

      Unit tests should be readable

      No one wants to spend time trying to figure out what is that your test does. Ideally, this should be clear just by looking at the test name.

      Unit tests should be maintainable

      We should try to write our tests in a way that minor changes to the code shouldn't make us change all of our tests. The DRY (don't repeat yourself) principle applies here, and we should treat our test code the same as the production code. This lowers the possibility that one day someone gets to the point where he/she needs to comment out all of our tests because it has become too difficult to maintain them.

      Unit sets should be fast

      If tests are taking too long to execute, it is probable that people will run them less often. That is certainly a bad thing and no one wishes to wait too long for tests to execute.

      Unit tests should not have any dependances

      It is important that anyone who is working on the project can execute tests without the need to provide access to some external system or database. Tests need to run in full isolation.

      Make tests trustworthy rather than just aiming for the code coverage

      Good tests should provide us with the confidence that we will be able to detect errors before they reach production. It is easy to write tests that don’t assert the right things just to make them pass and to increase code coverage. But there is no point in doing that. We should try to test the right things to be able to rely on them when time comes to make some changes to the code.

      Conclusion

      In this post, we’ve learned what unit testing is and how to set up the unit testing project with xUnit. We’ve also learned the basic scenarios of testing the controller logic on some CRUD operations. These examples should give you a great starting point for writing your own unit tests, and test the more complex projects. Thanks for reading and hopefully this article will help you grasp the unit concepts and unit testing in ASP.NET Core a little bit better. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      3847 0 0 0 251 https://www.alvinashcraft.com/2018/07/16/dew-drop-july-16-2018-2766/ 0 0 258 https://devlinduldulao.pro/how-to-create-a-simple-web-service-for-your-xamarin-application/ 0 0 260 https://chevenix.wordpress.com/2018/07/22/szumma-119-2018-29-het/ 0 0 439 0 0 444 439 0 465 0 0 470 465 0 477 470 0 788 0 0 961 956 0 914 788 0 956 0 0 990 ()); That took me a minute but I figured it out.]]> 0 0 1235 0 0 1905 0 0
      Creating Vue.js Client Side - Preparing Project and Project Structure Overview https://code-maze.com/creating-vuejs-project/ Mon, 13 Aug 2018 07:00:35 +0000 https://code-maze.com/?p=4018 .NET Core Web API), we have to create the frontend part to consume it. In this series, we are going to use Vue.js. It is a pretty new framework but it is already attracting a lot of attention. In comparison to other popular frameworks, Vue.js is much easier and straightforward to use, so if you are a beginner it should be easier to start with. We are going to build our frontend as a SPA (Single Page Application). That is the type of web application that never reloads because it's rewriting the content of a single HTML file resulting in smoother user experience and feeling like its native desktop app. [sc name="part_of_series" headline="This article is part of the series"] For the main page of this series, you can visit Vue.js Series. To download the source code for this part, visit Preparing Vue.js Project Source Code. Let’s start. This post is divided into several sections:

      Installation of Vue CLI and Starting a New Project

      First, we are going to install the Vue CLI (Command Line Interface) which will help us initialize a new Vue application. To install the Vue CLI we are going to type this command at the terminal: npm install -g vue-cli After the installation, we are going to create a new project by using the official Vue.js Webpack template. Webpack is a module bundler which will generate static assets from our modules and dependencies. To create our first Vue.js project, let’s navigate to the folder where we want to create a project and type in terminal: vue init webpack AccountOwnerClient After some time, the wizard is going to ask us a couple of questions.

      Project Details

      The first question is Project name and we are going to type account-owner-client because the node package name must not contain capital letters and best practice is to type a name in Kebab Case. The second question is Project description and we are going to type Account Owner Client for this one. The third question is Author and we are going to type our name and email in the format Name Surname <email@domain.tld>.

      Configuring Vue.js and Linter

      The fourth question is whether we want the Runtime + Compiler or just the Runtime. We want the Runtime + Compiler because we are going to write Vue.js components and for that we need Compiler. The fifth question is whether we want vue-router and we will type Y because we are going to have several pages to navigate through. The sixth question is whether we want ESLint. ESLint is a great package that takes care of our code style and forces us to write the code by strict rules like the code indentation, writing space before brackets on function definitions, writing a semicolon and a lot of other things. We would advise you to use ESLint, so we are going to answer with Y. The seventh question is which ESLint preset we want to use. ESLint preset is a set of rules and there are a lot of presets because people have different preferences and, for example, some people like writing semicolon at the end of the line while others don’t. Wizard is asking us if we want Standard preset, Airbnb preset or to write our own rules. You can learn more about the Standard rules here, and about the Airbnb rules here. Personally, we like writing a semicolon and the Airbnb rules force that while the Standard does not. But, we don’t like writing a comma on the last element of JSON and that’s what the Standard rules are forcing us. On the other side, the Airbnb does not, so, we are going to use our own ESLint rules. Of course, you can choose whatever suits your style and change that later in the .eslintrc.js file.

      Configuring Tests and misc.

      The eighth question is about unit tests and we are going to type n because we will not cover any type of tests in this series. The ninth question is about e2e tests and we are going to type n as well. The tenth and the last question is whether wizard should run npm install for us to install all dependencies we need. We are going to choose Yes, use NPM. If you’ve followed everything carefully, you are going to see this as a result:Vue Init Project - Creating Vue.js project Let’s wait for the npm to install everything for us and then open the project with any code editor and analyze the project structure. Our advice is to use Visual Studio Code because it has great extensions which can help us a lot with building Vue.js applications. Also, our advice is installing the Vue.js Extension Pack as well.

      Project Structure Overview

      After we open the project with a code editor, we are going to notice the following folder structure: Project Structure - Creating Vue.js project Explanation of the most important files and folders:
      • src - Our project source code:
        • assets - Module assets which will be processed with Webpack
        • components - This is where we keep our UI components
        • router - Here we write routes and connect them to our UI components
        • App.vue - This is an entry point component, it’s the main UI component in which all the other components will render
        • main.js - Entry point file which will mount App.vue - our main UI component
      • assets - pure assets which will not be processed with Webpack
      • index.html - You may remember that the SPA application always rewrites the content of one file, so, this is that file. This is the file that we are serving to our visitors. After building a project, this file will load static files which were bundled with Webpack
      Let’s take a look at the index.html file:
      <!DOCTYPE html>
      <html>
       <head>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width,initial-scale=1.0">
         <title>account-owner-client</title>
       </head>
       <body>
         <div id="app"></div>
         <!-- built files will be auto injected -->
       </body>
      </html>
      The only important thing in this file is a div tag with id app, which is going to be replaced with our App.vue component. On the next line, we can notice the comment that says:  “built files will be auto injected”. So, our js file, which is bundled with Webpack, is going to be injected below the div with id app. You may remember that main.js is our entry point, right? Let’s take a look at main.js file which is the first file  to be executed when the application starts:
      import Vue from 'vue';
      import App from './App';
      import router from './router';
      
      Vue.config.productionTip = false;
      
      new Vue({
        el: '#app',
        router,
        components: { App },
        template: '<App/>'
      });
      In this code example, we can notice that our main.js imports the Vue module. In the next lines, we can see two additional imports. One is our App component (App.vue) and another one is the router, and for all the import statements we don’t need to specify an extension. Furthermore, we instantiate a new Vue component which is going to mount our App.vue component inside a div with the id app attribute - you remember div with id app in index.html? Finally, we notify our Vue component to use the router.

      Component Structure

      Let’s take a look at the App.vue file:
      <template>
       <div id="app">
         <img src="./assets/logo.png">
         <router-view/>
       </div>
      </template>
      
      <script>
      export default {
       name: 'App'
      };
      </script>
      
      <style>
      #app {
       font-family: 'Avenir', Helvetica, Arial, sans-serif;
       -webkit-font-smoothing: antialiased;
       -moz-osx-font-smoothing: grayscale;
       text-align: center;
       color: #2c3e50;
       margin-top: 60px;
      }
      </style>
      Every Vue component may contain a template, a script, and a style section.
      • The template is a visible content of the component
      • The script is a logic of the component
      • Style, you guessed it, is a style for a template

      Template

      In a template, we can use native HTML tags as well as our custom Vue components. Here we can see the <router-view/> element and that’s the place where our router is going to render a matching component for that route. We are going to analyze the router file in a few moments.

      Script

      In the script tag, we write logic for that component. In this component, we only have the name property because this component isn’t dynamic, it is nothing other than the wrapper around our router-view. We are going to cover writing components in the next part of this series.

      Style

      Here we can write a style for a template. Style can be scoped or global. The scoped style is written this way: <style scoped>. That type of style only affects the template of that component and root element of child components so there is no way for style to leak to another component. Counterwise, a non-scoped style will be shared between components.

      Router

      Let’s take a look at the router/index.js file:
      import Vue from 'vue';
      import Router from 'vue-router';
      import HelloWorld from '@/components/HelloWorld';
      
      Vue.use(Router);
      
      export default new Router({
       routes: [
         {
           path: '/',
           name: 'HelloWorld',
           component: HelloWorld
         }
       ]
      });
      In this file, we import Vue again so we can notify Vue to use Router which is imported from a package named vue-router. Furthermore, we import the HelloWorld component from the components folder. The @ symbol in a path is a Webpack alias for the root of the project, so whenever we want to import something and to navigate to the root folder we can use @. Next, we notice instantiating the Router and list of routes. We may notice the path / which means when we open our application and navigate to the root (/) it is going to serve us the HelloWorld component. The content of that component is going to be served in the App.vue component inside the <router-view/> tag, remember? We are not going to analyze the HelloWorld.vue file because it’s pretty much the same as the App.vue file. It’s just another component with some content in a template and we are going to see how it looks like, but this time in a browser.

      Starting Our Application

      Now that we understand the code, it’s time to run the application and see everything on a live example. Let’s type this command in terminal: npm run dev This is going to run our application in a development mode. After running that command, let’s open our web browser and navigate to http://localhost:8080/. If everything is OK, we should be able to see the starting page of our application: Starting Our Application - Creating Vue.js project We may notice the Vue.js logo which is the part of the App.vue component. Under the logo, we see the content of the HelloWorld.vue file which is rendered inside the <router-view/>element of the App.vue component. The application is rendering the HelloWorld.vuecomponent because we are on the / route.

      Conclusion

      Excellent. You have learned how to create and run a new Vue.js project. But this is just the beginning of this great journey! We still have a lot of work to do and to try out the great Vue.js features! By reading this post you’ve learned:
      • The way of creating a new Vue.js application
      • What is SPA and why it’s a great way of writing applications
      • The overview od Vue.js components
      • The basics of Vue.js routing
      In the next part of the series, we are going to show how to install a third-party library, create navigation and also how to use routing. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4018 0 0 0 332 https://www.alvinashcraft.com/2018/08/20/dew-drop-august-20-2018-2785/ 0 0
      Creating Vue.js Client Side - Routing and Navigation https://code-maze.com/vuejs-routing-navigation/ Mon, 20 Aug 2018 07:00:31 +0000 https://code-maze.com/?p=4048 vue-router in the application and we are going to use it to create new routes and connect it to the components. We are going to use the Bootstrap library to styling our components. [sc name="part_of_series" headline="This article is part of the series"] For the main page of this series, you can visit Vue.js Series. To download the source code for this part, visit Routing and Navigation. Let’s start with the installation. This post is divided into several sections:

      Installing the Third-Party Library

      Let’s navigate a terminal to the root of our application, and type: npm i bootstrap-vue. After that, we are going to notify Vue.js to use Bootstrap and then the Vue.js file is going to register all the Bootstrap components for us. We are going to import the styles as well. To do that, let’s open the main.js file and modify it:
      import Vue from 'vue';
      import App from './App';
      import router from './router';
      import BootstrapVue from 'bootstrap-vue';
      import 'bootstrap/dist/css/bootstrap.css';
      import 'bootstrap-vue/dist/bootstrap-vue.css';
      
      Vue.config.productionTip = false;
      
      Vue.use(BootstrapVue);
      
      new Vue({
        el: '#app',
        router,
        components: { App },
        template: '<App/>'
      });
      Vue.use() is the function which is used to install vue plugins. It will trigger the install function inside a plugin. In this case, the Vue.use() function will trigger the install function inside the BootstrapVue plugin and that function will register all the Bootstrap components for us.

      Creating a Navbar Component

      Let’s navigate to src/components and create a new file named Navbar.vue. In our navbar, we are going to have 3 nav links:
      1. A link to the homepage
      2. A link to the owner actions
      3. And a link to the account actions
      Because Bootstrap is our styling library of choice, we are going to use its components as much as possible. So, let’s create the Navbar.vue file:
      <template>
        <b-navbar
          toggleable="md"
          type="dark"
          variant="info">
          <b-navbar-brand href="#">Account-Owner Home</b-navbar-brand>
          <b-navbar-nav>
            <b-nav-item href="#">Owner Actions</b-nav-item>
            <b-nav-item href="#">Owner Actions</b-nav-item>
          </b-navbar-nav>
        </b-navbar>
      </template>
      <script>
      export default {
        name: 'Navbar'
      };
      </script>
      Currently, our links are not pointing to any location, thus we use the # sign, and that's because we have not made our routes and components yet. Let’s make a route and a component for our home page and then edit this file again to use the actual route.

      Creating Routes and Components

      Let’s open the routes/index.js file:
      import Vue from 'vue';
      import Router from 'vue-router';
      import HelloWorld from '@/components/HelloWorld';
      
      Vue.use(Router);
      
      export default new Router({
        routes: [
          {
            path: '/',
            name: 'HelloWorld',
            component: HelloWorld
          }
        ]
      });
      This is the same HelloWorld component from the end of the previous part. We are going to delete it and create our first component - Home.vue and then make the route that is going to render that component. Let’s start with component first.

      Creating a New Component

      We are going to create the Home.vue file in the components directory and modify it:
      <template>
        <p class="homeText">
          WELCOME TO ACCOUNT-OWNER APPLICATION
        </p>
      </template>
      <script>
      export default {
        name: 'Home'
      };
      </script>
      <style scoped>
      .homeText{
          font-size: 35px;
          color: red;
          text-align: center;
          position: relative;
          top:30px;
          text-shadow: 2px 2px 2px gray;
      }
      </style>

      Creating a new Route

      Let’s open the routes/index.js file and modify it:
      import Vue from 'vue';
      import Router from 'vue-router';
      import Home from '@/components/Home';
      
      Vue.use(Router);
      
      export default new Router({
        routes: [
          {
            path: '/',
            name: 'Home',
            component: Home
          }
        ]
      });
      Now, we need to include our navbar somewhere in our project. Let’s open the App.vue file. In this file, we are going to remove the vue-cli generated template from the previous part and delete the Vue.js logo and style as well. Then, we are going to import our Navbar component and register it so we can use it as a tag in the template. We also want to use Bootstrap as much as possible so we are going to replace our main div tag with the b-container. So, let’s modify the App.vue file:
      <template>
        <b-container id="app">
          <b-row>
            <b-col>
              <navbar/>
            </b-col>
          </b-row>
          <b-row>
            <b-col>
              <router-view/>
            </b-col>
          </b-row>
        </b-container>
      </template>
      
      <script>
      import Navbar from '@/components/Navbar';
      
      export default {
        name: 'App',
        components: {
          Navbar
        }
      };
      </script>
      Let’s open the Navbar.vue file again and add the real route to the Account-Owner Home link:
      <b-navbar-brand :to="{ name: 'Home' }">Account-Owner Home</b-navbar-brand>
      You might have noticed a strange parameter :to and a JSON object next to it. This is how we pass some data to our components and we are going to learn more about that in the following parts of this series. Stay tuned! Let’s start the application and inspect the results:Welcome to account-owner application - Routing and Navigation in Vue.js Great! But what if someone goes to URL which doesn’t exist, like http://localhost:8080/#/whatever? We need to display NotFound component when this happens. Let’s implement that.

      Creating the NotFound Component

      We are going to create the catch all route which will be triggered when none of our routes are matched. Let’s first create the NotFound.vue component. In the src directory, we are going to make a new directory named error-pages. We are going to put our NotFound.vue component which only shows the message like page is not found in that directory. Here is how the ‘NotFound.vue’ file looks:
      <template>
        <p>
          404 SORRY COULDN'T FIND IT!!!
        </p>
      </template>
      <script>
      export default {
        name: 'NotFound'
      };
      </script>
      <style scoped>
      p {
          font-weight: bold;
          font-size: 50px;
          text-align: center;
          color: #f10b0b;
      }
      </style>
      Now, we are going to open the routes/index.js and add the catch all route:
      import Vue from 'vue';
      import Router from 'vue-router';
      import Home from '@/components/Home';
      import NotFound from '@/components/error-pages/NotFound';
      
      Vue.use(Router);
      
      export default new Router({
        routes: [
          {
            path: '/',
            name: 'Home',
            component: Home
          },
          {
            path: '*',
            name: 'NotFound',
            component: NotFound
          }
        ]
      });
      Let’s inspect the result by navigating to: http://localhost:8080/#/whatever 404 Not Found - Routing and Navigation in Vue.js The last thing we want to do is to change the routing mode from the hash mode to the history mode. It’s a lot prettier to remove # from the URL. History mode relies on the browser’s API history.pushState and it gives us the ability to navigate without # in the URL. Vue.js router will use hash mode as the fallback if the browser doesn’t support history.pushState. Let’s open the routes/index.js file and add another parameter to the Router constructor:
      ...
      export default new Router({
        mode: 'history',
        routes: [
      ...

      Conclusion

      As you can see, creating our navigation menu and linking routes with components has been a pretty easy and straightforward job. In the next parts, we are going to make more complex components and create routes for all the remaining navbar links. By reading this post you’ve learned:
      • How to create a navigation menu
      • How to create simple components
      • The way of creating routes and connecting it with the components
      • And how to change from hash mode to history mode
      In the next part of the series, we are going to show how to install, configure and use Axios HTTP client and how to create and use environment files.]]>
      4048 0 0 0
      Creating Vue.js Client Side - Axios HTTP Client and Environment Files https://code-maze.com/vuejs-axios-http-environment-files/ Mon, 27 Aug 2018 07:00:29 +0000 https://code-maze.com/?p=4078
    • Preparing the Project
    • Routing and Navigation
    • Axios HTTP Client and Environment Files (Current article)
    • Creating Components and Displaying Data from Backend
    • Components Props and Events
    • Details and Delete Owner Components
    • Create and Update Owner Components
    • [sc name="part_of_series" headline="This article is part of the series"] For the main page of this series, you can visit Vue.js Series. To download the source code for this part, visit Axios and Environment Files Source Code. This post is divided into several sections:

      Setting up Environments

      In the development mode, we need to point our HTTP requests to our local backend which is at http://localhost:5000/api. When we deploy an application to the production environment, we will point our API requests to the backend which is on the remote server and not our local backend, something like http://www.accountowner.com/api. To implement this behavior, we need to modify our environment files. In the config directory, we have our environment files. Let’s open the config/dev.env.js file and add a new environment variable to it:
      module.exports = merge(prodEnv, {
        NODE_ENV: '"development"',
        API_ENDPOINT: '"http://localhost:5000/api"'
      })
      Let’s do the same thing for the config/prod.env.js file:
      module.exports = {
        NODE_ENV: '"production"',
        API_ENDPOINT: '"http://www.accountowner.com/api"'
      }
      Now we need to install Axios and configure it to use these variables.

      Installing Axios

      Let’s open a terminal and in the root of our project we are going to type: npm i axios After the installation finishes, we are going to open the main.js file, import Axios and configure it to use the API_ENDPOINT environment variable as a base URL for all the Axios requests. So, let’s do exactly that:
      import 'bootstrap/dist/css/bootstrap.css';
      import 'bootstrap-vue/dist/bootstrap-vue.css';
      import Axios from 'axios';
      
      Vue.config.productionTip = false;
      
      Axios.defaults.baseURL = process.env.API_ENDPOINT;
      
      Vue.use(BootstrapVue);
      The process is a global Node.js variable through which we can access our environment variables. After we have finished with environment variables, it’s time to create our first API service through which we are going to gather some data from our backend. Vue.js doesn’t have any rules about code architecture, for example, where to put services or models. We can structure a Vue.js project in a way we want because it is not aware of terms like Service, Controller or Model. We can implement any of that inside a simple .js file which we create for that specific purpose.

      Creating API Service

      Let’s create a new directory in our src directory and name it api-services. Then inside of it, we are going to create a new file and name it owner.service.js. In this file, we are going to implement our Axios calls to the backend and it is recommended to create separate API service files for every entity in our database or RESTful resource. So, let’s import Axios and implement RESTful calls in our owner.service.js file:
      import Axios from 'axios';
      
      const RESOURCE_NAME = '/owner';
      
      export default {
        getAll() {
          return Axios.get(RESOURCE_NAME);
        },
      
        get(id) {
          return Axios.get(`${RESOURCE_NAME}/${id}`);
        },
      
        create(data) {
          return Axios.post(RESOURCE_NAME, data);
        },
      
        update(id, data) {
          return Axios.put(`${RESOURCE_NAME}/${id}`, data);
        },
      
        delete(id) {
          return Axios.delete(`${RESOURCE_NAME}/${id}`);
        }
      };
      Just for the testing purposes, let’s import our API service in the Home.vue component and check if it works.

      Fetching Data From the Backend

      To verify if everything is working as it supposes to, let’s fetch some data from the backend and log it in the console. This is just for the testing purposes but in the next part of this series, we are going to actually render the fetched data in respective components. Let’s open the src/Home.vue file, import our OwnerService in it and execute the getAll() function from it:
      <script>
      import OwnerService from '@/api-services/owner.service';
      
      export default {
        name: 'Home',
        created() {
          OwnerService.getAll().then((response) => {
            console.log(response.data);
          }).catch((error) => {
            console.log(error.response.data);
          });
        }
      };
      </script>
      The created() function is a part of the Vue.js component lifecycle. It is going to be executed once the component is instantiated and all the data is set but the component’s mounting stage is still not started. We are using that hook just to verify if our OwnerService is working. When we call any of the Axios functions in our API service, that function returns a Promise because the HTTP calls are asynchronous. So what we need to do is to return that promise from our API service and then to listen to the Promise resolve. That is the main reason why we have the .then() block after the API service call. Excellent. Now it is a time to see the result. Let’s start our application with npm run dev, and type http://localhost:8080 in the web browser. Then, we are going to open DevTools (F12), switch to Console and reload our page. We should see the following result: Welcome to Account-Owner Application - Axios HTTP Client and Environment files Great! We are going to delete implementation of the created() lifecycle hook because we are verified that our API service is working well.

      Conclusion

      Excellent, we are now ready to create new components and render this data which we get from the backend. By reading this post you’ve learned:
      • How to work with environment files in Vue.js
      • How to install and configure Axios client
      • The way of creating api-service
      • How to make HTTP calls
      In the next part, we are going to learn how to create custom components and display data fetched from the backend.]]>
      4078 0 0 0 809 Creating API Service step, the should the ${RESOURCE_NAME}/${id} be in side of quotation marks? e.g. update(id, data) { return Axios.put('${RESOURCE_NAME}/${id}', data); }, The current code cannot be compiled because it conflicts with jquery static syntax.]]> 0 0 810 809 0 811 810 0 1532 0 0 1534 1532 0 1536 1534 0
      Top Docker Monitoring Tools https://code-maze.com/top-docker-monitoring-tools/ Mon, 06 Aug 2018 07:05:39 +0000 https://code-maze.com/?p=4136 Introduction As more and more companies tend to adopt DevOps trend in their software development process, DevOps has slowly become a set of practices rather than a concept. It is about finding a smooth interaction between development and operations in order to overcome the frequent conflicts that we can face in every software delivery industry. One of the DevOps Ecosystem landing tools is Docker. This open-source emerging technology aims to easily pack, ship, and run any application as a lightweight, portable, self-sufficient container, which can run virtually anywhere. With the explosion of the Docker container market, almost all IT and cloud companies have adopted docker technology. [sc name="part_of_series" headline="This article is part of the series"] Researchers have proven that Enterprises are packing in 50% more containers per host in 2018 as compared to 2017, according to the 2018 Docker Usage Report by container monitoring company Sysdig. Docker is dominating the container runtime, used by 83% of all the containers in production, and in order to manage this increasing number, we have to face the challenge of keeping an eye close to understand what is really happening inside our container. Monitoring containers are one of the most challenging activities related to container-based applications. In front of this high scale of containers, we can see an increased need to understand what is really happening inside the containerized application. Monitoring comes as a primordial step towards optimizing and boosting the performance of containers running in different environments.

      What is monitoring

      The essence of Monitoring philosophy is to ensure that a running program or process is performing to the expected level. It is a systematic review process aiming to observe and check the quality and progress of such an application over a period of time. In fact, the Open Source community offers a ton of tools to monitor Docker and to ensure that these plenty of containerized applications are performing as expected. When talking about docker monitoring tools, we do focus on processes and their evolutions. It is about collecting and analyzing information and metrics to track the containerized application progress toward reaching its objectives. This Dashboard interface is going to be a guide to the management decision, to evaluate and to track applications ‘performance.

       noteNote: Monitoring VS orchestration

      In order not to confuse monitoring and orchestration as basic concepts related to the container's lifecycle we should clarify that Orchestration tools are defined to provide a specific layer added to the containers toolchain to ensure their deployment, availability scaling, and networking.

      Why do we need to monitor our docker containers?

      It is obvious that monitoring is an essential part of Docker-based environments. The main points that we are going to highlight are:
      • Detecting issues and troubles at an early stage to avoid risks at a production level
      • Changes and upgrades can be done safely as the whole environment is tracked
      • Refine applications for better performance and robustness
      What are the best tools to monitor our containers In this article, we have decided to round up the most useful docker monitoring tools. Below we have tried to define a rich list of these existing tools. The Open Source community offers a ton of tools to monitor our docker containers.

      1. Docker API (Docker Stats) 

      Let’s start with a simple monitoring tool provided by Docker itself, as an API already included in the Docker engine client which is docker stats command line. Docker stats provides an overview of some metrics we need to collect to ensure the basic monitoring function of Docker containers. This API gives us an access to CPU usage, memory, networking information and disk utilization for a running Docker container. It is about displaying a live stream of a container(s) resource usage statistics. To see statistics related to a running Docker container, we can simply run docker stats with the desired options, associated with the name of the container. If there is no specified name, docker stats per default shows all the existent containers and its statistics. To use docker stats we simply write on the command line: docker stats [OPTIONS] [CONTAINER...] The execution of this command will show us this output: container-performance-analysis-32-638 We can use many options with docker stats command line. Docker stats shows the percentage of CPU utilization for each container, the memory used and total memory available to the container. Added to that we can see total data sent and received over the network by the container. Actually, Docker stats is a quick data stream, that shows you a detailed overview of important parameters related to your Docker container automatically. It is really useful, as you can use it at any given moment. But we have to mention that the shown data, is not stored at any place, so this is about an instant capture related to the actual status of the container. For that reason, we cannot talk about tracking with docker stats as a monitoring tool, as we cannot review these metrics and understand what is really happening with our containerized application. To Sum Up with docker stats tool, it is an open-source API deployed in the docker demon itself, offering a detailed statics and metrics of containers automatically when running the command line which is useful in many cases, but we don’t talk about any level of aggregation with this tool, or any ability to raise alarms or track the performance of the running application inside the docker container. Verdict: already deployed with docker daemon, quick to use, basic metrics and statics, no capacity to send alerts Official website:  docker docs Availability: Open Source API

      2. cAdvisor 

      dgpIE5EcAdvisor is another monitoring tool for Docker containers, offered by Google and having native support for Docker containers. It consists of a single shipped container, that you can run and access via a graphical interface showing the attached statics of our dockized application. This container can collect, process, aggregate and export information related to the running containers. In order to get up and run cAdvisor, we have just to run the command below and expose the web interface on port 8080, which is the graphical overview of the docker stats-all command.
      docker run\
      --volume=/:/rootfs:ro\
      --volume=/var/run:/var/run:rw \
      --volume=/sys:/sys:ro \
      --volume=/var/lib/docker/:/var/lib/docker:ro \
      --publish=8080:8080 \  --detach=true \
      --name=cadvisor \
      google/cadvisor:latest
      Once launched, we can access the graphical interface, via typing this  http://localhost:8080/ in a browser. cAdvisor will connect itself directly to the Docker daemon running on our host machine so we can start visualizing the related metrics of the running container, including cAdvisor too as a single container. cAdvisor displays the graphs related to the CPU usage, Memory usage, Network input/output and disk space utilization. The figure below illustrates an overview of the dashboard displayed by cAdvisor. One of the most important tasks in the monitoring is the graphical part, cAdvisor is a very useful tool that it can be set up in an easy way to discover the resources consumption by the containerized applications. Another plus for cAdvisor is that we can integrate it easily in our environment, as it is an open-source tool supporting docker features, giving us a quick overview of our running application. This information is invaluable if we need to know when the additional resources are needed for our cluster for example. However, it has its limitations, with cAdvisor we talk about metrics related to one single container. If we have more than one docker host, it is going to be difficult for the cAdvisor to monitor these nodes. Also, the data visualized concerns o capture the running processors during one-minute so we cannot talk about a long-term monitoring concept. Sending alerts if one of our resources is at a dangerous level is critical, but not provided by cAdvisor so we do not have global visibility. We have to note, that there are other monitoring tools based on cAdvisor. For example, Rancher is integrating cAdvisor for every connected host and exposes the stats through its UI. Verdict:  graphical dashboard for metrics and statics, support docker API, no capacity to send alerts Official website:  cAdvisor Availability: Open Source tool

      3. Scout 

      logoAs an improvement to cAdvisor, Scout comes with a hosted monitoring service that has the capacity to aggregate metrics from many hosts and containers and it is able to present the data over longer time-scales. In addition to that Alerts can be created based on the result of these metrics. Scout is not an open-source tool, we have to be signed in the scoutapp.com site and a free trial version is up for 14 days, which will offer us the opportunity to test out its integration. Other points are handled by Scout tool which is the following:
      • Reporting metrics related to CPU Usage, Memory limit, and usage, Network usage, Number of containers running …
      • Sending Alerts based on capacity and performance issues
      • Identifying if background jobs take too much in the queue
      • Comparing performance between different deployments
      The picture below illustrates a global overview of the Docker Scout Dashboard. Based on this dashboard we can remark that comparing the performance of our application is easily handled, between different time periods we can analyze the performance of a specific metric selected via a drop-down list and view the graphical diagram. time_compare_annotated It is obvious that Scout is a powerful tool to monitor docker containers in a very attractive designed UI and ease of install and management. Although all these attractive points of Scout, we have to note also that one drawback of Scout is that we can get detailed information related to a specific container on each host like cAdvisor. This could be problematic in some cases, while monitoring we need to get in detail if there are any issues, or abnormal processing for a specified application especially if we are running multiple containers on the same server. Added to that, Scout has a price, which is almost 10 dollars per monitored host, and this could be a problem if we are running a large number of hosts. Verdict:  powerful tool, attractive design, ease of install and management, not an open-source tool Official website:  Scout Availability: 10 USD per Host

      4. Data Dog

       dd_logo_70x75-258ff008Another powerful tool to monitor docker containers is DataDog. It is full-stack cloud-based monitoring and alerting service designed for IT operations and development teams. It can monitor container environments easily thanks to its containerized environments. The Data Dog system can be integrated with many applications and tools, and docker is one of them. Datadog addresses many drawbacks of Scout and cAdvisor. It focuses on providing very detailed metrics of the whole running applications. All the monitored points are arranged in an elegantly designed dashboard that can be customized based on our needs and properties. Notifications also are triggered depending on the issues and severity. One of the powerpoint to highlight with Datadog is the capability to highlight any follow-up that occurred, so we can avoid it and get annotated about it in the future. When running the Datadog agent in our host, based on the command given by the Datadog website, the launched agent will start reporting metrics to the Datadog system. The full Access API of Datadog system has the ability to capture events and analyze them via graphical diagrams in the dashboard view, alerts and also collaboration tools as is illustrated in the figure below. screenboard-ci-dash-fullscreen1 This dashboard overview reflects the granular way of how Datadog functions. Datadog shows the personalized key metrics, the build history, the running events… Using Datadog we can specify how to create alerts, as this tool supports alerting based on a feature called Monitors. It is equivalent to Scout trigger and it allows you to define the composition of your alert events. All these features lead us to the conclusion that Datadog is a real powerful cloud tool, that has shown significant improvement to all the monitoring tools mentioned above, with a very user-friendly design, a rich set of supported tools and systems (docker and non-docker resources), and it’s easy to use. However, these advanced functionalities come with a higher cost equal to 15 dollars per agent. Verdict:  cloud solution, user-friendly design, a rich set of supported tools and systems, easy to use, alerts raising Official website:  DataDog Availability: 15 USD per Agent

      5. Prometheus

       58481585cef1014c0b5e4971Another important monitoring tool for Docker containers is Prometheus. It is an open-source toolkit used for monitoring and alerting. Prometheus can be used to monitor computer systems and alert sysadmins about the health of our environment. To understand how Prometheus work we have to look at it as a self-hosted set of tools that provide metrics storage, visualization, alerting and aggregation. We call these metrics providers: Exporters. There are also libraries that can be used to create custom exporter. When it comes to Docker, Prometheus needs a container exporter that will capture metrics and collect data, so that it can be analyzed later. Whenever our exporters are running, we can launch the Prometheus server based on a configuration file that tells the server from where the metrics can be scrapped. Once the metric endpoints are integrated into their time-series database, the rest of the Prometheus monitoring toolset can be used to observe metrics and raise alerts if something is underperforming. Related metrics and graphs are displayed in a UI dashboard based on the Grafana toolkit as it is illustrated in the figure below. grafana_prometheus-cbb943f0bb3 One of the important points to mention with Prometheus is the ability to raise alerts based on the applied alerting rules over the input data and to display the alerts on the UI. This alerting process will be more useful when sending an email or notifying a pager duty to manage business-impacting incidents. All of this can be done via running the Alert manager, and via creating related configuration files at the first stage. It is Obvious that Prometheus uses simplified format for input data and can be integrated with any web endpoint to scrape the data from. there are also several libraries defined to monitor common resources. What Prometheus lacks the most is the ease of deployment. We have to manage the configuration files for every container so they can support the Prometheus server. In addition to that, the fact that all components are dockized is a major plus, however, we had to launch 4 different containers, each with their own configuration files to support the Prometheus server. The project is also lacking detailed, comprehensive documentation of these various components. So, if we are comparing this monitoring tool to the other ones, we will conclude that Prometheus is a powerful tool, with a competitive UI and high analytical capabilities that necessitates an additional effort for deploying it and configuring its files. Verdict:  Competitive UI, high analytical capabilities, capacity to raise alerts, high level of aggregation, non-ease of deployment, support non-docker resources. Official website:  Prometheus Availability: Free

      6. Sysdig

      sysdig Sysdig as a Docker monitoring tool has per objective to get proper visibility of what is happening inside our containers. It is based on transparent instrumentation to aggregate metrics from Docker containers in real-time in order to provide meaningful dashboards and alerts related to the running applications. It is a hosted service, that works differently compared to the other tools we have been talking about. The idea is using an agent related to Sysdig, that sits at the operating system level instead of installing an agent on every docker container. This agent understands the processes that are running in the system and the services running in a container. We are talking about container-native monitoring, which is displayed in Sysdig dashboards as arrays coverings CPU usage, memory consumption, network usage, File I.O, and other Performance metrics. We should be logged into the Sysdig portal, to get a trial version. When running the Docker containers in our hosts, they will appear in the console of Sysdig too. We can select any host, or a container so that we can see its proper metrics and dashboards. Another option offered by Sysdig is live metrics. We can control our containers in real-time thanks to real-time mode. We can define comparisons between 2 different moments, to evaluate the application's performance as well. The Sysdig supports very detailed alerting criteria, and presents a very useful UI to manage these alerts, and define its rules. The figure below illustrates an overview of the Sysdig dashboard. Screenshot_-_Container_Dashboard_(1) All things considered, we have to mention that Sysdig is a very user-friendly tool to monitor docker containers, as it offers an ease of deployment. We can set up the Sysdig agent easily. Moreover, it shows very detailed dashboards with a real-time tracking option. The alert system is very well managed by Sysdig. We have to note that this toolset is not for a free price, we have to pay 20 dollars per hots to get the benefices of this large list of options. Reviewing the price, we will notice that Sysdig is a serious competitor for the Data Dog tool. Verdict:  Cloud solution, easy to set up, detailed metrics, great visualization tools for real-time and historical data, medium ease of deployment, support non-docker resources. Official website:  Sysdig Availability: 20 USD per Host

      7. Sensu monitoring framework

       sensu_logoSensu is more than a simple monitoring tool It is a comprehensive tool that is powerful enough to provide monitoring solutions for complex environments. But also simple enough to be used with traditional ones. With Scout and Datadog we talk about centralized monitoring and alerting, however with Sensu it is about a self-hosted and centralized metric s service. To run Sensu with Docker containers, there is an image at the Docker Hub containing the basic API of the Sensu server so we can run it as a single container on our host. And in order to support docker container metrics and status checks, we have to use a plugin system. Based on configurations and scripts, we can refine our evaluation criteria with Sensu. We have the ability to collect details about our docker containers, aggregate the value on our single storage place, and raise some alerts based on these checks. Comparing this framework with other monitoring tools, we will find ourselves in front of a tough job to be done for the deployment of this framework. In order to Deploy Sensu, we need to automate so many steps in order to launch complement processes that Sensu works with such as Redis, RabitMQ, Sensu API, Uchiwa and Sensu Core. Furthermore, we still need to use a graphical tool that integrates with Sensu to display these metrics such us: Graphite. From the perspective of Alerts, Sensu is also offering a basic alerting system, that refers to checks failing on individual hosts. And if we want to create more checks, we need to restart the Sensu server so that new metrics can be tracked and that we can collect them. For all these reasons, Sensu comes in fairly low competition with the other monitoring tools, despite being free cost tool, and its ability to monitor non-docker resources. Verdict:  Framework, high level of aggregation, ability to raise alerts, limited Ability to monitor non-docker resources Official website:  Sensu Availability: Free

      8. Sematext

       logo_sematextLet’s move on to another monitoring tool which is Sematext. It is a Docker-native monitoring and logging solution that collects and processes infrastructure and application performance. It tracks docker metrics, events, and logs for all auto-discovered container and cluster nodes. This powerful tool has its own log management solution and provides very high-performance monitoring and even faster troubleshooting. The following image illustrates a global overview, of the Sematext dashboard. React.js-Redux-Pretty-Charts-Better-Dashboards Sematext has a single agent that captures logs, metrics, and events for all containers and their hosts automatically. It runs as a single container on every Docker host, in order to provide processing and automatic collection of Docker metrics. Added to that it provides full-stack visibility so the business user can extract value and decide based on performance. Sematext supports a lot of tools and cloud platforms such as docker swarm, docker cloud, docker datacenter, Amazon EC2, Kubernetes, Mesos, RancherOs, Google container … Obviously, we cannot deny that Sematext brings real-time performance monitoring capacity, log management, events, and alerts… Being a powerful tool that provides visibility of our dynamic infrastructure comes with a cost. Verdict:  real-time performance, log management solution, performance monitoring service, support a lot of tools, Official website:  Sematext Availability: depends on data transfer rates per day (example the standard package is for 50 USD per month)

      clipart-tango-face-glasses-512x512-af8dLet’s Summarize

      Regarding all this variety of tools, we can see that every single feature in each tool, Before choosing a specific tool, we have to take in consideration the time aspect: time to handle these tools, to deploy them, to maintain and to stay up-to-date with their new features, and capabilities.

      Conclusion

      There are plenty of tools to monitor our docker containers. Making a refined choice from this variety of tools is always up to the needs and metrics we need to supervise and to the combination of the existing DevOps toolchain used in our infrastructure. The objective is to keep an open eye to what is going inside these containers in order to optimize our applications and systems. Monitoring philosophy itself is very sensitive, and when it comes to docker environments it becomes even more challenging, simply because every container runs its own environments, processes, variables, has its virtual network, and storage configuration. In this article we have tried to cover the most popular tools on the market, mixing open source solutions with paid tools and platforms. We can definitely make a comparison based on some metrics, UI, cost, and performance between these tools, but choosing the best monitoring solution depends on the combination of tools that we have, our challenges and our needs. The objective is to identify which metrics we need to monitor, how to define the rules and levels of our alerts, visualize dependencies, and ensure high availability without sacrificing performance. Docker Ecosystem is increasingly growing throughout the software development industry, so the companies need to be aware of how much being able to monitor our systems is crucial for boosting the performance of their applications. [sc name="subscribe" formNumber="3820" contentType="CI/CD"]]]>
      4136 0 0 0 312 https://www.alvinashcraft.com/2018/08/06/dew-drop-august-6-2018-2781/ 0 0 333 https://chevenix.wordpress.com/2018/08/20/szumma-122-2018-32-het/ 0 0 590 https://www.baeldung.com/java-weekly-241 0 0 659 0 0
      C# Basics - Development Environment Setup https://code-maze.com/csharp-basics-ide-introduction/ Thu, 26 Jul 2018 07:28:22 +0000 https://code-maze.com/?p=4142
    • Development Environment Setup (Current article)
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. In this article, we are going to cover the following topics:

      Integrated Development Environment (IDE)

      IDE stands for Integrated Development Environment. It is basically a tool that helps us develop applications more easily. It has many features that can make our lives easier. For example, some common features include:
      • Source code editor
      • Debugger
      • Compiler
      • Templates for different kinds of projects
      • Much more
      Our IDE of choice will be Visual Studio 2017 Community Edition. To download it, visit Visual Studio Download Page. Visual Studio has support for multiple programming languages, which makes it a very popular development tool. After the installation, we can start a new project by clicking the File menu and choosing New => Project: New Project Page - Visual Studio For this tutorial, we will use the console application project the most, so let's choose that option: Creating Console Project in Visual Studio After we click on the OK button, we are going to see our created project. The main file to work with is the Program.cs and soon enough we are going to talk more about it: Program file in Visual Studio

      Watch Window

      In some examples, we are going to use this window to examine results. To open it we need to place a breakpoint on any code line first (by clicking left mouse button): Brakepoint in Visual Studio Then start our application by pressing F5, and finally to open the watch window: Watch window in Visual Studio

      Conclusion

      Now we have the perfect IDE that will help us and the knowledge to create a new project. Soon enough we are going to use this knowledge in our applications. In the next part, we will learn about data types, declarations and definitions. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4142 0 0 0 269 https://www.alvinashcraft.com/2018/07/26/dew-drop-july-26-2018-2774/ 0 0
      C# Back to Basics - C# Data Types, Declarations and Variable Definitions https://code-maze.com/csharp-basics-data-types-variables/ Fri, 27 Jul 2018 07:04:26 +0000 https://code-maze.com/?p=4150
    • How to register data in memory
    • The possible values for that data
    • Possible actions on the data
    • So, let's talk more about data types in C#. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. In this article, we are going to cover:

      Data Type Registration

      Data types that represent the whole numbers are expressed with a certain number of bits. For unsigned numbers, the range is from 0 to 2N-1, and signed numbers range is from -2N-1 to 2N-1-1. So if the data type has a size of 8 bits like the sbyte data type, we can represent its range like this: from -27 to 27-1 => from -128 to 127. The following table contains different data types that represent the whole numbers: whole numbers table - C# Data Types Letter u in front of the type means that type can’t contain negative numbers, it is unsigned. The types mentioned above are the whole number types. But in C#, we have the number types with the floating point. We can present them in a table as well: Decimal numbers table in C# Data Types In C#, we have two more basic data types: Char-bool table C# Data Types To use char type in our code we must place it inside the single quotes: ’a’ or ’A’ or ’3’... One more type that is often introduced as the basic data type is the string type. But the string is not a value type it is a reference type. To use a string in our code we must place the value inside the double quotes: „This is the string type“ or „3452“... So, we know we have the value types and reference types, and it is time to talk more about them and variables as well.

      Variables in C#

      Variable is a name of a memory location in which application stores values. We should create our variables by following examples:
      • studentName
      • subject
      • work_day ...
      The wrong examples would be
      • student Name
      • work-day
      • 1place
      We should mention that C# is a case-sensitive language so the studentName is not the same as the StudentName. The C# language has its own set of reserved words, so-called keywords. We can't use them as a name for our variables. For the list of keywords, you can visit keyword-list. In C#, we have variables divided into two categories: Value type and Reference type. The difference is that the value type variables stores their values inside their own memory locations, but the memory location for the reference type variables contains only address to the dynamic memory location where the value is stored. Let’s see how the value types behave in a graphic example: Value type C# Data Type Let’s do the same for the reference types: Reference type C# Data Types

      Variable Declarations and Expressions

      We should declare our variables in the following way: <data type> <variable name> ;  or <data type> <variable name>, <variable name> ... ; So a few examples would be:
      class Program
      {
          static void Main(string[] args)
          {
              int age;
              double temperature, change;
              Student student;
          }
      }
      
      Just with the declaration, we can't assign a value to a value type variable. To do that, we need to use expressions in addition: <data type> <variable name> = <expression> ; Again, let's look at this with an example:
      class Program
      {
          static void Main(string[] args)
          {
              int x = 5;
              int y = 145 + x;
              char p = 'p';
              p = 'A';
          }
      }
      
      To add value for the reference type variable, we need to use the newkeyword in the expression part (string is an exception to this rule):
      class Program
      {
          static void Main(string[] args)
          {
              Student student = new Student("John", 25);
          }
      }
      
      We would like to mention that we don’t recommend to call variables with names „x“ or „y“... We have used those names just for the sake of simplicity. It is a better idea to give meaningful names to our variables.

      Conclusion

      Now we have learned how to declare our variables and how to assign values to them as well. In a next post, we are going to talk about operators in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4150 0 0 0 C# Basics – Development Environment Setup]]> 273 https://www.alvinashcraft.com/2018/07/27/dew-drop-july-27-2018-2775/ 0 0
      C# Basics - Operators in C# https://code-maze.com/csharp-basics-operators/ Wed, 01 Aug 2018 06:52:30 +0000 https://code-maze.com/?p=4192
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C# (Current article)
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. In this article, we are going to cover:

      Type of Operators

      The most used operators in C# are: Operators in C# Table

      Arithmetic Operators

      Arithmetic operators are defined for all the numeric data types. Operators +, -, *, / represent the basic binary arithmetic operations (addition, subtraction, multiplication and division). Operator % is the remainder after division. The important thing to notice is that the + operator behaves differently with the number and string types. With the numbers, the result of expression 5 + 5 is 10. But with the strings, the result of expression „5“ + „5“ is „55“. So with the number type, it is an addition operator but with the string type, it is a concatenation operator.

      Relational Operators

      All the relational operators return a true or false result. They are used to compare expressions or variables from both sides of the relational operator. These operators have a lower priority than arithmetic ones. So in the following example: x*a-8*b>y+5*z; the left side of the greater than operator has calculated first then the right side and then they are compared. For the value-type variables and the strings, the == (equality) operator will return true only if they are the same, otherwise, it will return false. But if variables are reference types then the == operator will return true only if those two variables point to the same memory location, otherwise, it will return false. So let's see this through an example: Equality Operators in C# We see that a and b are the equal as well as the s1 and s2. But the student1 and student2 are not equal because they point to different memory locations. But if we create another variable of type Student and assign the value of student1 variable to it, the == operator will return true: Equality ref Operators in C#

      Logical Operators

      The logical operators && (and) and || (or) serve to connect logical values. Expression <expression1>&&<expression2> is true only if both expressions are true. Expression <expression1>||<expression2> is false only if both expressions are false, otherwise, it is true. The ! (negation) operator negates the logical value it is applied on. It has the highest priority of all the operators mentioned. So the expression !logicalValue will be false only if logicalValue is true and vice verse. Let’s see this with an example:
      class Program
      {
          static void Main(string[] args)
          {
              int a = 14;
              int b = 30;
              int c = 20;
      
              if(a < b && a < c)
              {
                  Console.WriteLine($"min number is {a}");
              }
      
              if(a < b || a < c)
              {
                  Console.WriteLine("The a variable is smaller then b or c");
              }
      
              if(!(a > b))
              {
                  Console.WriteLine("a is less than b");
              }
          }
      }
      

      Increment and Decrement Operators

      In the C# language, we can use operators that increments and decrements the variable value by 1. Those operators are ++ and --, and they are very useful in many cases. So, the better way of writing this code:
      static void Main(string[] args)
      {
           int a = 15;
           a = a + 1; //now it is 16
      }
      
      Would be to write it like this:
      static void Main(string[] args)
      {
          int a = 15;
          a++; //now it is 16
      }
      
      The same applies to the -- operator. These two operators have the prefix notations: --variable, ++variable and the suffix notations: variable--, variable++ . Even though both notations will change the value by 1, the result will be different. This is easier to explain through an example: Decrement Operatorc in C# What we can notice is that the prefix notation decrements the value of "a" variable first and then assigns that value to the "b" variable. But the expression with suffix notation is different. The value of the "c" variable is assigned to the "d" variable first and then decremented by 1. The same applies for the increment operator: Increment Operators in C#

      Conclusion

      Excellent. Now we have more knowledge about operators in C#. In the next part, we are going to talk about the type-conversions in C#. In the next post, we are going to talk about type conversions in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4192 0 0 0
      C# Back to Basics - Type Conversion https://code-maze.com/csharp-basics-type-conversion/ Fri, 03 Aug 2018 06:51:35 +0000 https://code-maze.com/?p=4219
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion (Current article)
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. So, let's talk more about that. In this article, we are going to cover:

      Implicit Conversion

      Many different data could be interpreted by using different types. For example, number 74 can be interpreted as an integer but also as double (74.0). There are two situations in which implicit conversion applies. The first one is when we calculate an expression. The compiler automatically adapts data types that we use in that expression:
      class Program
      {
          static void Main(string[] args)
          {
              double b = 12.45;
              int x = 10;
              b = b + x;
          }
      }
      
      In the code above the b variable is of type double and x is of type int. In the expression b + x, the compiler implicitly converts x from int to double and then it assigns a result to the b. The second situation for a conversion is when the compiler stores the result to a variable: Implicit Conversion C# Type Conversions In this example, we see that both x and y are of the type int, but the result is of the type double. Now, let's pay attention to this example:
      int x = 21;
      int y = 5
      
      double b = x/y;
      What do you think, which value is stored in the variable b? The answer: "The result is 4.2" is not correct. Of course, now goes the question: "But why"? The compiler calculates the right side expression first and only then converts that result into double. So, the right side expression x/yconsist of integer numbers, thus the result is an integer number as well, in this example, it is 4 (the value is truncated). After that calculation, compiler converts result into a double and assigns the value to the b variable: implicit conversion 2 We can fix this if we want, by using the explicit conversion on either x or y variable in the expression.

      Explicit Conversion

      For the explicit conversion, we need to write additional code to convert one type to another. We have two different ways, by using a cast operator or by using the Convert class. Let's look at the following example: Missing Cast C# Type Conversions The compiler complains about an invalid conversion. What we are missing here is the cast operator, so let's use it: Success cast C# Type Conversions By using the cast (int)conversion, we can safely cast our data types and the compiler approves of that. But what we can see is that our result is not what we would expect it to be. But this is the correct result. It is very important to understand that the cast operator can shrink data when we convert the type with the larger value scope to a type with the smaller value scope. Like we did with the double to int conversion for example. Now we can apply the cast operator on our example from the Implicit Conversion part, to get the right result:
      class Program
      {
          static void Main(string[] args)
          {
              int x = 21;
              int y = 5;
      
              double b = (double)x / y;
          }
      }
      The result is going to be 4.2.

      Using the Convert Class

      As we said, we can use the Convert class with its static methods, to explicitly convert one base type to another base type: ToString C# Type Conversions

      Conclusion

      Excellent. We have learned about type conversion and how implicit and explicit conversion behaves in C#. In a next post, we are going to talk about Linear Structures in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4219 0 0 0 302 https://www.alvinashcraft.com/2018/08/03/dew-drop-august-3-2018-2780/ 0 0 307 0 0 308 307 0 309 http://blog.cwa.me.uk/2018/08/06/the-morning-brew-2637/ 0 0 330 https://rvhuang.github.io 0 0 331 330 0
      C# Back to Basics - Input and Output in C# https://code-maze.com/csharp-basics-input-output/ Wed, 08 Aug 2018 07:00:19 +0000 https://code-maze.com/?p=4226
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C# (Current article)
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. If you want to download the source code for our examples, you can do that from here Input and Output Source Code. So let’s start. Example 1: Create an application that prints out the sum of two integer values which user inputs in the console window. Let’s create a new console application and name it SumGenerator. Then let’s type this code inside the Main method:
      namespace SumGenerator
      {
          class Program
          {
              static void Main(string[] args)
              {
                  Console.WriteLine("Write the first integer:");
                  int first = Convert.ToInt32(Console.ReadLine());
      
                  Console.WriteLine("Write the second integer:");
                  int second = Convert.ToInt32(Console.ReadLine());
      
                  int result = first + second;
                  Console.WriteLine($"The result is {result}");
      
                  Console.ReadKey();
              }
          }
      }
      
      With the Console.WriteLine() statement, we display the message on the console window and move to the next line. The Console.ReadLine() statement will read our input, but it is of type string and what we need is an int type. So, we need to convert it with the Convert.ToInt32() statement. Finally, we calculate the sum and print it out. The Console.ReadKey() statement is here just to keep our console window open. Let’s press F5 to start our application and enter two integer numbers: Sum generator Input and Output in C# Example 2: Write an application that for two provided inputs (name and last name), prints out the full name in a format: name <space> last name. Let’s create a new console application and to write this code:
      namespace FullNameGenerator
      {
          class Program
          {
              static void Main(string[] args)
              {
                  Console.WriteLine("What is your first name:");
                  string name = Console.ReadLine();
      
                  Console.WriteLine("What is your last name:");
                  string lastName = Console.ReadLine();
      
                  string fullName = name + " " + lastName;
                  Console.WriteLine($"Your full name is: {fullName}");
      
                  Console.ReadKey();
              }
          }
      }
      
      That's it. If we run our project by hitting F5, we will see the result with the name and last name, space-separated.

      Conclusion

      Excellent. Now we know how to manipulate inputs in our applications and how to display the result in the console window. Furthermore, we have used the data conversion from string to int, to be able to sum the inputs from a user. In a next post, we ware going to show you how to work with a strings in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4226 0 0 0 317 https://www.alvinashcraft.com/2018/08/08/dew-drop-august-8-2018-2783/ 0 0 797 https://www.brandwebdirect.com/website-design-portfolio-samples/web-design-portfolio-sample 0 0 1026 https://brandwebdirect.com/website-design-portfolio-samples/website-design-for-ecommerce-online-sales-web-development-service 0 0
      C# Back to Basics - Working With Strings https://code-maze.com/csharp-basics-string-methods/ Fri, 10 Aug 2018 07:00:22 +0000 https://code-maze.com/?p=4241 Length property. All the character positions inside that string are enumerated from 0 to Length-1. C# provides us with many different methods to work with strings and we are going to examine ones that are used most of the time. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. If you want to download the source code for our examples, you can do that from here Work With Strings Source Code. So, let's start. We will learn about the following topics:

      Substring, IndexOf, LastIndexOf

      Substring(int startIndex) is the method which returns part of the string from startIndex to the end of the string. Substring(int startIndex, int length) is the method which returns part of the string with defined length from the startIndex. Let’s see this in practice:
      class Program
      {
          static void Main(string[] args)
          {
              string testString = "this is some string to use it for our example.";
      
              string partWithoutLength = testString.Substring(10);
              string partWithLength = testString.Substring(5, 10);
      
              Console.WriteLine(partWithoutLength);
              Console.WriteLine(partWithLength);
      
              Console.ReadKey();
          }
      }
      
      IndexOf() is the method that returns the integer value of the position of the first appearance of the character or a string in the selected string. If that value doesn't exist, the method will return -1. There are different overloads of this method: IndexOf(char value), IndexOf(string value), IndexOf(char value, int startIndex), IndexOf(string value, int startIndex) etc. If we use this method with the startIndex parameter, we will not search from the beginning of the string but from that position to the end:
      int charPosition = testString.IndexOf('i');
      int stringPosition = testString.IndexOf("some");
      int charPosWithStartIndex = testString.IndexOf('s', 10);
      int stringPosWithStartIndex = testString.IndexOf("some", 10);
      
      LastIndexOf() is the method that returns the position of the last appearance of character or string value. This method has the same overloads as the IndexOf method:
      int lastPosition = testString.LastIndexOf('o');
      int stringLastPosition = testString.LastIndexOf("is");
      

      Contains, StartsWith, EndsWith

      Contains(string value) is the method that returns true if a string contains the value, otherwise, it will return false:
      bool containsResult = testString.Contains("for");
      StartsWith(string value) is the method which returns true if a string starts with the value, otherwise, returns false. As opposed to this method the EndsWith(string value) method returns true if a string ends with the value, otherwise, returns false:
      bool startsWithResult = testString.StartsWith("bad");
      bool endsWithResult = testString.EndsWith("example");
      

      Remove, Insert

      Remove(int startIndex) method removes characters from the string from the startIndex position to the end of the string and returns that new string. There is an overloaded method Remove(int startIndex, int count) which removes a specified number of characters from the string from the starting index position. With the count parameter we decide how many characters we want to delete:
      string loweredString = testString.Remove(10);
      string loweredStringWithCount = testString.Remove(10, 9);
      
      Insert(int startIndex, string value) is the method that inserts the value into the string from the startIndex position and returns a modified string:
      string stringWithInsert = testString.Insert(13, "UPDATED ");

      ToLower, ToUpper

      ToLower() returns a new string with all the lower case letters:
      string lowerCaseString = testString.ToLower();
      ToUpper() returns a new string with all the upper case letters:
      string upperCaseString = testString.ToUpper();

      Examples

      Example 1: Create an application which accepts the name and last name, space separated, as input, and then prints out the name in one row and last name in another row:
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter your full name, blank space separated");
              string fullName = Console.ReadLine();
      
              int blankPosition = fullName.IndexOf(' ');
              string name = fullName.Substring(0, blankPosition);
              string lastName = fullName.Substring(blankPosition + 1);
      
              Console.WriteLine(name);
              Console.WriteLine(lastName);
      
              Console.ReadKey();
          }
      }
      
      The result: string example 1 String Methods in C# Example 2: Create an application that accepts as input a sentence and removes the first and last word of that sentence:
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter your sentence: ");
      
              string sentence = Console.ReadLine();
      
              int firstBlankPosition = sentence.IndexOf(' ');
                  
              string withoutFirstWord = sentence.Remove(0, firstBlankPosition + 1);
      
              int lastBlankPosition = withoutFirstWord.LastIndexOf(' ');
      
              string withoutFirstAndLast = withoutFirstWord.Remove(lastBlankPosition);
      
              Console.WriteLine(withoutFirstAndLast);
      
              Console.ReadKey();
          }
      }
      
      Example 2 String Methods in C#

      Conclusion

      That is awesome. We have mastered the usage of the most used string methods in C#. With the combination of these methods, we can create powerful solutions for our applications. Stay with us in a next post, where we are talking about Conditions in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4241 0 0 0
      C# Back to Basics - Conditions in C# https://code-maze.com/csharp-basics-conditions/ Wed, 15 Aug 2018 07:00:55 +0000 https://code-maze.com/?p=4244
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case) (Current article)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. If you want to download the source code for our examples, you can do that from here Conditions in C# Source Code. Let's begin.

      Basic Condition Statements

      If we want to execute some expression but only if some condition is met, then we need to use conditional statements. To create such a statement we need to use if and else keywords. We can create a conditional statement like this:
      if (condition)
      {
          < expression1 > ;
      }
      else
      {
          < expression2 > ;
      }
      
      The condition is a logical expression which can result in true or false. If it is true then the <expression1> will be executed, otherwise, <expression2> will be executed. After every expression, we need to place the ; sign. We can execute more expressions if the condition is true or false:
      if (condition)
      {
          < expression1 > ;
          < expression2 > ;
      }
      else
      {
          < expression3 > ;
          < expression4 > ;
      }
      
      Example 1: Create an application which determines the greater number of two integer inputs:
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter the first number: ");
              int first = Convert.ToInt32(Console.ReadLine());
      	
              Console.WriteLine("Enter the second number: ");
              int second = Convert.ToInt32(Console.ReadLine());
      
              if(first > second)
              {
                  Console.WriteLine($"The greater number is {first}");
              }
              else
              {
                  Console.WriteLine($"The greater number is {second}");
              }
      
             Console.ReadKey();
          }
      } 
      
      Example 1 Conditions in C# We don’t have to use only if and else keywords in conditional statements, we can add another condition by adding else if block part:
      if(condition1)
      {
          < expression 1 > ;
      }
      else if(condition 2)
      {
          < expression 2 > ;
      }
      else if(condition n)
      {
          <expression n>
      }
      else
      {
          < expression k > ;
      }
      
      Example 2: Create an application which takes any string and the font color (r for red, g for green, o for other) as inputs. Then it needs to print out that string with the selected color:
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter your random string: ");
              string sentence = Console.ReadLine();
      
              Console.WriteLine("Choose your color: r for Red, g for Green, o for Other");
              char color = Convert.ToChar(Console.ReadLine());
      
              if(color == 'r')
              {
                  Console.ForegroundColor = ConsoleColor.Red;
                  Console.WriteLine(sentence);
              }
              else if(color == 'g')
              {
                  Console.ForegroundColor = ConsoleColor.Green;
                  Console.WriteLine(sentence);
              }
              else
              {
                  Console.ForegroundColor = ConsoleColor.Blue;
                  Console.WriteLine(sentence);
              }
      
              Console.ReadKey();
          }
      }
      
      Else if example Conditions in C#

      Nested Conditional Statements

      In C#, we can write a conditional statement inside a conditional statement if that’s one of the requirements of our project. So, the base syntax looks like this:
      if (condition)
      {
          if (condition2)
          {
              < expression1 > ;
          }
          else
          {
              < expression2 > ;
          }
      }
      else
      {
          < expression3 > ;
      }
      
      Even though we can create nested conditional statements, we do not recommend them that much, because it would lead to low readability. Example 3: Create an application in which the user enters a number between 1 and 100. If the number is lower then 50, our application will output multiplication by 5. But if a number is greater then 50 then for even number application will output multiplication by 2 and for an odd number application will output multiplication by 3:
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter your number: ");
              int number = Convert.ToInt32(Console.ReadLine());
      
              if (number > 50)
              {
                  if(number % 2 == 0) //reminder in division with two for even numbers is always a zero.
                  {
                      Console.WriteLine(number * 2);
                  }
                  else
                  {
                      Console.WriteLine(number * 3);
                  }
              }
              else
              {
                  Console.WriteLine(number * 5);
              }
      
               Console.ReadKey();
          }
      }
      
      Nested Conditions in C#

      Switch-Case Statements

      In a situation where we need more than one or two conditions to execute some expression, using multiple branching could be an advantage. To use multiple branching in C#, we need to use switch and case keywords:
      switch (expression)
      {
          case value1:
             <expression 1> ;
             break;
          case value2:
             <expression 2> ;
             break;
          default:
             < expression3>;
             break;
      }
      
      Example 4: Create an application which accepts month number as an input and prints out the number of days in that month:
      static void Main(string[] args)
      {
          Console.WriteLine("Enter the month number from 1 to 12");
          int month = Convert.ToInt32(Console.ReadLine());
      
          switch (month)
          {
              case 1: case 3: case 5:
              case 7: case 8:
              case 10: case 12:
                 Console.WriteLine("Number of days is 31");
                  break;
              case 4: case 6:
              case 9: case 11:
                  Console.WriteLine("Number of days is 30");
                  break;
              case 2:
                  Console.WriteLine("Number of days is 28 or 29");
                  break;
              default:
                  Console.WriteLine("Your number is not between 1 and 12");
                  break;
          }
      
          Console.ReadKey();
      }
      
      Multiple Conditions in C#

      Conclusion

      Well done. We this knowledge, we can create conditional structures in our code and make decisions based on whether the conditions are true or false. In our next post of this seres, we are going to talk about Loops in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4244 0 0 0
      C# Back to Basics - Loops in C# https://code-maze.com/csharp-basics-loops/ Fri, 17 Aug 2018 07:00:07 +0000 https://code-maze.com/?p=4258
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For) (Current article)
    • Handling Exceptions
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. If you want to download the source code for our examples, you can do that from here Loops in C# Source Code. Let's dig in. In this article, we are going to talk about:

      While Loop

      While loop is a loop with a precondition. This means that we are checking a condition first and then if a condition returns true, we execute our expression:
      while(condition)
      {
         < expression > ;
      }
      
      Example 1: Create an application which calculates the sum of all the numbers from n to m (inputs from a user):
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter the integer n number:");
              int n = Convert.ToInt32(Console.ReadLine());
      
              Console.WriteLine("Enter the integer m number");
              int m = Convert.ToInt32(Console.ReadLine());
      
              int sum = 0;
      
              while(n <= m)
              {
                  sum += n;
                  n++;
              }
      
              Console.WriteLine($"Sum from n to me is {sum}");
              Console.ReadKey();
          }
      }
      
      While example Loops in C# So let's explain the code above. Because we calculate the sum of all numbers from "n" to "m", we need to have a variable to store that value. It needs to be initialized with a zero at a beginning. Without that, our app will fail to build due to the sum variable being unassigned. In a while loop, we are going through all the numbers from n to m and adding every number to the sum variable. We are using this expression: sum += n; which is a shorter for sum = sum + n; Finally, we need to increment the n variable by 1. Without that, we would have an infinite loop because the value of the n variable would always be lesser than the value of the „m“ variable. We should use while loops when the number of iterations is uncertain. This means that we could repeat iteration until some condition is fulfilled, but we are not sure how many iterations we would need to reach the condition fulfillment.

      For Loop

      For loop is another loop with a precondition. We use the following syntax to write it in C#:
      for (initialization; condition; progression;)
      {
          <loop body > ;
      }
      
      We use initialization at the beginning of the loop and it serves the purpose of initializing the variable with a value. The condition is used to determine when the loop is completed. Progression is a part in which we increment or decrement our variable initialized in the initialization part. The body consists of all the expressions we need to execute as long as the condition is true. It is important to know that the order of execution is: Initialization, Condition, Loop Body, Progression. Example 1: Create an application which calculates the sum of all the numbers from n to m (inputs from a user):
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter the integer n number:");
              int n = Convert.ToInt32(Console.ReadLine());
      
              Console.WriteLine("Enter the integer m number");
              int m = Convert.ToInt32(Console.ReadLine());
      
              int sum = 0;
      
              for(int i = n; i <= m; i++)
              {
                  sum += i;
              }
      
              Console.WriteLine($"Sum from n to m is {sum}");
      
              Console.ReadKey();
      
          }
      }
      
      For example Loops in C# Example 2:  Create an application that prints out all the integer numbers from n to 1:
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter number n that is greater than 1: ");
              int n = Convert.ToInt32(Console.ReadLine());
      
              for (int i = n; i >= 1; i--)
              {
                  Console.WriteLine(i);
              }
      
              Console.ReadKey();
          }
      }
      
      For example2 Loops in C# We should use for loops when we know how many iterations we are going to have. This means if we iterate through all the elements inside a collection or we have an ending point for iterations.

      Do-While Loop

      The do-while loop is a loop with postcondition. What this means is that the loop body is executed first and the condition is checked after. That's totally opposite from the previous loop examples. We can implement this loop in the following way:
      do
      {
          < expression > ;
      } while (condition);
      
      Example 1: Create an application which calculates the sum of all the numbers from n to m (inputs from a user):
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter the integer n number:");
              int n = Convert.ToInt32(Console.ReadLine());
      
              Console.WriteLine("Enter the integer m number");
              int m = Convert.ToInt32(Console.ReadLine());
      
              int sum = 0;
      
              do
              {
                  sum += n;
                  n++;
              } while (n <= m);
      
              Console.WriteLine($"The sum from n to m is {sum}");
              Console.ReadKey();
          }
      }
      
      Do-while example 1 Loops in C# Example 2: Create an application which prints out the sum of all the even numbers to n:
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter the upper border number n: ");
              int n = Convert.ToInt32(Console.ReadLine());
      
              int sum = 2;
              int startingNumber = 4;
      
              do
              {
                  sum += startingNumber;
                  startingNumber += 2;
              }while (startingNumber <= n);
      
              Console.WriteLine($"Sum of all the even number to n is {sum}");
              Console.ReadKey();
          }
      }
      
      Do-while example 2

      Conclusion

      Good job. Now we can implement iterations in a combination with all that we have learned from the previous articles, thus making our application more powerful. In our next post, we are going to talk about how to handle exceptions in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4258 0 0 0 548 0 0
      C# Back to Basics - Handling Exceptions in C# https://code-maze.com/csharp-basics-handling-exceptions/ Wed, 22 Aug 2018 07:00:01 +0000 https://code-maze.com/?p=4282
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions (Current article)
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. If you want to download the source code for our examples, you can do that from here Handling Exceptions in C# Source Code. This post is divided into several sections:

      What is Exception

      The exception is a problem that appears unexpectedly in our code while we develop our project. That’s why we call these exceptions unhandled exceptions. If they are not handled, they will cause our application to stop working and will throw one of the exception messages. That is not something we want in our code.

      Try-Catch Block

      C# provides us with a built-in support to handle these exceptions by using a try-catch block of code:
      try
      {
          // expressions that could cause an exception
      }
      catch(Exception ex)
      {
          // handle exception
      }
      
      In the try block, we write our code and the catch block will handle all the exceptions that could arise in the try block. This way our program won’t stop at all and we can show some meaningful message to a user. Let’s see how our program works without and with exception handling. Example 1: Create an application which prints out the square root of the integer number entered by the user:
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("Enter your number: ");
              int num = Convert.ToInt32(Console.ReadLine());
      
              Console.WriteLine(Math.Sqrt(num));
          }
      }
      
      This code is going to work just fine if a user enters an integer number, but look at what is going to happen if a user enters a string: Unhandled exception - Handling Exceptions in C# We see that our application has stopped working. This is very bad for the user experience. So, let’s implement the same code but with the try-catch block:
      class Program
      {
          static void Main(string[] args)
          {
              try
              {
                  Console.WriteLine("Enter your number: ");
                  int num = Convert.ToInt32(Console.ReadLine());
      
                  Console.WriteLine(Math.Sqrt(num));
                  Console.ReadKey();
              }
              catch (Exception ex)
              {
                  Console.WriteLine($"There is something wrong in our application, please look at this message: {ex.Message}");
                  Console.ReadKey();
              }
          }
      }
      
      If we check out the result now: Handled exception - Handling Exceptions in C# As we can see, our app has not stopped and we have a nice readable message for our user, which provides a much better user experience than the previous example.

      Specific Exceptions

      C# has its own set of specific exceptions which we can use in our application. Some of them are: NullReferenceException, ArgumentOutOfRangeException, InvalidCastException, FileNotFoundException, DevideByZeroException, FormatException, InvalidOperationException etc. We can use them in this way:
      public class Program
      {
          public static void Main()
          {
              try
              {
                  //Code in here that could cause an exception
              }
              catch (DivideByZeroException ex)
              {
                  Console.Write("Cannot divide by zero. Please try again.");
              }
              catch (InvalidOperationException ex)
              {
                  Console.Write("Not a valid number. Please try again.");
              }
              catch (FormatException ex)
              {
                  Console.Write("Not a valid number. Please try again.");
              }
              catch(Exception ex)
              {
                  Console.Write("Any exception that previous catch blocks didn’t handle.");
              }
      
              Console.ReadKey();
      
          }
      }
      
      It is very important to place the specific catch blocks before the global catch block, otherwise, our compiler will complain: Invalid Exception - Handling Exceptions in C#

      Conclusion

      Excellent. Now we know how to write a safe code and how to handle errors in our app. In a next post, we are going to talk about Access Modifiers in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4282 0 0 0 336 https://www.alvinashcraft.com/2018/08/22/dew-drop-august-22-2018-2787/ 0 0 504 https://danielt.co.uk/blog/2018/11/18/learning-week-eight/ 0 0
      C# Back to Basics - Access Modifiers in C# https://code-maze.com/csharp-basics-access-modifiers/ Fri, 24 Aug 2018 07:00:30 +0000 https://code-maze.com/?p=4289
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers (Current article)
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. Access modifiers specify the accessibility of an object and all of its members in the C# project. Moreover, all the C# types have access modifiers implemented, even if they are not stated (default access modifier is applied then). Even though this topic is more related to the object-oriented concept, we will talk about it now, thus making easier to understand the next article about methods, that strongly relies on access modifiers.

      Access Modifiers Types

      C# provides four types of access modifiers: private, public, protected, internal, and two combinations: protected-internal and private-protected.

      Private Access Modifier

      Objects that implement private access modifier are accessible only inside a class or a structure. As a result, we can't access them outside the class they are created:
      class NumberClass
      {
          private int number = 10;
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              NumberClass num = new NumberClass();
              Console.WriteLine(num.number); // Error. We can't access the number variable because 
              // it has the private access modifier and its only accessible in the NumberClass class
          }
      }

      Public Access Modifier

      Objects that implement public access modifiers are accessible from everywhere in our project. Therefore, there are no accessibility restrictions:
      class NumberClass
      {
          public int number = 10;
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              NumberClass num = new NumberClass();
              Console.WriteLine(num.number); // This is OK. The number variable has the public access modifier.
          }
      }

      Protected Access Modifier

      The protected keyword implies that the object is accessible inside the class and in all classes that derive from that class. We will talk in more detail about inheritance in module 2 about object-oriented programming. But for now, we are going to take a look at this example to understand the behavior of the protected members:
      class NumberClass
      {
          protected int number = 10; //we can access this variable inside this class
      }
      
      class DerivedClass: NumberClass //this is inheritance. DerivedClass derives from the NumberClass class
      {
          void Print()
          {
              Console.WriteLine(number); //we can access it in this class as well because it derives from the NumberClass class
          }
      }
      
      class Program
      {
          void Print()
          {
              NumberClass num = new NumberClass();
              Console.WriteLine(num.number); // Error. The number variable is inaccessible due to its protection level. 
                                     // The Program class doesn't derive from the NumberClass
          }
      }

      Internal Access Modifier

      The internal keyword specifies that the object is accessible only inside its own assembly but not in other assemblies:
      //First Project (ASSEMBLY)
      public class NumberClassInFirstProject
      {
          internal int number = 10; //we can access this variable inside this class
      }
      
      class ProgramInFirstProject
      {
          void Print()
          {
              NumberClassInFirstProject num = new NumberClassInFirstProject();
              Console.WriteLine(num.number); // This is OK. Anywhere in this project (assembly) 
                                                 // we can access the number variable.
          }
      }
      
      //Second project (ASSEMBLY)
      class Program
      {
          void Print()
          {
              NumberClassInFirstProject num = new NumberClassInFirstProject();
              Console.WriteLine(num.number); // Error. The number variable is inaccessible due to its protection level. 
                                     //The Program class in second project can't access the internal members from another project
          }
      }

      Protected Internal Access Modifier

      The protected internal access modifier is a combination of protected and internal. As a result, we can access the protected internal member only in the same assembly or in a derived class in other assemblies (projects):
      //First Project (ASSEMBLY)
      public class NumberClassInFirstProject
      {
          protected internal int number = 10; //we can access this variable inside this class
      }
      
      class ProgramInFirstProject
      {
          void Print()
          {
              NumberClassInFirstProject num = new NumberClassInFirstProject();
              Console.WriteLine(num.number); // This is OK. Anywhere in this project (assembly) we can access the number variable.
          }
      }
      
      //Second project (ASSEMBLY)
      class Program: NumberClassInFirstProject //Inheritance
      {
          void Print()
          {
              Console.WriteLine(number); //This is OK as well. The class Program derives from the NumberClassInFirstProject clas.
          }
      }

      Private Protected Access Modifier

      The private protected access modifier is a combination of the private and protected keywords. We can access members inside the containing class or in a class that derives from a containing class, but only in the same assembly(project). Therefore, if we try to access it from another assembly, we will get an error.

      Conclusion

      So, that's it about access modifiers. As a result, we have learned what types of access modifiers we can use in C# and what are the limitations of them. Our next topic is going to be about Methods in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4289 0 0 0 340 https://www.alvinashcraft.com/2018/08/24/dew-drop-august-24-2018-2789/ 0 0 518 0 0 519 518 0
      C# Back to Basics - Methods in C# https://code-maze.com/csharp-basics-methods/ Wed, 29 Aug 2018 07:00:05 +0000 https://code-maze.com/?p=4313
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers
    • Methods (Current article)
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. If you want to download the source code for our examples, you can do that from here Methods in C# Source Code. In this article, we are going to talk about: In our previous article, we have talked about access modifiers (which are an important part of every method), so we strongly suggest to read it if you haven't already. A method is a code block we can use to extract part of our code to reuse it, thus making our classes more readable and easier to maintain. We can execute all the code inside a method once we call that method by using its name and specifying the required arguments.

      Method Signatures

      We can declare our methods by specifying the method signature that consists of the access modifier (public, private...), the name of a method, and method parameters. If we want our method to have an implementation, it needs to have two curly braces to specify the body of the method. We place our code between those curly brackets. Additionally, we have to include a return value (void, int, double...) for our method to be valid. The return type doesn't apply as a part of the method signature, but we can't create a method without it. Thus it injects itself into a signature (or specification). A method that returns a value needs to satisfy two conditions. First, it needs to specify a return type before the method name. The second, it needs to have a return statement within its body (inside curly braces). On the other hand, if the method doesn’t return anything, the void keyword is used instead of the return keyword. If that's the case, a method doesn’t need to have a return statement inside its body: Method signatures - Methods in C# In our project, we can have two different methods with the same name, but we can’t have two different methods with the same method signature. At least one part of the method signature needs to be different. When we have two or more methods with the same name but different signature, that’s called Method Overloading.

      Parameters and Arguments

      In the previous example, we have seen that our methods accept only one parameter. But, we can create a method that accepts as many parameters as we need:
      public void WriteAllNumbers(int a, int b, int c)
      {
           Console.WriteLine($"{a} {b} {c}");
      }
      
      It is important that every parameter has its own type, name and that they are comma-separated. When we create a method in the signature, we create parameters (imagine them as the placeholders for the value of the same type). But, when we call that method we are passing real values (arguments) for those parameters:
      WriteAllNumbers(15, 16, 67);
      Example 1: Create an application which prints out the sum, subtraction, and multiplication of two inputs:
      class Program
      {
          public static void Sum(int first, int second) //method needs to be static because we are calling it in a static Main method.
          {
              int result = first + second;
              Console.WriteLine($"Sum result: {result}");
          }
      
          public static void Subtract(int first, int second)
          {
              int result = first - second;
              Console.WriteLine($"Substraction result: {result}");
          }
      
          public static void Multiplication(int first, int second)
          {
              int result = first * second;
              Console.WriteLine($"Multiplication result: {result}");
          }
      
          static void Main(string[] args)
          {
              Console.WriteLine("Enter the first number: ");
              int firstArgument = Convert.ToInt32(Console.ReadLine());
      
              Console.WriteLine("Enter the second number: ");
              int secondArgument = Convert.ToInt32(Console.ReadLine());
      
              Sum(firstArgument, secondArgument);
              Subtract(firstArgument, secondArgument);
              Multiplication(firstArgument, secondArgument);
      
              Console.ReadKey();
          }
      }
      
      Methods in C# Example 1

      Optional Parameters

      An optional parameter has a default value. The method that has optional parameters could be called without those arguments. But we can provide them as well. If we provide the values as arguments for optional parameters then the default values will be overridden:
      public static void MethodWithOptParams(int first, int second = 10)
      {
          Console.WriteLine(first + second);
      }
      
      MethodWithOptParams(20); //result is 30
      MethodWithOptParams(20, 35); //result is 55
      

      Conclusion

      Using methods is very useful, not only in C# but in programming overall. So having this knowledge is quite an advantage. Don't be afraid to use them while coding, they will make your code cleaner, maintainable, readable, and above all reusable. In our next post, you will learn more about Ref and Out keywords in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4313 0 0 0 351 https://www.alvinashcraft.com/2018/08/29/dew-drop-august-29-2018-2792/ 0 0 1668 0 0 1669 1668 0
      Enabling CORS in ASP.NET Core https://code-maze.com/enabling-cors-in-asp-net-core/ Mon, 08 Oct 2018 07:00:40 +0000 https://code-maze.com/?p=3565
    • .NET Core Tutorial
    • Global Error Handling in .NET Core Web API
    • Create PDF in .NET Core Web API
    • Implementing Action Filters in .NET Core Web API
    • Design Patterns in C#
    • [sc name="part_of_series" headline="Recommended Articles"] You can find the source code for this blog post on GitHub. This post is divided into the following sections: Before we understand how to enable CORS in ASP.NET Core applications, we need to understand an important concept in the web application security model called the Same-Origin policy.

      About Same-Origin and What it Means

      Two URLs are considered to have the same origin if they have the same URL scheme (HTTP or HTTPS), same hostname (domain name) and same port number (the endpoint at which applications talk to each other).

      What Is the Same-Origin policy?

      The Same-origin policy states that a Web browser will only allow communication between two URLs if they belong to the same origin. That is, the client app (https://example.com) cannot communicate with the server app (https://example.net) as they belong to a different origin. This policy exists to isolate potentially malicious scripts from harming other documents on the web. Let’s come to the core of the article.

      What Is CORS?

      But there are some cases where Cross-Domain scripting is desired and the idea of the open web makes it compelling. Cross-Origin Resource Sharing is a mechanism to bypass the Same-Origin policy of a Web browser. Specifically, a server app uses additional HTTP headers to tell a browser that it is fine to load documents (from its origin) in a few selected client apps (of different origin). Let’s go deep.

      How Does CORS Work?

      In this section, let's learn how CORS works at the level of HTTP messages.

      Inner Workings

      Let’s have a look at the picture below. There is a client app (https://example.com) sending a GET request to a server app (https://example.net) for some resource: Enabling CORS in ASP.NET Core All modern browsers set the Origin header automatically, which indicates the domain of the site is making the request. If the server allows Cross-origin requests from the Origin (https://example.com), it sets the Access-Control-Allow-Origin header with its value matching the origin header’s value from the request. In other way, if the server doesn’t include this header, the request fails. The browser shall receive the response data, but this data shall not be accessible to the client. That’s how a simple CORS request works.

      Preflight Requests

      Sometimes, instead of a simple GET request, a client may need to send requests like PUT, DELETE etc. For such requests, the browser sends an additional request (an OPTIONS request) called a Preflight request. This is done just before the actual request to make sure that the original request succeeds. If it does, the browser sends the actual request. This diagram illustrates such a scenario:

      Preflight RequestA preflight request

      Actual RequestActual request

      Demo

      Enough of the theory. Let’s get to the action.

      Same-Origin Policy in Action

      In the demo, let’s create two projects. One is a server app which we are going to deploy to Azure. And the other the client app, which we are going to run locally. Additional read: You can see the CORS in action with .NET Core and Angular projects by reading the .NET Core tutorial. In Visual Studio, let’s create an MVC project. This will be our server app: New MVC Project - Enable CORS in ASP.NET Core If you are new to Azure deployment, then check out this article. You can also get a free Azure account here. Once the project is created, let’s deploy it to Azure Web App: Azure web app deploy After deployment, the server app is hosted at testapp1158.azurewebsites.net: Live page - Enable CORS in ASP.NET Core Next, let’s create a Client app. Again, we are going to create another ASP.NET Core MVC project, which runs locally. We have both client and the server app running and they belong to different origins. To make cross-origin requests, let’s go to Solution Explorer, navigate to Views -> Home -> Index.html. Let’s go to the bottom of the document and add the following piece of code:
      <div>
          <input type="button" value="Try" onclick="CORSRequest()" />
          <span id='data'>(Result)</span>
      </div>
      This markup creates a button and, when the user clicks the button, it calls a function. Just below the markup, let’s create a script tag with the code that makes an ajax request to the server app when the user clicks the button:
      <script>
          
          var serviceUrl = 'http://testapp1158.azurewebsites.net';
      
          function CORSRequest() {
              var method = $('#method').val();
      
              $.ajax({
                  type: method,
                  url: serviceUrl
              }).done(function (data) {
                  $('#data').text(data);
              }).error(function (jqXHR, textStatus, errorThrown) {
                  $('#data').text(jqXHR.responseText || textStatus);
              });
          }
      
      </script>
      Since we haven’t enabled CORS on the server app, the server app doesn’t set any headers and the browser rejects the response. So, if we navigate to the client app running locally and click the Try button, we should see an error: error Okay! Let’s get to the best part.

      Enabling CORS

      Now that we have seen the Same-Origin policy in action, let’s enable CORS. There are 3 steps to enable CORS in a server app:
      • First of all, we need the Microsoft.AspNetCore.Corspackage in our project. It should be already installed in our project via the Microsoft.AspNetCore.App package, which is created as soon as our project was created. But if for some reason, you can't see that package in the Solution Explorer, go to Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution. Search for Microsoft.AspNetCore.Cors and install the package
      • Next, we need to inject CORS into the container so that it can be used by the application. In Startup.cs class, let’s go to the ConfigureServices method and register CORS:
        public void ConfigureServices(IServiceCollection services)
        {
              services.AddCors();
              services.AddMvc();
        }
      • To enable CORS for our application, let’s add the CORS middleware to the HTTP request pipeline in the Configure method, just below the if-else statement. Let’s specify an URL from where the CORS requests are allowed when building the CORS policy. Here, we have given the Client URL:
        app.UseCors(builder =>
            builder.WithOrigins("http://localhost:55294"));
      Let’s save our Server app and re-publish it to the App Service. Now let’s open the client app and click the Try button. We should see an HTML document retrieved from the server app: data from server app - Enable CORS in ASP.NET Core Congratulations! We have successfully enabled CORS. Okay, but that’s not all. Let us show you a few more tricks.

      Configuring CORS Requests

      If we only want to allow CORS requests to a selected few methods, instead of enabling CORS at the entire application level, we can also enable CORS at the controller level or at the action level. So to enable CORS at the Action level, let’s replace the code we added previously in the ConfigureServices method with:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddCors(options =>
          {
              options.AddPolicy("AllowOrigin",
                  builder => builder.WithOrigins("http://localhost:55294"));
          });
      
          services.AddMvc();
      }
      This method defines a named CORS policy called,AllowOrigin that allows cross-origin requests from our client app. And now, instead of specifying CORS middleware in the HTTP request pipeline in theConfiguremethod , we are going to select this policy at runtime at the Action level. So, in our server app, let’s go to Controllers -> HomeController.cs and add the EnableCors decorator to the Index method:
      [EnableCors("AllowOrigin")]
      public IActionResult Index()
      {
          return View();
      }
      Let’s save the project and deploy the server app to Azure again. In the client app, in the Index.cshtml file, let’s update the serviceURL in the script section:
      var serviceUrl = 'http://testapp1158.azurewebsites.net/Home/Index';
      Now, if we run the project without debugging, we should still see the output: data from server app - Enable CORS in ASP.NET Core But, if we update the serviceUrl in client app to access any other method in Home Controller in the server app, we should see an error output. The updated serviceURL:
      var serviceUrl = 'http://testapp1158.azurewebsites.net/Home/About';
      shall give the expected output as: error Similarly, we can do this per controller as well. It’s not over. More power to you.

      Configure CORS Policy Options

      In ASP.NET Core, we can use various options to customize our CORS policy. For example, to only allow GET methods on our resource, let’s use the WithMethods method when we define the CORS policy:
      services.AddCors(options =>
      {
          options.AddPolicy("AllowOrigin",
                  builder => builder.WithOrigins("http://localhost:55294")
                                    .WithMethods("GET"));
      });
      If we need to allow any origin to access the resource, let’s use AllowAnyOrigin instead of WithOrigins:
      services.AddCors(options =>
      {
          options.AddPolicy("AllowOrigin",
              builder => builder.AllowAnyOrigin());
      });
      And ASP.NET Core provides several tools to customize what kind of requests we would like to allow. In Addition, you can see how CORS is important while working with SignalR in .NET Core by reading .NET Core with SignalR Real-Time Charts.

      Conclusion

      In conclusion, think of CORS as a relaxation attempt to the more restrictive Same-Origin policy. On the one side, there is a growing need for security on the web. And on the other, there is a need to integrate web services. CORS provides rich tools for our need for an open and secure web and ASP.NET Core allows us to take advantage of CORS in our cross-platform web applications. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      3565 0 0 0 424 https://www.alvinashcraft.com/2018/10/08/dew-drop-october-8-2018-2818/ 0 0 426 http://blog.cwa.me.uk/2018/10/09/the-morning-brew-2681/ 0 0 427 0 0 428 427 0 429 0 0 430 429 0 431 https://howtocode.net/2018/10/how-to-enable-cors-in-asp-net-core-by-example-code-maze-blog/ 0 0 432 https://howtocode.net/2018/10/how-to-enable-cors-in-asp-net-core-by-example/ 0 0 442 https://bricegomis.wordpress.com/2018/10/16/enabling-cors-in-asp-net-core/ 0 0 529 http://denizzzd.byethost4.com/pro/2018/09/24/interesting-articles-sep-dec-2018/ 0 0
      Implementing Action Filters in ASP.NET Core https://code-maze.com/action-filters-aspnetcore/ Mon, 01 Oct 2018 07:00:39 +0000 https://code-maze.com/?p=3738 MVC action invocation pipeline. Therefore, we can use filters to extract code which can be reused and make our actions cleaner and maintainable. There are some filters that are already provided by ASP.NET Core like the authorization filter, and there are the custom ones that we can create ourselves. There are different filter types:
      • Authorization filters – They run first to determine whether a user is authorized for the current request
      • Resource filters – They run right after the authorization filters and are very useful for caching and performance
      • Action filters – They run right before and after the action method execution
      • Exception filters – They are used to handle exceptions before the response body is populated
      • Result filters – They run before and after the execution of the action methods result.
      In this article, we are going to talk about Action filters and how to use them to create a cleaner and reusable code in our Web API. [sc name="part_of_series" headline="Recommended Articles"] To download the source code for our starting project, you can check out the start-project-branch. For the finished project refer to end-project-branch We have divided this article into the following sections:

      Action Filters Implementation

      To create an Acton filter, we need to create a class that inherits either from the IActionFilter interface or IAsyncActionFilter interface or from the ActionFilterAttribute class which is the implementation of the IActionFilter, IAsyncActionFilter, and a few different interfaces as well:
      public abstract class ActionFilterAttribute : Attribute, IActionFilter, IFilterMetadata, IAsyncActionFilter, IResultFilter, IAsyncResultFilter, IOrderedFilter
      In our examples, we are going to inherit from the IActionFIlter interface because it has all the method definitions we require. To implement the synchronous Action filter that runs before and after action method execution, we need to implement OnActionExecuting and OnActionExecuted methods:
      namespace ActionFilters.Filters
      {
          public class ActionFilterExample : IActionFilter
          {
              public void OnActionExecuting(ActionExecutingContext context)
              {
                  // our code before action executes
              }
      
              public void OnActionExecuted(ActionExecutedContext context)
              {
                  // our code after action executes
              }
          }
      }
      
      We can do the same thing with an asynchronous filter by inheriting from IAsyncActionFilter, but we only have one method to implement the OnActionExecutionAsync:
      namespace ActionFilters.Filters
      {
          public class AsyncActionFilterExample : IAsyncActionFilter
          {
              public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
              {
                  // execute any code before the action executes
                  var result = await next();
                  // execute any code after the action executes
              }
          }
      }
      

      The Scope of Action Filters

      Like the other types of filters, the action filter can be added to different scope levels: Global, Action, Controller. If we want to use our filter globally, we need to register it inside the AddMvc() method in the ConfigureServices method:
      services.AddMvc(
        config =>
        {
           config.Filters.Add(new GlobalFilterExample());
        });
      
      But if we want to use our filter as a service type on the Action or Controller level, we need to register it in the same ConfigureServices method but as a service in the IoC container:
      services.AddScoped<ActionFilterExample>();
      services.AddScoped<ControllerFilterExample>();
      
      Finally, to use a filter registered on the Action or Controller level, we need to place it on top of the Controller or Action as a ServiceType:
      namespace AspNetCore.Controllers
      {
          [ServiceFilter(typeof(ControllerFilterExample))]
          [Route("api/[controller]")]
          [ApiController]
          public class TestController : ControllerBase
          {
              [HttpGet]
              [ServiceFilter(typeof(ActionFilterExample))]
              public IEnumerable<string> Get()
              {
                  return new string[] { "example", "data" };
              }
      
          }
      }
      

      Order of Invocation

      The order in which our filters are executed is as follows: Order of execution of filters - Action Filters Of course, we can change the order of invocation by adding an additional property Order to the invocation statement:
      namespace AspNetCore.Controllers
      {
          [ServiceFilter(typeof(ControllerFilterExample), Order=2)]
          [Route("api/[controller]")]
          [ApiController]
          public class TestController : ControllerBase
          {
              [HttpGet]
              [ServiceFilter(typeof(ActionFilterExample), Order=1)]
              public IEnumerable<string> Get()
              {
                  return new string[] { "example", "data" };
              }
      
          }
      }
      
      Or something like this on top of the same action:
      [HttpGet]
      [ServiceFilter(typeof(ActionFilterExample), Order=2)]
      [ServiceFilter(typeof(ActionFilterExample2), Order=1)]
      public IEnumerable<string> Get()
      {
          return new string[] { "example", "data" };
      }
      

      Improving the Code with Action Filters

      If we open the starting project from the AppStart folder from our repository, we can find the MoveController class in the Controllers folder. This controller has an implementation for all the CRUD operations. For the sake of simplicity, we haven’t used any additional layers for our API. This project also implements global error handling so if you are not familiar with that topic, we suggest you read Global Exception Handling in .NET Core Web API. Our actions are quite clean and readable without try-catch blocks due to global exception handling, but we can improve them even further. The important thing to notice is that our Movie model inherits from the IEntity interface:
      [Table("Movie")]
      public class Movie: IEntity
      {
          [Key]
          public Guid Id { get; set; }
          [Required(ErrorMessage = "Name is required")]
          public string Name { get; set; }
          [Required(ErrorMessage = "Genre is required")]
          public string Genre { get; set; }
          [Required(ErrorMessage = "Director is required")]
          public string Director { get; set; }
      }
      
      So let’s start with the validation code from the POST and PUT actions.

      Validation with Action Filters

      If we look at our POST and PUT actions, we can notice the repeated code in which we validate our Movie model:
      if (movie == null)
      {
           return BadRequest("Movie object is null");
      }
      
      if (!ModelState.IsValid)
      {
           return BadRequest(ModelState);
      }
      
      We can extract that code into a custom Action Filter class, thus making this code reusable and the action cleaner. So let’s do that. Let’s create a new folder in our solution explorer, and name it ActionFilters. Then inside that folder, we are going to create a new class ValidationFilterAttribute:
      using Microsoft.AspNetCore.Mvc.Filters;
      
      namespace ActionFilters.ActionFilters
      {
          public class ValidationFilterAttribute : IActionFilter
          {
              public void OnActionExecuting(ActionExecutingContext context)
              {
                  
              }
      
              public void OnActionExecuted(ActionExecutedContext context)
              {
                  
              }
          }
      }
      
      Now we are going to modify the OnActionExecuting method to validate our model:
      using ActionFilters.Contracts;
      using Microsoft.AspNetCore.Mvc;
      using Microsoft.AspNetCore.Mvc.Filters;
      using System.Linq;
      
      namespace ActionFilters.ActionFilters
      {
          public class ValidationFilterAttribute : IActionFilter
          {
              public void OnActionExecuting(ActionExecutingContext context)
              {
                  var param = context.ActionArguments.SingleOrDefault(p => p.Value is IEntity);
                  if(param.Value == null)
                  {
                      context.Result = new BadRequestObjectResult("Object is null");
                      return;
                  }
                  
                  if(!context.ModelState.IsValid)
                  {
                      context.Result = new BadRequestObjectResult(context.ModelState);
                  }
              }
      
              public void OnActionExecuted(ActionExecutedContext context)
              {          
              }
          }
      }
      
      Next, let’s register this action filter in the ConfigureServices method:
      public void ConfigureServices(IServiceCollection services)
      {
             services.AddDbContext<MovieContext>(options =>
                 options.UseSqlServer(Configuration.GetConnectionString("sqlConString")));
      
             services.AddScoped<ValidationFilterAttribute>();
      
             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
      }
      
      Finally, let’s remove that validation code from our actions and call this action filter as a service:
      [HttpPost]
      [ServiceFilter(typeof(ValidationFilterAttribute))]
      public IActionResult Post([FromBody] Movie movie)
      {
           _context.Movies.Add(movie);
           _context.SaveChanges();
      
           return CreatedAtRoute("MovieById", new { id = movie.Id }, movie);
      }
      
      [HttpPut("{id}")]
      [ServiceFilter(typeof(ValidationFilterAttribute))]
      public IActionResult Put(Guid id, [FromBody]Movie movie)
      {
          var dbMovie = _context.Movies.SingleOrDefault(x => x.Id.Equals(id));
          if (dbMovie == null)
          {
              return NotFound();
          }
      
          dbMovie.Map(movie);
      
          _context.Movies.Update(dbMovie);
          _context.SaveChanges();
      
          return NoContent();
      }
      
      Excellent. This code is much cleaner and more readable now without the validation part. And furthermore, the validation part is now reusable as long as our model classes inherit from the IEntity interface, which is the quite common behavior. If we send a POST request for example with the invalid model we will get the BadRequest response: Action Filters request

      Dependency Injection in Action Filters

      If we take a look at our GetById, DELETE and PUT actions, we are going to see the code where we fetch the move by id from the database and check if it exists:
      var dbMovie = _context.Movies.SingleOrDefault(x => x.Id.Equals(id));
      if (dbMovie == null)
      {
           return NotFound();
      }
      
      That's something we can extract to the Action Filter class as well, thus making it reusable in all the actions. Of course, we need to inject our context in a new ActionFilter class by using dependency injection. So, let’s create another Action Filter class ValidateEntityExistsAttribute in the ActionFilters folder and modify it:
      using System.Linq;
      
      namespace ActionFilters.ActionFilters
      {
          public class ValidateEntityExistsAttribute<T> : IActionFilter where T: class, IEntity
          {
              private readonly MovieContext _context;
      
              public ValidateEntityExistsAttribute(MovieContext context)
              {
                  _context = context;
              }
      
              public void OnActionExecuting(ActionExecutingContext context)
              {
                  Guid id = Guid.Empty;
      
                  if (context.ActionArguments.ContainsKey("id"))
                  {
                      id = (Guid)context.ActionArguments["id"];
                  }
                  else
                  {
                      context.Result = new BadRequestObjectResult("Bad id parameter");
                      return;
                  }
      
                  var entity = _context.Set<T>().SingleOrDefault(x => x.Id.Equals(id));     
                  if(entity == null)
                  {
                      context.Result = new NotFoundResult();
                  }
                  else
                  {
                      context.HttpContext.Items.Add("entity", entity);
                  }
              }
      
              public void OnActionExecuted(ActionExecutedContext context)
              {
              }
          }
      }
      
      We've created this Action Filter class to be generic so that we could reuse it for any model in our project. Furthermore, if we find the entity in the database, we store it in HttpContext because we need that entity in our action methods and we don’t want to query the database two times (we would lose more than we gain if we double that action). Now let’s register it:
      services.AddScoped<ValidateEntityExistsAttribute<Movie>>();
      And let’s modify our actions:
      [HttpGet("{id}", Name = "MovieById")]
      [ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))]
      public IActionResult Get(Guid id)
      {
          var dbMovie = HttpContext.Items["entity"] as Movie;
      
          return Ok(dbMovie);
      }
      [HttpPut("{id}")]
      [ServiceFilter(typeof(ValidationFilterAttribute))]
      [ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))]
      public IActionResult Put(Guid id, [FromBody]Movie movie)
      {
          var dbMovie = HttpContext.Items["entity"] as Movie;
      
           dbMovie.Map(movie);
      
           _context.Movies.Update(dbMovie);
           _context.SaveChanges();
      
           return NoContent();
      }
      
      [HttpDelete("{id}")]
      [ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))]
      public IActionResult Delete(Guid id)
      {
          var dbMovie = HttpContext.Items["entity"] as Movie;
      
           _context.Movies.Remove(dbMovie);
           _context.SaveChanges();
      
           return NoContent();
      }
      
      Awesome. Now our actions look great without code repetition, try-catch blocks or additional fetch request towards the database.

      Conclusion

      Thank you for reading this article. We hope you have learned new useful things. As we already said, we always recommend using Action Filters because they give us reusability in our code and cleaner code in our actions as well. [sc name="subscribe" formNumber="2792" contentType="Web Development"]]]>
      3738 0 0 0 421 https://howtocode.net/2018/10/action-filters-in-net-core-web-api-how-to-clean-up-your-actions/ 0 0 498 0 0 499 498 0 511 >(); thanks]]> 0 0 512 ) so you need to provide it a model which inherits from IEntity interface (in this case it is the Movie model class). If you want to create another controller to work with CRUD or any other operations with Customer model for example, during the registration of the ValidationEntityExistsAttribute filter, you need to provide the Customer model as well (Of course it needs to implement IEntity interface).]]> 511 0 779 0 0 780 779 0
      Creating Vue.js Client Side - Creating Components and Displaying Data from Backend https://code-maze.com/vuejs-creating-components/ Mon, 03 Sep 2018 06:00:19 +0000 https://code-maze.com/?p=4090 We have learned how to create an API service and fetch data from the backend. Now, it’s time to display that data to a user and for that, we need to create a component. As we mentioned in Part 1 of this series, the Vue.js components consist of three parts: a template, a script and a style.

      After we create a component, we have various ways to use it. We can register it globally to use it in any other component. Otherwise, we can internally register it inside a single component and it will be available only in that component. We can register a component in both ways by specifying a custom tag for that component and the component itself. When we use that custom tag somewhere in our code, that tag will be replaced with the template from that component. Another way of using component is actually rendering component when the route for that component is matched. All that being said, let’s create a component to display all the owner entities. [sc name="part_of_series" headline="This article is part of the series"] For the main page of this series, you can visit Vue.js Series. To download the source code for this part, visit Creating Components and Data Display Source Code. This post is divided into several sections:

      Creating a Component

      Let’s navigate to the src/components directory and create a new directory and name it owner. Then, we are going to create the OwnerList.component.vue file in that directory. For now, we are going to put some dummy text in the component template. Next, we are going to make a route for that component and check if everything is OK. Let’s create the OwnerList component:
      <template>
        <div>
          <p>This is owner-list component page.</p>
        </div>
      </template>
      <script>
      export default {
        name: 'OwnerList'
      };
      </script>
      Now, we are going to edit the src/router/index.js file and add the new route for this component:
        routes: [
          {
            path: '/',
            name: 'Home',
            component: Home
          },
          {
            path: '/owner/list',
            name: 'OwnerList',
            component: OwnerList
          },
          {
            path: '*',
            name: 'NotFound',
            component: NotFound
          }
        ]
      Finally, we are going to edit the Navbar.vue component and change the Owner Actions link to point to our new component. Let’s modify link in the Navbar.vuecomponent:
          <b-navbar-nav>
            <b-nav-item :to="{ name: 'OwnerList' }">Owner Actions</b-nav-item>
            <b-nav-item href="#">Owner Actions</b-nav-item>
          </b-navbar-nav>
      To inspect the results, let’s start our application by typing the npm run dev command inside a terminal window, and navigate to the Owner Actions menu item: The Owner List Component - Vue.js Interpolation Great! Now, let’s modify our component to fetch owners from the backend, store it in the variable inside a component and render that data in a template.

      Fetching Data From the Server

      It’s important to say that every Vue component is actually a new Vue instance. But what does that mean? That means that every component has its own isolated scope. If we use the same component at different places we are actually instantiating it multiple times, and each instance would have its own data. So, to summarize. Vue components are reusable Vue instances. Because Vue.js creates a Vue instance from our .vue files, we must follow some naming conventions in our script tag. For example, where we put components data, methods, lifecycle hooks, etc. Let’s create a variable where we are going to store our owners fetched from the backend:
      <template>
        <div>
          <p>This is owner-list component page.</p>
        </div>
      </template>
      <script>
      export default {
        name: 'OwnerList',
        data() {
          return {
            owners: []
          };
        }
      };
      </script>
      There is the data() function which returns a JSON object with keys and values. That’s how we declare data for our component. We declare the owners variable and set it’s initial value to an empty array. To access this variable in our code, we are going to use the this.owners expression and the this keyword will point to the Vue instance of this component. As a continuation, we are going to implement the created() lifecycle hook in which we are going to call our API service created in the previous part of this series. As a result, we are going to store data in the owners variable by using this.owners expression:
      <script>
      import OwnerService from '@/api-services/owner.service';
      
      export default {
        name: 'OwnerList',
        data() {
          return {
            owners: []
          };
        },
        created() {
          OwnerService.getAll().then((response) => {
            this.owners = response.data;
          });
        }
      };
      </script>
      Excellent.

      Displaying Data in a Template

      Now that we have our data, it’s time to change a template part of our component to actually display that data. So, we are going to do exactly that:
      <template>
        <div>
          <b-row>
            <b-col
              md="2"
              offset-md="10">
              <a href="#">Create owner</a>
            </b-col>
          </b-row>
          <br>
          <b-row>
            <b-col md="12">
              <div class="table-responsive">
                <table class="table table-striped">
                  <thead>
                    <tr>
                      <th>Owner name</th>
                      <th>Owner address</th>
                      <th>Date of birth</th>
                      <th>Details</th>
                      <th>Update</th>
                      <th>Delete</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr
                      v-for="owner in owners"
                      :key="owner.id">
                      <td>{{ owner.name }}</td>
                      <td>{{ owner.address }}</td>
                      <td>{{ owner.dateOfBirth }}</td>
                      <td>
                        <b-button variant="default">Details</b-button>
                      </td>
                      <td>
                        <b-button variant="success">Update</b-button>
                      </td>
                      <td>
                        <b-button variant="danger">Delete</b-button>
                      </td>
                    </tr>
                  </tbody>
                </table>
              </div>
            </b-col>
          </b-row>
        </div>
      </template>
      In case you wonder why we haven’t used the table component from the bootstrap-vue package, the answer is because we want to explain the v-for directive and how to use it to iterate through data and render some HTML multiple times. The table component from the bootstrap-vue package does all that under the hood.

      Interpolation and v-for

      Let’s again take a look at this code snippet:
                    <tr
                      v-for="owner in owners"
                      :key="owner.id">
                      <td>{{ owner.name }}</td>
                      <td>{{ owner.address }}</td>
                      <td>{{ owner.dateOfBirth }}</td>
                      <td>
                        <b-button variant="default">Update</b-button>
                      </td>
                      <td>
                        <b-button variant="success">Update</b-button>
                      </td>
                      <td>
                        <b-button variant="danger">Delete</b-button>
                      </td>
                    </tr>
      We can see the v-for directive inside the tr tag. That means that a tr tag with its children tags will be rendered for every owner in owners and owners is the variable from our data() function. We can also see the :key prop. It’s called a data binding and we will talk more about that in the next part of this series. For now, it’s only important to remember that the v-for directive needs a unique key for every element. When something changes in the owners array, Vue.js will automatically update our table with the new value, and Vue.js knows which element to update due to key property. You might have noticed curly braces as well. This is an interpolation and all it does is displaying some dynamic data in our template. In this case, the v-for directive creates the local variable owner which is only accessible in a tr element. It contains a single owner from the owners array and we can use the {{ owner.someProperty }} expression to actually render it in our template. We can also access any variable from our data() function. Let’s open a terminal and execute the npm run dev command. After starting the application, let’s click on Owner Actions and inspect the result:

      Conclusion

      As you can see, we have implemented all that we have learned so far including components, routes and API services. On the other hand, we’ve also we learned some new concepts. By reading this post you’ve learned:
      • How to execute an HTTP request
      • How to display data from the backend in the component
      • The way of render DOM element multiple times
      • And how to interpolate data from the component
      In the next part of the series, we are going to show how the parent component can pass some data to the child component and vice versa. Also, we are going to show how to listen to JavaScript events and how to create and listen to custom events.]]>
      4090 0 0 0 363 https://www.alvinashcraft.com/2018/09/04/dew-drop-september-4-2018-2795/ 0 0 1176 0 0
      Creating Vue.js Client Side - Components Props and Events https://code-maze.com/vuejs-props-events/ Mon, 10 Sep 2018 07:00:13 +0000 https://code-maze.com/?p=4119
    • Preparing the Project
    • Routing and Navigation
    • Axios HTTP Client and Environment Files
    • Creating Components and Displaying Data from Backend
    • Components Props and Events (Current article)
    • Details and Delete Owner Components
    • Create and Update Owner Components
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete series navigation visit: Vue.js Series. To download the source code for this part, visit Components Props and Events. This post is divided into several sections:

      Creating a New Component

      Do you remember the OwnerList component from a previous part? We are going to make a new component which will render a data of single owner inside <tr> tag for the OwnerList’s table. Let’s navigate to src/components/owner and create the OwnerListRow.vue component:
      <template>
        <tr>
          <td>{{ owner.name }}</td>
          <td>{{ owner.address }}</td>
          <td>{{ owner.dateOfBirth }}</td>
          <td>
            <b-button variant="default">Details</b-button>
          </td>
          <td>
            <b-button variant="success">Update</b-button>
          </td>
          <td>
            <b-button variant="danger">Delete</b-button>
          </td>
        </tr>
      </template>
      <script>
      export default {
        name: 'OwnerListRow',
        props: [ 'owner' ]
      };
      </script>
      In the example above we have used the owner variable but where did we declare it? The owner variable is located in the props array and that means that our new component expects from a parent component to send the owner object to this component as a property. So, what we need to do is to declare this OwnerListRow component in the OwnerList component, render it instead of the old <tr> tag and then send the owner object back to the OwnerListRow component. We are going to do exactly that:
      <template>
        <div>
          <b-row>
            <b-col
              md="2"
              offset-md="10">
              <a href="#">Create owner</a>
            </b-col>
          </b-row>
          <br>
          <b-row>
            <b-col md="12">
              <div class="table-responsive">
                <table class="table table-striped">
                  <thead>
                    <tr>
                      <th>Owner name</th>
                      <th>Owner address</th>
                      <th>Date of birth</th>
                      <th>Details</th>
                      <th>Update</th>
                      <th>Delete</th>
                    </tr>
                  </thead>
                  <tbody>
                    <owner-list-row
                      v-for="owner in owners"
                      :key="owner.id"
                      :owner="owner"/>
                  </tbody>
                </table>
              </div>
            </b-col>
          </b-row>
        </div>
      </template>
      <script>
      import OwnerService from '@/api-services/owner.service';
      import OwnerListRow from '@/components/owner/OwnerListRow';
      
      export default {
        name: 'OwnerList',
        components: {
          OwnerListRow
        },
        data() {
          return {
            owners: []
          };
        },
        created() {
          OwnerService.getAll().then((response) => {
            this.owners = response.data;
          });
        }
      };
      </script>
      Let’s analyze this code. First, we import the new OwnerListRow component. Then, we inform Vue.js that we want to use the OwnerListRow inside of this component. We do that by specifying a component’s name inside the components JSON object. Right now, Vue.js is going to create a custom tag owner-list-row that will point to the OwnerListRow component under the hood. Otherwise, we can specify a custom tag as well by changing it to:
        components: {
          'some-custom-tag': OwnerListRow
        },
      But we are satisfied with the auto-generated name and we will continue to use it. Now when we have the custom tag for our component, we are going to replace the <tr> tag with it. Let’s examine the following code snippet:
                  <tbody>
                    <owner-list-row
                      v-for="owner in owners"
                      :key="owner.id"
                      :owner="owner"/>
                  </tbody>
      In this code, we have the v-for directive and the :key prop, that help us render our owner-list-row component for every owner in the owners array. But also, in every iteration, we pass that owner to the child component by using the :owner="owner" statement. That’s called data binding and we have two type of data bindings. In this example, we use a colon (:) and it stands for dynamic binding. Its purpose is to bind variables, in this case, the owner object with all of its properties. If we change our owner in any way, child component will be notified about it and will receive a new value. That’s called reactivity. Another type of binding is without the colon and that type of binding only binds a string. For example, if we write owner=”owner” then we only pass the string owner and not the actual variable owner. Let’s open a terminal and type npm run dev to analyze results: Owner List Table - Props and Events As we can see, everything works the same as it worked in the previous part. We did this because it’s a good practice to have smaller components that we can reuse in other components. Our next task is to show all the details for a single owner. For now, we are going to show owner details when we click on the table row. To do that, we need to listen to the click event of that <tr> tag.

      Event Handling

      Vue.js can listen to native javascript events like onclick, onmouseover, etc. We can make our own events as well and listen to them. So, what we are going to do is to show you the usage of both cases right now. Let’s modify the template of the OwnerList.vue component:
                  <tbody>
                    <owner-list-row
                      v-for="owner in owners"
                      :key="owner.id"
                      :owner="owner"
                      @click.native="onOwnerClicked(owner.id)"/>
                  </tbody>
      Also, let’s create onOwnerClicked method inside a script tag:
      <script>
      import OwnerService from '@/api-services/owner.service';
      import OwnerListRow from '@/components/owner/OwnerListRow';
      
      export default {
        name: 'OwnerList',
        components: {
          OwnerListRow
        },
        data() {
          return {
            owners: []
          };
        },
        created() {
          OwnerService.getAll().then((response) => {
            this.owners = response.data;
          });
        },
        methods: {
          onOwnerClicked(ownerId) {
            console.log(ownerId);
          }
        }
      };
      </script>
      We can listen to events with @nameOfEvent and that event can reside in the native HTML tags as well as in the custom components. But there is some difference. If an event listener is placed on native HTML tags, then we are listening to the native JavaScript events. For example, if we write: <div @click=”clickHandler”/> we will listen to onClick event and will call the clickHandler from the method's object in the script tag. But if we want to listen on native JavaScript events on custom components, we need to use the .native modifier to tell Vue.js to listen on native events and not on custom events. We will show how to use the custom events in a few moments. When we click on the first row, onOwnerClicked method is triggered and we got the id of that owner in the console: On Owner Clicked Event - Props and Events Great. We want to emit custom events for every button in our OwnerListRow.vue component. Let’s do exactly that:
      <template>
        <tr>
          <td>{{ owner.name }}</td>
          <td>{{ owner.address }}</td>
          <td>{{ owner.dateOfBirth }}</td>
          <td>
            <b-button
              variant="default"
              @click="onDetailsClick">Details</b-button>
          </td>
          <td>
            <b-button
              variant="success"
              @click="onUpdateClick">Update</b-button>
          </td>
          <td>
            <b-button
              variant="danger"
              @click="onDeleteClick">Delete</b-button>
          </td>
        </tr>
      </template>
      <script>
      export default {
        name: 'OwnerListRow',
        props: [ 'owner' ],
        methods: {
          onDetailsClick() {
            this.$emit('details', this.owner.id);
          },
          onUpdateClick() {
            this.$emit('update', this.owner.id);
          },
          onDeleteClick() {
            this.$emit('delete', this.owner.id);
          }
        }
      };
      </script>
      You may wonder how can we listen to the click event on the b-button element. That’s because the b-button element emits a custom event named click when we click on that button. That’s exactly what we will use here. We’ll listen for the click event on all three buttons and then we’ll emit new events named details, update and delete. We also pass the owner’s id as a parameter of that event. Great! Now let’s listen to those events in the OwnerList.vue component. We are going to delete the @click.native event listener because we are going to open owner’s details when we click on the details button and not on a table row.
      <tbody>
        <owner-list-row
          v-for="owner in owners"
          :key="owner.id"
          :owner="owner"
          @details="detailsOwner"
          @update="updateOwner"
          @delete="deleteOwner"/>
      </tbody>
      ...
        methods: {
          detailsOwner(ownerId) {
            console.log('details', ownerId);
          },
          updateOwner(ownerId) {
            console.log('update', ownerId);
          },
          deleteOwner(ownerId) {
            console.log('delete', ownerId);
          }
        }
      Let’s open a web browser and click on all of the buttons to inspect results: All Three Events are Working - Props and Events Everything seems to be working!

      Conclusion

      Excellent. You have learned how to bind and pass some data to child components and also how to notify parent component about some events. By reading this post you’ve learned:
      • The way of creating component props
      • How data binding works in Vue.js
      • How to listen to events
      • And how to create custom events
      In the next part of this series, we are going to use the id parameter to display the details about a single owner, as well as update it and delete it.]]>
      4119 0 0 0
      Creating Vue.js Client Side - Using ref Keyword to Reference Components (Details and Delete Components) https://code-maze.com/vuejs-details-deleting/ Mon, 17 Sep 2018 06:00:29 +0000 https://code-maze.com/?p=4159 details, update and delete and now we are going to implement functionalities for these events. [sc name="part_of_series" headline="This article is part of the series"] For the complete series navigation visit: Vue.js Series. To download the source code for this part, visit Details and Deleting Owner Entity Source Code. This post is divided into several sections:

      Creating Owner Details Component

      In the previous part, we have created the custom details event and an event listener for that event. In the event handler, we’ve used the id parameter from the owner object we have clicked on, and now we are going to use that id to get the owner's details. To accomplish that, we need to create a new component in which we are going to render the owner’s data. Let’s do exactly that.

      Creating a Component

      Let’s Navigate to the src/components/owner directory and create the OwnerDetails.vue file:
      <template>
        <div>
          <div class="well">
            <div class="row">
              <div class="col-md-3">
                <strong>Owner name:</strong>
              </div>
              <div class="col-md-3">
                {{ owner.name }}
              </div>
            </div>
            <div class="row">
              <div class="col-md-3">
                <strong>Date of birth:</strong>
              </div>
              <div class="col-md-3">
                {{ owner.dateOfBirth }}
              </div>
            </div>
            <div
              v-if="owner.accounts && owner.accounts.length <= 2"
              class="row">
              <div class="col-md-3">
                <strong>Type of user:</strong>
              </div>
              <div class="col-md-3">
                <span class="text-success">Beginner user.</span>
              </div>
            </div>
            <div
              v-else
              class="row">
              <div class="col-md-3">
                <strong>Type of user:</strong>
              </div>
              <div class="col-md-3">
                <span class="text-info">Advanced user.</span>
              </div>
            </div>
          </div>
      
          <div class="row">
            <div class="col-md-12">
              <div class="table-responsive">
                <table class="table table-striped">
                  <thead>
                    <tr>
                      <th>Account type</th>
                      <th>Date created</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr
                      v-for="account in owner.accounts"
                      :key="account.id">
                      <td>{{ account.accountType }}</td>
                      <td>{{ account.dateCreated }}</td>
                    </tr>
                  </tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
      </template>
      <script>
      import OwnerService from '@/api-services/owner.service';
      
      export default {
        name: 'OwnerDetails',
        data() {
          return {
            owner: {}
          };
        },
        created() {
          OwnerService.getAccounts(this.$router.currentRoute.params.id).then((response) => {
            this.owner = response.data;
          });
        }
      };
      </script>
      The code snippet is pretty self-explanatory but however, let's analyze it.

      Component's Code Explanation

      We have the template with some interpolations to display owner’s name, birthday, etc. There is a conditional rendering as well, which will conditionally display Beginner user or Advanced user text based on the number of accounts related to the owner entity. In the script tag, we have the created lifecycle hook in which we fetch an owner’s id from a query string with the this.$router.currentRoute.params.id expression and then fetch a data from the backend for that owner.

      Implementing an API Service and Routing to the Component

      Now, let’s create the getAccounts method in our OwnerService:
        ...
        delete(id) {
          return Axios.delete(`${RESOURCE_NAME}/${id}`);
        },
      
        getAccounts(id) {
          return Axios.get(`${RESOURCE_NAME}/${id}/account`);
        }
      };
      We still need to create the route for this component. To do that, we are going to edit the src/router/index.js file:
          {
            path: '/owner/list',
            name: 'OwnerList',
            component: OwnerList
          },
          {
            path: '/owner/:id',
            name: 'OwnerDetails',
            component: OwnerDetails
          },
          {
            path: '*',
            name: 'NotFound',
            component: NotFound
          }
      We have just defined a new route which will accept id as a parameter through the query string. We can access the route parameters in our components through the this.$router.currentRoute.params object. Finally, we need to navigate to this component when we click on the Details button in the owner list table. Let’s edit the src/components/owner/OwnerList.vue file:
          detailsOwner(ownerId) {
            this.$router.push({ name: 'OwnerDetails', params: { id: ownerId } });
          },
      Great! Now, we are going to open a terminal and type the npm run dev command to view results: Owner Account Details - Begginer User - ref keyword Owner Account Details - Advanced User - ref keyword Everything looks perfect. Now let’s implement the Delete functionality.

      Deleting the Owner Entity

      When a user clicks on the Delete button, the confirmation dialog needs to show up. If we confirm the delete action, a request will be sent to the server. The server may respond with 204 (No Content) status code which means that we have successfully deleted the owner entity. Otherwise, the server may respond with one of the error messages, depending on what went wrong. In both outcomes, we will display a modal which will inform us if our action was successful or not. So, let’s summarize it. We are going to create two modals. One is confirmation modal which has a static content and the second one is a modal which will inform us about the response of the delete request. So, in the second one, we are going to implement the data binding because we need to display different messages depending on the server response.

      Creating a Component

      Let’s navigate to the OwnerList.vue component and implement that data binding action:
      <template>
        <div>
          <b-row>
            <b-col
              md="2"
              offset-md="10">
              <a href="#">Create owner</a>
            </b-col>
          </b-row>
          <br>
          <b-row>
            <b-col md="12">
              <div class="table-responsive">
                <table class="table table-striped">
                  <thead>
                    <tr>
                      <th>Owner name</th>
                      <th>Owner address</th>
                      <th>Date of birth</th>
                      <th>Details</th>
                      <th>Update</th>
                      <th>Delete</th>
                    </tr>
                  </thead>
                  <tbody>
                    <owner-list-row
                      v-for="owner in owners"
                      :key="owner.id"
                      :owner="owner"
                      @details="detailsOwner"
                      @update="updateOwner"
                      @delete="deleteOwner"/>
                  </tbody>
                </table>
              </div>
            </b-col>
          </b-row>
          <b-modal
            ref="deleteConfirmModal"
            title="Confirm your action"
            @ok="onDeleteConfirm"
            @hide="onDeleteModalHide">
            <p class="my-4">Are you sure you want to delete this owner?</p>
          </b-modal>
      
          <b-modal
            ref="alertModal"
            :title="alertModalTitle"
            :ok-only="true">
            <p class="my-4">{{ alertModalContent }}</p>
          </b-modal>
        </div>
      </template>
      <script>
      import OwnerService from '@/api-services/owner.service';
      import OwnerListRow from '@/components/owner/OwnerListRow';
      
      export default {
        name: 'OwnerList',
        components: {
          OwnerListRow
        },
        data() {
          return {
            owners: [],
            selectedOwnerId: null,
            alertModalTitle: '',
            alertModalContent: ''
          };
        },
        created() {
          this.fetchOwners();
        },
        methods: {
          detailsOwner(ownerId) {
            this.$router.push({ name: 'OwnerDetails', params: { id: ownerId } });
          },
          updateOwner(ownerId) {
            console.log('update', ownerId);
          },
          deleteOwner(ownerId) {
            this.selectedOwnerId = ownerId;
            this.$refs.deleteConfirmModal.show();
          },
          fetchOwners() {
            OwnerService.getAll().then((response) => {
              this.owners = response.data;
            });
          },
          onDeleteConfirm() {
            OwnerService.delete(this.selectedOwnerId).then(() => {
              this.alertModalTitle = 'Successfully';
              this.alertModalContent = 'Successfully deleted Account Owner';
              this.$refs.alertModal.show();
              this.fetchOwners();
            }).catch((error) => {
              this.alertModalTitle = 'Error';
              this.alertModalContent = error.response.data;
              this.$refs.alertModal.show();
            });
          },
          onDeleteModalHide() {
            this.selectedOwnerId = null;
          }
        }
      };
      </script>
      First, let’s analyze the script part.

      Component's Code Explanation

      We have moved a fetch owner logic from the created lifecycle hook to the method named fetchOwners. That’s because we need to fetch all the owners again after we successfully delete one owner. Now that we’ve moved the logic to the method, we can reuse our fetch owner code. Next up, we create some data variables. In the selectedOwnerId variable we are going to store the id parameter from the owner entity we want to delete. In the remaining two variables we will store the title and the content for the second modal. Let’s take a look at the this.$refs statement. We use it for the first time and we are going to explain it right now. In the template part, we have two modals. On both modals, we have the ref=”something” attribute. Just as we can get an element in a vanilla JavaScript with the document.getElementById() expression, we can get a reference to the element (or another vue component, b-modal in this case) in the Vue.js by using the ref and this.$refs statements. We can use these references to the modals to trigger the show() method inside them. The rest of the template is also self-explanatory.

      Inspecting the Results

      Let’s again open a terminal and run the npm run dev command to view results. If we try to delete Anna Bosh, the confirmation modal will show up: Confirm your action - ref keyword When we click on the OK button, the error appears because Anna Bosh have the related accounts: Error modal - ref keyword If we try to delete Nick Somion, the confirmation dialogue shows up again: Confirm your action - ref keyword And the successfully message shows up: Successfully modal - ref keyword

      Conclusion

      By reading this post you’ve learned:
      • How to handle server errors
      • How to reference elements or components from the template
      • And how to create a confirmation dialogue
      In the next part of the series, we are going to implement creating and deleting the Account Owner and with that, we are finalizing or application development.]]>
      4159 0 0 0
      Creating Vue.js Client Side - Two-way binding and v-model directive (Create and Update Components) https://code-maze.com/vuejs-create-and-update/ Mon, 24 Sep 2018 07:00:43 +0000 https://code-maze.com/?p=4262
    • Preparing the Project
    • Routing and Navigation
    • Axios HTTP Client and Environment Files
    • Creating Components and Displaying Data from Backend
    • Components Props and Events
    • Details and Delete Owner Components
    • Create and Update Owner Components (Current article)
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete series navigation visit: Vue.js Series. To download the source code for this part, visit Create and Update Owner Entity Source Code. This post is divided into several sections:

      Creating Owner Entity

      We are going to create a form that has three input fields: name, date of birth and address. As soon as the user fills up the data and submits the form, we are going to send a POST request to the server. After receiving a response, a modal will show up and display if we successfully created the Owner entity or not. To make things easier to read and understand we are going to write this component in several segments. Let’s start with a template.

      The OwnerCreate Template

      First, we are going to create a form with three input fields. To do that, let’s create the OwnerCreate.vue component in the src/components/Owner folder:
      <template>
        <b-container fluid>
          <div class="form-wrapper">
            <b-form @submit.prevent="createOwner">
              <b-form-group 
                :label-cols="2" 
                breakpoint="md" 
                horizontal 
                label="Name of the owner:"
                for="name">
                <b-col :md="5">
                  <b-input 
                    id="name" 
                    v-model="formData.name" 
                    maxlength="60" 
                    required />
                </b-col>
              </b-form-group>
      
              <b-form-group
                :label-cols="2"
                breakpoint="md"
                horizontal
                label="Date of birth:"
                for="dateOfBirth">
                <b-col :md="5">
                  <b-input
                    id="dateOfBirth"
                    v-model="formData.dateOfBirth"
                    type="date"
                    required />
                </b-col>
              </b-form-group>
      
              <b-form-group
                :label-cols="2"
                breakpoint="md"
                horizontal
                label="Address:"
                for="Address">
                <b-col :md="5">
                  <b-input
                    id="Address"
                    v-model="formData.address"
                    maxlength="100"
                    required />
                </b-col>
              </b-form-group>
            </b-form>
          </div>
        </b-container>
      </template>
      In this template, we have two new features. On the form tag, there is a new statement @submit.prevent. We have learned in part 5 that @submit is the syntax to listen on the submit event. But now we have the .prevent modifier which is equivalent to the vanilla JavaScript expression event.preventDefault(). So, we are preventing the native browser behavior to redirect to the form’s action because we want to send data with Axios. Another new feature is v-model and we are going to explain that a bit later on. Under the form tag, we are going to create the Save and Cancel buttons. The first will submit the form and the second will redirect to the OwnerList route. So, right below the last b-form-grouptag we are going to insert this piece of code:
              <br ><br >
      
              <b-col
                :md="5"
                offset="4">
                <b-button
                  type="submit"
                  variant="info">Save</b-button>
                <b-button
                  :to="{ name: 'OwnerList' }"
                  variant="danger">Cancel</b-button>
              </b-col>
      Furthermore, we are going to create a modal to show an information whether or not our action was successful. We are going to put that modal before the closing b-container tag:
          <b-modal
            ref="alertModal"
            :title="alertModalTitle"
            :ok-only="true"
            @ok="onAlertModalOkClick">
            <p class="my-4">{{ alertModalContent }}</p>
          </b-modal>
      As a continuation, we are going to add some styles:
      <style>
      .form-wrapper {
        margin-top: 20px;
        min-height: 20px;
        padding: 19px;
        margin-bottom: 20px;
        background-color: #f5f5f5;
        border: 1px solid #e3e3e3;
        border-radius: 4px;
        box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
      }
      </style>
      Now, let’s implement the logic behind this template.

      OwnerCreate Script

      We need to create variables that will store the data from our inputs. What we need more is to create variables for a modal title and modal body. To do that, we are going to create one more variable isSuccessfully to show the correct message whether our POST request was successful or not. It’s time to explain the v-model directive and how we use it to bind these variables with inputs. It’s like the data binding expression (:prop=”value”) that we have seen in the previous parts but this one is a two-way binding. In the previous parts, we have only passed the data from the parent component to the child component using the :prop=”value” syntax and if a child component tries to modify that value, the warning will appear because that’s not allowed. By using the v-model directive, both parent and child component can modify that value and both components will be notified about that modification. Let’s implement that:
      <script>
      export default {
        name: 'OwnerCreate',
        data() {
          return {
            formData: {
              name: '',
              dateOfBirth: '',
              address: ''
            },
            alertModalTitle: '',
            alertModalContent: '',
            isSuccessfully: false
          };
        },
      };
      </script>
      Now, let’s implement the createOwner method which sends a POST request and the ‘formData’ object to the server. Depending on the server’s response, the isSuccessfully variable will be set to true or false. If the create action is successful, the modal will show up and the formData object will be cleaned up. If any problem occur, the modal will display an error message. Let’s implement that method:
        methods: {
          createOwner() {
            OwnerService.create(this.formData).then(() => {
              this.isSuccessfully = true;
              this.alertModalTitle = 'Successfully';
              this.alertModalContent = 'Successfully created Account Owner';
              this.$refs.alertModal.show();
      
              this.formData = {
                name: '',
                dateOfBirth: '',
                address: ''
              };
            }).catch((error) => {
              this.isSuccessfully = false;
              this.alertModalTitle = 'Error';
              this.alertModalContent = error.response.data;
              this.$refs.alertModal.show();
            });
          }
        }
      We also need to import the OwnerService reference:
      import OwnerService from '@/api-services/owner.service';
      When modal shows up, we need to click on the OK button. That click will trigger the onAlertModalOkClick method which will redirect a user to the OwnerList route if the response was successful. We are going to edit the OwnerCreate.vue component one more time to implement that method:
          onAlertModalOkClick() {
            if (this.isSuccessfully) {
              this.$router.push({ name: 'OwnerList' });
            }
          }
      We still need to make a route and a way to go to this component.

      Navigating to the OwnerCreate component

      Let’s edit the src/router/index.js file:
          {
            path: '/owner/list',
            name: 'OwnerList',
            component: OwnerList
          },
          {
            path: '/owner/create',
            name: 'OwnerCreate',
            component: OwnerCreate
          },
          {
            path: '/owner/:id',
            name: 'OwnerDetails',
            component: OwnerDetails
          },
      It is important to place our route above the OwnerDetails route. If we place it below, then when we access the /owner/create route the OwnerDetails route will be triggered because create part of the route will be recognized as the :id parameter. And finally, let’s modify the src/components/Owner/OwnerList.vue component:
      <template>
        <div>
          <b-row>
            <b-col
              md="2"
              offset-md="10">
              <router-link :to="{ name: 'OwnerCreate' }">Create owner</router-link>
            </b-col>
          </b-row>
          ....
      The <router-link> tag will create an <a> tag but will not initiate redirection process when clicked, it will only trigger vue-router to switch a route. Great! Now, let’s start a terminal and type the npm run dev command to check if everything is OK: Creating Account Owner - v-model When we enter valid data and click on the Save button: Successfully created Account Owner - v-model

      Editing Owner Entity

      The edit form is almost identical as the create form. There are only a few differences. We have the :id parameter in a route and we have to fetch the data for that owner from the backend and display it on the form. Once we click on the Save button, we are going to send the PUT request to the backend with the new data for that Owner. Let’s create the new OwnerUpdate.vue component inside the src/components/Owner directory. The only difference between this template and the one for creating the owner is the name of the event handler for the submit form. That being the case let’s just copy the template from the OwnerCreate.vue and paste it inside a new component and edit the name of the event handler:
      <template>
        <b-container fluid>
          <div class="form-wrapper">
            <b-form @submit.prevent="updateOwner">
              <b-form-group
                :label-cols="2"
                breakpoint="md"
                ...
      </template>
      In the script part, we have the same data as in the previous component. Moreover, the event handler for the modal’s ok button is the same. We are going to implement the logic for fetching owner in the created lifecycle hook and for the updateOwner method. So, let’s implement the script part of the OwnerUpdate.vue file:
      <script>
      import OwnerService from '@/api-services/owner.service';
      
      export default {
        name: 'OwnerUpdate',
        data() {
          return {
            formData: {
              name: '',
              dateOfBirth: '',
              address: ''
            },
            alertModalTitle: '',
            alertModalContent: '',
            isSuccessfully: false
          };
        },
        created() {
          OwnerService.get(this.$router.currentRoute.params.id).then((response) => {
            this.formData.name = response.data.name;
            this.formData.dateOfBirth = response.data.dateOfBirth.split('T')[0];
            this.formData.address = response.data.address;
          });
        },
        methods: {
          updateOwner() {
            OwnerService.update(this.$router.currentRoute.params.id, this.formData).then(() => {
              this.isSuccessfully = true;
              this.alertModalTitle = 'Successfully';
              this.alertModalContent = 'Successfully updated Account Owner';
              this.$refs.alertModal.show();
            }).catch((error) => {
              this.isSuccessfully = false;
              this.alertModalTitle = 'Error';
              this.alertModalContent = error.response.data;
              this.$refs.alertModal.show();
            });
          },
          onAlertModalOkClick() {
            if (this.isSuccessfully) {
              this.$router.push({ name: 'OwnerList' });
            }
          }
        }
      };
      </script>
      Let’s also define the route for this component:
          {
            path: '/owner/create',
            name: 'OwnerCreate',
            component: OwnerCreate
          },
          {
            path: '/owner/update/:id',
            name: 'OwnerUpdate',
            component: OwnerUpdate
          },
          {
            path: '/owner/:id',
            name: 'OwnerDetails',
            component: OwnerDetails
          },
      And finally, we need to direct a user to this page when a user clicks on the Update button on the OwnerList component:
          updateOwner(ownerId) {
            this.$router.push({ name: 'OwnerUpdate', params: { id: ownerId } });
          },
      Let’s again type npm run dev command in a terminal and see results: When we click the Update button on the OwnerList component, we will be directed to a new component and the form will be populated with the actual data: Updating Account Owner - v-model Let’s try to modify our input fields and click on the Save button. The modal will appear: Successfully updated Account Owner - v-model And we will have updated Owner entity on the OwnerList component.

      Conclusion

      We have finished our Vue.js journey. Now we have a fully functional application that is ready for deployment. Vue.js has many other great features, which we haven’t covered in this series, but we have learned a lot from this series as well. By reading this post you have learned:
      • How two-way binding works
      • How to prevent the default behavior of events
      • The way to handle POST request
      • The way to handle PUT request
      ]]>
      4262 0 0 0 406 https://www.alvinashcraft.com/2018/09/24/dew-drop-september-24-2018-2809/ 0 0
      C# Back to Basics - Ref and Out Keywords in C# https://code-maze.com/cshrap-basics-ref-out-keywords/ Fri, 31 Aug 2018 07:00:42 +0000 https://code-maze.com/?p=4319 ref and out keywords. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. If you want to download the source code for our examples, you can do that from here Ref and Out Keywords Source Code. This article is divided into several sections: In the previous post, we were passing a value type arguments while calling our methods. Why is the type of argument important here? Well, when we pass the argument of type int, double, decimal etc (basic value types), we do not pass the actual value but its copy. This means that our original values are not changed inside the methods, because we pass a new copy of the original value. As a result, all the operations inside a method are executed upon the copy value. We can show this in an example:
      class Program
      {
          public static void ChangeAndWrite(int number)
          {
              number = 10;
              Console.WriteLine($"Inside ChangeAndWrite method, number value is: {number}");
          }
          static void Main(string[] args)
          {
              int number = 5;
              Console.WriteLine($"Value of the number prior to ChangeAndWrite call is: {number}");
      
              ChangeAndWrite(number);
              Console.WriteLine($"Value of the number after the ChangeAndWrite call is: {number}");
      
              Console.ReadKey();
          }
      }
      
      Value parameter - Ref and Out Keywords in C# As we can see, the value of the number variable changes only inside the ChangeAndWrite method. But the original value is the same as before calling the ChangeAndWrite method. And again, this is because we are passing the exact copy of the original value.

      Using Ref and Out Keywords

      We can change this default behavior. If we want to change the original values inside our methods, we can do that by using ref and out keywords inside the method signature and inside the method call as well. We can use the ref keyword only if the variable which we use as an argument is initialized before calling a method. By using the out keyword, we don't have to initialize a variable before calling a method but, we must initialize it inside a method. So, let’s simplify that. If we want to change an existing value of a variable inside a method, we are going to use the ref keyword. But, if we want to assign a completely new value to the variable inside a method, then we use the out keyword.

      Example 1 for the Value Type

      In the previous example, we saw how the value type variables behave if we don’t use the ref or out keywords. In this one, we are going to see the behavior of value type variables when we use those keywords:
      class Program
      {
          public static void ChangeRef(ref int numberRef)
          {
              numberRef = 25;
              Console.WriteLine($"Inside the ChangeRef method the numberRef is {numberRef}");
          }
      
          public static void ChangeOut( out int numberOut)
          {
              numberOut = 60;
              Console.WriteLine($"Inside the ChangeOut method the numberOut is {numberOut}");
          }
          static void Main(string[] args)
          {
              int numberRef = 15;
       
              Console.WriteLine($"Before calling the ChangeRef method the numberRef is {numberRef}");
              ChangeRef(ref numberRef);
              Console.WriteLine($"After calling the ChangeRef method the numberRef is {numberRef}");
      
              Console.WriteLine();
      
              int numberOut;
              Console.WriteLine("Before calling the ChangeOut method the numberOut is unassigned");
              ChangeOut(out numberOut);
              Console.WriteLine($"After calling the ChangeOut method the numberOut is {numberOut}");
      
              Console.ReadKey();
          }
      }
      
      Sending value parameter - Ref and Out Keywords in C# This all is pretty clear. If we use the ref or the out keyword on the value type variable, its original value will change. But the difference is that with the out keyword we can use unassigned variables.

      Example 2 for the Reference Type

      We have learned, that the reference type doesn’t store its value inside its own memory location. It stores the address towards the memory location where the value is stored. Therefore when we send an argument as a reference type to the method and change that parameter, the original value is changed. This is because we are not sending the copy of the value but the copy of the address that points to the original value. This is the same thing as when we use the ref keyword with the value types. Still, we can use the ref keyword with the reference types if we want to create a new object with the same address. Let’s see all of this in an example:
      class Program
      {
          public static void ChangeColor(Pen pen)
          {
              pen.Color = Color.Green;
              Console.WriteLine($"Inside the ChangeColor method the color is {pen.Color}");
          }
      
          public static void CreateNewObjectWithoutRef(Pen pen)
          {
              pen = new Pen(Color.Red);
              Console.WriteLine($"Inside the CreateNewObjectWithoutRef method the color of new pen object is {pen.Color}");
          }
      
          public static void CreateNewObjectWithRef(ref Pen pen)
          {
              pen = new Pen(Color.Yellow);
              Console.WriteLine($"Inside the CreateNewObjectWithRef method the color of new pen object is {pen.Color}");
          }
      
          static void Main(string[] args)
          {
              Pen pen = new Pen(Color.Blue);
      
              Console.WriteLine($"Before ChangeColor method: {pen.Color}");
              ChangeColor(pen);
              Console.WriteLine($"After the ChangeColor method: {pen.Color}");
      
              Console.WriteLine();
      
              Console.WriteLine($"Before CreateNewObjectWithoutRef method: {pen.Color}");
              CreateNewObjectWithoutRef(pen);
              Console.WriteLine($"After CreateNewObjectWithoutRef method: {pen.Color}");
      
              Console.WriteLine();
      
              Console.WriteLine($"Before CreateNewObjectWithRef method: {pen.Color}");
              CreateNewObjectWithRef(ref pen);
              Console.WriteLine($"After CreateNewObjectWithRef method: {pen.Color}");
      
              Console.ReadKey();
          }
      }
      
      In the first method, we are not using the ref keyword. The value is changed because we pass the copy of the address in which the original value is stored. In the second method, the original value stays the same. That's because we create a new object inside method thus the new memory address is allocated. But in the third method, we are using the ref keyword and the original value changes. Why? Because with the ref keyword we are copying the same address to a new object.

      Conclusion

      Now we know how to use ref and out keywords with the value and reference types. This is quite a useful feature in C#, thus knowing how to work with those keywords is an advantage for the developers. In the next post, we will talk about recursion and recursive methodsrecursion and recursive methods. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4319 0 0 0 358 https://www.alvinashcraft.com/2018/08/31/dew-drop-august-31-2018-2794/ 0 0
      C# Back to Basics - Recursion and Recursive Methods https://code-maze.com/csharp-basics-recursion/ Wed, 05 Sep 2018 07:00:30 +0000 https://code-maze.com/?p=4334
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods (Current article)
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. If you want to download the source code for our examples, you can do that from here Recursive Methods in C# Source Code. So what is recursion? Recursion is a concept in which method calls itself. Every recursive method needs to be terminated, therefore, we need to write a condition in which we check is the termination condition satisfied. If we don't do that, a recursive method will end up calling itself endlessly. Example 1: Create an application which calculates the sum of all the numbers from n to m recursively:
      class Program
      {
          public static int CalculateSumRecursively(int n, int m)
          {
              int sum = n;
      
              if(n < m)
              {
                  n++;
                  return sum += CalculateSumRecursively(n, m);
              }
      
              return sum;
         }
      
          static void Main(string[] args)
          {
              Console.WriteLine("Enter number n: ");
              int n = Convert.ToInt32(Console.ReadLine());
      
              Console.WriteLine("Enter number m: ");
              int m = Convert.ToInt32(Console.ReadLine());
      
              int sum = CalculateSumRecursively(n, m);
      
              Console.WriteLine(sum);
      
              Console.ReadKey();
          }
      }
      
      Recursion and Recursive Methods

      Code Explanation

      The method CalculateSumRecursively is our recursive method that calculates the sum of the numbers from n to m. The first thing we do is to set our sum to the value of n. Then, we check if the value of n is less than the value of m. If it is, we increase the value of n by 1 and add to our sum a result of the same method but with the increased n. If it is not, we just return the value of the sum variable. The C# will reserve memory storage for every recursive method so that the values from the previous method are not overridden. So let's see our example through the diagram: Graph Recursion and Recursive Methods

      Additional Example

      Let’s practice some more with the Example2: Create an application which prints out how many times the number can be divided by 2 evenly:
      class Program
      {
          public static int CountDivisions(double number)
          {
              int count = 0;
      
              if(number > 0 && number % 2 == 0)
              {
                  count++;
                  number /= 2;
      
                  return count += CountDivisions(number);
              }
      
              return count;
          }
      
          static void Main(string[] args)
          {
              Console.WriteLine("Enter your number: ");
              double number = Convert.ToDouble(Console.ReadLine());
      
              int count = CountDivisions(number);
              Console.WriteLine($"Total number of divisions: {count}");
      
              Console.ReadKey();
          }
      }
      

      Conclusion

      Excellent. Now we have a good knowledge of recursion and recursive methods. In the next post, we are going to talk about Arrays in C#, how to use them and about different types of arrays. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4334 0 0 0 366 https://www.alvinashcraft.com/2018/09/05/dew-drop-september-5-2018-2796/ 0 0 791 http://www.brandwebdirect.com/website-design-services 0 0
      C# Back to Basics - Arrays in C# https://code-maze.com/csharp-basics-arrays/ Fri, 07 Sep 2018 07:57:57 +0000 https://code-maze.com/?p=4339
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays) (Current article)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. To download the source code, visit Arrays in C# Source Code. Arrays are the reference data types that consist of data of the same type, arranged in the sequential collection. We can access any information inside an array by stating the name of the array and the position of the data. The data position inside an array is called Index. In arrays, indexes are zero-based. That means that the first element is stored in the index zero and the last element is on the array Length – 1 index. So, if our array has 5 elements, indexes are addressed from 0 to 4. This post is divided into several sections:

      Array Declaration and Initialization

      To declare an array we state the type of that array then the square brackets and finally the name of that array:
      int[] numbers;
      Pen[] pens;
      
      The important thing to know is that no matter whether we store the reference type or the value type data inside an array, the array is always a reference data type. To initialize our arrays we need to use a new keyword then the data type and finally the square brackets with the array capacity inside:
      numbers = new int[5];
      pens = new Pen[5];
      
      In a first example, we store the type int (value type) inside the numbers array thus reserving the space in our memory for five integers. But in the second example, we are reserving the space in our memory for five Pen types (reference types) so we are not storing their values but their references. All the Pen values are null for now. Until now, we have just allocated the memory for our values, we didn’t actually add those values at all. So, to finish the initialization process we need to add values to our arrays. The most common way is to declare, allocate and initialize an array in one line of code:
      int[] arrayExample = new int[5] { 4, 5, 7, 8, 3};
      Pen[] penArrayExample = new Pen[3] { new Pen(Color.Red), new Pen(Color.Green), new Pen(Color.Blue) };
      
      We can use the indexes as well, to populate an array:
      int[] numbers = new int[2];
      numbers[0] = 5; numbers[1] = 7;
      

      Array Manipulation

      To manipulate with an array, we can use a for loop. With a for loop we are using indexes to access each element of an array:
      static void Main(string[] args)
      {
          int[] numbers = new int[5] { 4, 5, 7, 8, 3};
                 
          for(int i = 0; i < numbers.Length; i++)
          {
              Console.WriteLine(numbers[i]);
          }
      }
      
      We can do the same thing but with the foreach loop. Difference between these two approaches is because with the for loop we are using indexes to access elements (variable i), but with the foreach loop we are not using indexes but the actual values:
      static void Main(string[] args)
      {
          int[] numbers = new int[5] { 4, 5, 7, 8, 3};
      
          foreach(int i in numbers)
          {
              Console.WriteLine(i);
          }
      }
      

      Examples

      Example1: Create an application in which we create an array of n elements, populate that array with the random integer numbers, and finally print out all those numbers and their sum:
      class Program
      {
          //array is a reference type so every action in this method will affect original array
          public static void PopulateArray(int[] numbers)
          {
              Random r = new Random();
              for(int i = 0; i < numbers.Length; i++)
              {
                  numbers[i] = r.Next(1, 101);
                  Console.WriteLine($"The {i+1}. element is {numbers[i]}");
              }
          }
      
          public static void CalculateSum(int[] numbers)
          {
              int sum = 0;
              foreach (int i in numbers)
              {
                  sum += i;
              }
      
              Console.WriteLine($"The sum of all the elements is {sum}");
          }
      
           static void Main(string[] args)
          {
              Console.WriteLine("Enter an array capacity: ");
              int capacity = Convert.ToInt32(Console.ReadLine());
      
              int[] numbers = new int[capacity];
      
              PopulateArray(numbers);
              Console.WriteLine();
              CalculateSum(numbers);
      
              Console.ReadKey();
          }
      }
      
      Example Arrays in C#

      Parameter Arrays

      A params array enables us to pass a variable number of arguments to a method. To create a params array we must specify the params keyword when we specify the parameters for our method:
      public static void TestMethod(params int[] numbers)
      {
          //method body	
      }
      
      The effect of the params keyword is that it allows us to send any number of arguments to the method’s parameter without creating an array:
      static void Main(string[] args)
      {
         TestMethod(1, 2, 3);
      }
      
      Even though a params array is very useful, we still have some limitations while working with them:
      • We can’t use params keyword to work with two-dimensional arrays
      • Method overloading is not possible solely with the params keyword
      • We can’t specify ref or out keywords with params arrays
      • A params array has to be the last parameter in our method
      • A non-params method always take priority over the params methods
      Example2: Create an application that prints out the minimum of all the numbers sent to the PrintMin method:
      class Program
      {
          public static void PrintMin(params int[] numbers)
          {
              int min = numbers[0];
              for(int i=1; i < numbers.Length; i++)
              {
                  if(min > numbers[i])
                  {
                      min = numbers[i];
                  }
              }
      
              Console.WriteLine(min);
          }
          static void Main(string[] args)
          {
              PrintMin(49, 58, 12, 98, 47, 13);
      
              Console.ReadKey();
          }
      }
      

      Multi-Dimensional Array

      We know how to use single-dimensional arrays, but C# supports multi-dimensional arrays as well. In this section, we are going to talk about two-dimensional arrays. Why are they called two-dimensional? Well, because they have two dimensions, rows and columns. To create "2d" array, we are using the following syntax:
      int[,] numbersMultiDim = new int[3, 2] { { 1, 5 }, { 3, 8 }, { 6, 1 } };
      With this syntax, we create a two-dimensional array with three rows and two columns. So, in a graphical presentation it should look like this: Multi-dimensional Arrays in C# To access any number from this array we can use the syntax with the name of the array and the position of the number between square brackets. We are providing position by using indexes. So, the first row has the index 0 and the last one has the index (number of rows - 1). It is the same with the columns:
      int number = numbersMultiDim[2, 1]; // value 1    => third row on index 2 and second column on index 1
      To iterate through all the data we can use the for loop:
      for(int i = 0; i < numbersMultiDim.GetLength(0); i++)
      {
            for(int j = 0; j < numbersMultiDim.GetLength(1); j++)
            {
                Console.WriteLine(numbersMultiDim[i,j]);
            }
      }
      
      As you can see, we are using two loops to iterate through a two-dimensional array. First one is iterating through all the rows and the second one through all the columns in the current row. We use multidimensional arrays when we have to present our data in the multidimensional form. Specifically, we use two-dimensional arrays to work with the data in a table form with the rows and columns.

      Conclusion

      Great job. We have all the required knowledge to work with arrays and to use them in our development process. In the next part of the series, we are going to talk about StreamWriter and StreamReader classes in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4339 0 0 0
      C# Back to Basics - Working With Files, StreamWriter and StreamReader https://code-maze.com/csharp-basics-streamwriter-streamreader/ Wed, 12 Sep 2018 07:00:04 +0000 https://code-maze.com/?p=4345
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader (Current article)
    • Working with Files, File, and Directory
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics. If you want to download the source code for our examples, you can do that from here StreamWriter and StreamReader C# Source Code. The StreamReader and StreamWriter classes enable the reading and writing actions to a file. Both of these classes exist in the System.IO namespace as well as many other classes for working with files and directories. We are going to cover the following sections:

      Creating Objects for StreamWriter and StreamReader

      To create objects for the StreamReader and StreamWriter classes we need to use the standard initialization for the reference data types. We can execute this initialization in a couple of ways but the most common is by only providing an address to the file:
      StreamReader readerRelativePath = new StreamReader("test.txt");
      StreamReader readerAbsolutePath = new StreamReader("C:\\MyProject\\test.txt");
      StreamWriter writerRelativePath = new StreamWriter("test.txt");
      StreamWriter writerAbsolutePath = new StreamWriter("C:\\MyProject\\test.txt");
      
      As we can see from the code above, we can provide the relative or absolute path to our file. If we provide a relative path (just a name and extension) Visual Studio will place a file inside the projectName/bin/debug folder.

      StreamReader Methods

      StreamReader contains many different methods to work with files but we are going to mention few of those. The Read() method will return next sign as an integer number or -1 if we reached the end of the file. We can use explicit conversion (cast) to convert that integer into a char type:
      static void Main(string[] args)
      {
          StreamReader sr = new StreamReader("test.txt");
      
          int x;
          char ch;
      
          x = sr.Read();
                  
          while(x != -1)
          {
              ch = (char)x;
              //do stuff here
              x = sr.Read();
          }
      }
      
      The ReadLine() method will return a whole line as a string. If we reached the end of the file it will return null:
      static void Main(string[] args)
      {
          StreamReader sr = new StreamReader("test.txt");
      
          string line = sr.ReadLine();
      
          while(line != null)
          {
              //some coding
              line = sr.ReadLine();
          }
      }
      
      The ReadToEnd() method returns a whole file in one string. If there is nothing more to read it will return an empty string. The Peek() method checks the next character in the file or if it finds nothing it will return -1:
      static void Main(string[] args)
      {
          StreamReader sr = new StreamReader("test.txt");
          string line;
      
          while(sr.Peek() != -1)
          {
              line = sr.ReadLine();
              //some coding
          }
      }
      

      StreamWriter Methods

      The two most important methods for the StreamWriter class is the Write() and WriteLine(). With the Write() method we write a line inside a file but without moving to another line after. But with the WriteLine() method we write a line inside a file and moving to another line. It is very important to call the Close() method, after we are finished with using reader or writer objects. Example1: Create an application that writes five random numbers from 1 to 100 to a file named numbers.txt. Then it will read all the numbers from that file, print them out and print the maximum number:
      class Program
      {
          public static void WriteToFile(string path)
          {
              StreamWriter sw = new StreamWriter(path);
              Random r = new Random(); //class to generate random numbers
              for(int i = 1; i <= 5; i++)
              {
                  sw.WriteLine(r.Next(1,101));
              }
      
              sw.Close();
          }
      
          public static void PrintNumbersAndMax(string path)
          {
              StreamReader sr = new StreamReader(path);
              string line = sr.ReadLine();
              Console.WriteLine(line);
              int max = Convert.ToInt32(line);
      
              while ((line = sr.ReadLine()) != null)
              {
                  Console.WriteLine(line);
      
                  int temp = Convert.ToInt32(line);
                  if(temp > max)
                  {
                      max = temp;
                  }
              }
           
              sr.Close();
      
              Console.WriteLine($"Max number is: {max}");
          }
      
          static void Main(string[] args)
          {
              WriteToFile("numbers.txt");
      
              PrintNumbersAndMax("numbers.txt");
      
              Console.ReadLine();
         }
      }
      
      As we can see, we have to use the Close method to close our reader and writer. But there is an even better way to do this. By using the using block.

      Using Block

      The using block helps to manage our resources. It specifies a scope in which we use our resource, and once we leave that scope, the resource is going to be managed. To use the using block we need to specify the using keyword, create resources inside parentheses and declare the scope of the using block with the curly brackets:
      using (Resource creation)
      {
      
      }
      
      So, we can rewrite one of our methods form the previous example:
      public static void PrintNumbersAndMax(string path)
      {
          using (StreamReader sr = new StreamReader(path))
          {
              string line = sr.ReadLine();
              Console.WriteLine(line);
              int max = Convert.ToInt32(line);
      
              while ((line = sr.ReadLine()) != null)
              {
                  Console.WriteLine(line);
      
                  int temp = Convert.ToInt32(line);
                  if (temp > max)
                  {
                       max = temp;
                  }
              }
      
              Console.WriteLine($"Max number is: {max}");
          }
      }
      
      In this example, we are not using the Close method because as soon as execution leaves the body of the using statement, the StreamReader object is going to be managed.

      Conclusion

      There we go. Right now, we have a good knowledge to manipulate files from C#. In the next article, we are going to learn how to use File and Directory classes to manipulate files in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4345 0 0 0
      C# Back to Basics - Working With Files, File and Directory https://code-maze.com/csharp-basics-file-directory/ Fri, 14 Sep 2018 07:00:11 +0000 https://code-maze.com/?p=4348
    • Development Environment Setup
    • Data Types, Declarations and Variable Definitions
    • Operators in C#
    • Type Conversion
    • Input and Output in C#
    • Working with Strings
    • Conditions in C# (If, If-Else, If-ElseIf, Switch-Case)
    • Loops(While, Do-While, For)
    • Handling Exceptions
    • Access Modifiers
    • Methods
    • Ref and Out Keywords
    • Recursion and Recursive Methods
    • Arrays (single-dimensional and multi-dimensional arrays)
    • Working with Files, StreamWriter and StreamReader
    • Working with Files, File, and Directory (Current article)
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation of this series check out: C# Back to Basics.

      File Methods

      WriteAllText(string path, string contents) creates a new file and writes content to that file. If the target file already exists, it will overwrite it:
      string path = @"C:\FileExamples\test.txt";
      string content = "Example content as a string message";
      File.WriteAllText(path, content);
      
      WriteAllLines(string path, string[] contents) creates a new file and writes a specified string array, then it closes the file:
      string path = @"C:\FileExamples\WriteAllLines.txt";
      string[] contentArray = new string[3] { "Example content as a string message", "Another string text", "The last string" };
      File.WriteAllLines(path, contentArray);
      
      ReadAllText(string path) opens the file in the specified path, reads all the lines as a string, and then closes the file:
      string path = @"C:\FileExamples\WriteAllLines.txt";
      string readAllText = File.ReadAllText(path);
      Console.WriteLine(readAllText);
      
      ReadAllLines(string path) opens a text file, reads all lines of the file as a string array, and then closes the file:
      string path = @"C:\FileExamples\WriteAllLines.txt";
      string[] readAllLines = File.ReadAllLines(path);
      foreach (string line in readAllLines)
      {
          Console.WriteLine(line);
      }
      
      Delete(string path) Deletes the specified file:
      string path = @"C:\FileExamples\test.txt";
      File.Delete(path);
      
      Move(string sourceFileName, string destFileName)moves a specified file to a new location:
      string path = @"C:\FileExamples\test.txt";
      string moveToPath = @"C:\FileMoveExamples\MovedFile.txt";
      
      if(File.Exists(moveToPath)) //if the file on the target location exists, we need to remove it first.
      {
          File.Delete(moveToPath);
      }
      
      File.Move(path, moveToPath);
      
      AppendAllText(string path, string contents) opens a file, appends the content to the file, and then closes the file. If a file doesn’t exist, it will create a file, write the content, and close the file. This method is useful if we want to append a new content without overriding the previous one:
      string path = @"C:\FileExamples\test.txt";
      string content = "Append this content as a string message" + Environment.NewLine;
      File.AppendAllText(path, content);
      
      AppendAllLines(string path, IEnumerable<string> contents) appends lines to the file and then closes the file:
      string path = @"C:\FileExamples\test.txt";
      string[] content = new string[2] { "Append this content as a string message", "Another text line" };
      File.AppendAllLines(path, content); 
      

      Directory Methods

      CreateDirectory(string path) creates directories and subdirectories on the specified location unless they already exist. It returns a DirectoryInfo object for the existing directory:
      string path = @"C:\DirectoryExample\SubDir1\SubDir2";
      DirectoryInfo di = Directory.CreateDirectory(path);
      Console.WriteLine($"Full name: {di.FullName}, Name: {di.Name}, Parent: {di.Parent} ...");
      
      Delete(string path) deletes an empty directory from a specified path:
      string path = @"C:\DirectoryExample\SubDir1\SubDir2";
      Directory.Delete(path);
      
      Delete(string path, bool recursive) deletes the specified directory, and if it is stated, all the subdirectories and files in that directory:
      string path = @"C:\DirectoryExample";
      Directory.Delete(path, true);
      
      Move(string sourceDirName, string destDirName) moves a file or directory and its contents to a new location:
      string path = @"C:\DirectoryExample";
      string moveTo = @"C:\MoveDirectory";
      Directory.Move(path, moveTo);
      

      Conclusion

      Well done. With this article, we have finished with the basics of C#. In our next module, we are going to talk about object-oriented concepts in C# and how to integrate it into our code. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4348 0 0 0 385 https://www.alvinashcraft.com/2018/09/14/dew-drop-september-14-2018-2803/ 0 0
      C# Intermediate - Classes and Constructors in C# https://code-maze.com/csharp-classes-constructors/ Wed, 19 Sep 2018 07:00:24 +0000 https://code-maze.com/?p=4449 our module about C# basics. [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Classes and Constructors in C# Source Code.  In this article, we are going to talk about:

      Adding New Elements in Solution Explorer

      Even though we can create new classes inside the Program.cs file, it is much better to create a new class in a separate file. To do that, we need to right-click on our project name, choose Add and then New Item (Ctrl+Shift+A): new item in Visual Studio - Classes and Constructors in C# Then, we need to choose a class file and add it a name: Adding class to a project - Classes and Constructors in C#

      Defining Classes and How to Use Them

      In C#, to define a class, we need to use the class keyword. The class consists of members. All the class members are defined in the class body between two curly braces:
      public class Student
      {
          private string _name;
          private string _lastName;
      
          public string GetFullName()
          {
              return _name + ' ' + _lastName;
          }
       }
      
      We see that the body contains two private fields (variables in the class body are called fields) _name and _lastName and one public method GetFullName (if you are not familiar with the access modifiers: private, public etc. you can read more about them in our module 1 about C# basics). As we know from our module 1 C# basics, the class is a reference type, so to initialize it we need to use the new keyword:
      class Program
      {
          static void Main(string[] args)
          {
              Student student = new Student();
          }
      }
      
      Now the student object can access the members from the Student class. For now, we have only one method inside the Student class and we can call it with the student.GetFullName() syntax. This will return an empty string, but we are going to fix that as soon as we introduce constructors. It is so important not to confuse the terms class and object. The class is a type definition but an object is an instance of that type. We can have several object instances of the same class:
      Student student = new Student();
      Student student10 = new Student();
      Student student20 = new Student();
      Student student30 = new Student();
      

      Constructors

      When we use the new keyword to create an object, the CLR (Common Language Runtime) uses the class definition to construct that object for us by calling a constructor method. The constructor is a special method that has the same name as the class it is defined in, doesn’t return any value (not even void) and can take parameters. It runs automatically when we create an instance of a class. So, every time we use the new keyword to instantiate a class, we are calling a constructor of that class. Every class must have a constructor. If we don’t write one, the compiler automatically generates one for us. This type of constructor is called a default constructor. A default constructor will set all the data inside a class, to their default values (assigned values if we don’t assign them). So, in our example, the fields _name and _lastName will have an empty string as a value at a beginning. We can write our own default constructor as well:
      public class Student
      {
          private string _name;
          private string _lastName;
      
          public Student()
          {
              _name = string.Empty;
              _lastName = string.Empty;
          }
      
          public string GetFullName()
          {
              return _name + ' ' + _lastName;
          }
      }
      

      Constructor Overloading

      Our classes are not restricted on having just one constructor method. We can create more of them in a single class:
      public class Student
      {
          private string _name;
          private string _lastName;
      
          public Student()
          {
              _name = string.Empty;
              _lastName = string.Empty;
          }
      
          public Student(string name, string lastName)
          {
              _name = name;
              _lastName = lastName;
          }
      
          public string GetFullName()
          {
              return _name + ' ' + _lastName;
          }
      }
      
      Now we have two options to instantiate our class, first one with the default values (which we don’t have to write) and the overloaded one, which gives us the ability to set the values of our fields:
      class Program
      {
          static void Main(string[] args)
          {
              Student student = new Student(); //default constructor
      
              Student student1 = new Student("John", "Doe");//overloaded constructor
              Console.WriteLine(student1.GetFullName());
          }
      }
      
      There is one important thing to have in mind. If we create our own constructor for a class, the compiler won’t create a default one for us. So if we want to have a default one and the overloaded one, we must create both of them.

      Partial Classes

      In a real-world project, our class can be pretty large with so many lines of code. That kind of classes could become less readable and tough to maintain. To avoid that, we can use partial classes. Partial classes have even more advantages because multiple developers can work on the same class at the same time. Furthermore, we can create a partial method inside those classes as well. A partial class is nothing more than a part of a single class. To define partial classes, we need to use the partial keyword in each file:
      partial class Student
      {
          private string _name;
          private string _lastName;
      
          public Student()
          {
              _name = string.Empty;
              _lastName = string.Empty;
          }
      }
      
      partial class Student
      {
          public Student(string name, string lastName)
          {
              _name = name;
              _lastName = lastName;
          }
      
          public string GetFullName()
          {
              return _name + ' ' + _lastName;
          }
      }
      

      Conclusion

      In this article, we have learned:
      • What the classes are and how to use them
      • How to use constructors and how to overload them
      • How to use partial classes and why are they useful
      In the next article, we are going to talk about Properties in C#Properties in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4449 0 0 0 389 https://www.alvinashcraft.com/2018/09/19/dew-drop-september-19-2018-2806/ 0 0
      C# Intermediate - Properties in C# https://code-maze.com/csharp-properties/ Fri, 21 Sep 2018 07:00:55 +0000 https://code-maze.com/?p=4458
    • Classes and Constructors
    • Properties (Current article)
    • Static Members, Constants, and Extension Methods
    • Anonymous and Nullable Types
    • Structures
    • Enumerations
    • Inheritance
    • Interfaces
    • Abstract Classes
    • Generics
    • Queue, Stack, Hashtable
    • Generic List and Dictionary
    • Delegates
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Properties in C# Source Code.  We are going to divide this article into the following sections:

      Property Syntax

      The syntax of a property declaration can be used in the following way:
      Access_Modifier Type PropertyName
      {
          get
          {
              //read actions
          }
          set
          {
              //write action
          }
      }
      
      As we can see, a property can contain two blocks of code. The get block contains statements that execute when we read from a property. The set block contains statements that execute when we write to a property:
      public class Student
      {
          private string _name;
          private string _lastName;
      
          public string Name
          {
              get { return _name; }
              set { _name = value; }
          }
      
          public string LastName
          {
              get { return _lastName; }
              set { _lastName = value; }
          }
      
          public Student(string name, string lastName)
          {
              _name = name;
              _lastName = lastName;
          }
      
          public string GetFullName()
          {
              return _name + ' ' + _lastName;
          }
      
      }
      
      In the example above we see that our private fields are now exposed through the properties. If we want to read the value of the _name field all we have to do is to call the Name property with the student object. The same applies to the _lastName field. Moreover, if we want to set a value to our fields, all we have to do is to call a set block of our properties:
      class Program
      {
          static void Main(string[] args)
          {
              Student student = new Student("John", "Doe");
      
              string name = student.Name; //call to a get block of the Name property
              string lastName = student.LastName; // call to a get block of the LastName property
      
              student.Name = "David"; //call to a set block of the Name property
              student.LastName = "Dauni"; // call to a set block of the LastName property
          }
      }
      
      Our properties can have a complex code inside get or set blocks. They are not limited only to read a value or just to write a value. We can use conditions or method calls etc. in the get or set blocks:
      public int X
      {
          get 
          {
              return _x;
          }
          set
          {
              _x = CheckValue(value);
          }
      }	
      
      private int CheckValue(int val)
      {
          //code execution in here
      }
      

      Read-Only and Write-Only Properties

      We can declare a property that only has a get block and not the set. That kind of property is called Read-Only property. If we create a read-only property, we can only read the value of a private field. It is quite common to create a read-only property inside our class. What we want with it is to set it with the constructor method and then to use its value throughout the entire class, but never to set its value outside the constructor. If we try to set it, the compiler will throw an error:
      public string Name
      {
          get { return _name; }
      }
      
      Read only property error - Properties in C# In the same way, as we can create a read-only property, we can create a write-only property. That type of property has only the set block and not the get. It is not a common case to create write-only properties. Of course, if we need it, we can only set the values with this type of property and not read it:
      public string Name
      {
          set { _name = value; }
      }
      
      Write only property error - Properties in C#

      Property Accessibility

      We can specify an access modifier for our property (public, private…) if we want to restrict its availability. But in C# we can even override the accessibility of get or set accessors. So, what we can do is declare a public property which has the public get accessor and private set accessor. If our property is a public one, we don’t have to add the public keyword for the get accessor, it is going to be public anyway:
      public string Name
      {
           get { return _name; }
           private set { _name = value; }
      } 
      
      Privete set accessor - Properties in C# This means that we can read in all the classes from our Name property, but we can set it only within the Student class. When we use an accessor overriding inside the property, we must pay attention to the following rules:
      • We can change the accessibility level of only one accessor. There is no point in having both accessors modified. If we want to modify both accessors, we should just modify the property access level.
      • We can’t use access modifier on the get or set blocks that are less restrictive of the access modifier applied on a property itself. So, if our property is private, there is no point in having the public get or set block.

      Auto-Implemented Properties

      If no additional logic is required in a property accessor, we can use the auto-implemented properties for more readable and concise way of declaring properties. The auto-implemented property consists only of the get and set keywords, nothing more:
      public string Name { get; set; }
      public string LastName { get; set; }
      
      When we declare the properties like this, the compiler creates a private field for us, which could be accessed only through the property’s get or set accessors. So in our example instead of:
      private string _name;
      
      public string Name
      {
          get { return _name; }
          set { _name = value; }
      }
      
      We can just write:
      public string Name { get; set; }
      In the Visual Studio, we are even going to get a suggestion to use an auto property: Property suggestion

      Conclusion

      Excellent. In this article, we have learned:
      • About properties and it's syntax
      • How to use read and write-only properties
      • How to modify the accessibility level of the property
      • The way to use auto-implemented properties
      In the next article, we are going to talk about Static methods, static classes, and extension methods as well. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4458 0 0 0 401 https://www.alvinashcraft.com/2018/09/21/dew-drop-september-21-2018-2808/ 0 0 403 _name; set => _name = value; } or public string FullName => $"{_firstName} {_lastName}"; https://docs.microsoft.com/de-de/dotnet/csharp/programming-guide/classes-and-structs/properties]]> 0 0 404 403 0 563 https://datashoptalk.com/c-ironpython-and-python-reference-links/ 0 0
      ASP.NET Core Web API with EF Core Code-First Approach https://code-maze.com/net-core-web-api-ef-core-code-first/ Mon, 15 Oct 2018 07:00:43 +0000 https://code-maze.com/?p=4467 Database-First approach where we design our database first and then create the classes which match our database design. In the EF Core Code-First approach, we have full control over the code and the database is just a store without any logic. This approach is helpful in situations where we are starting with the development of a new project and we don’t have a clear picture of how our database should look like yet. We don’t have to worry about creating and updating the database. We can just make the changes in our code and then sync everything easily with the database. The important thing to note is that the manual changes that we make to the database could be lost during migration. So we should make changes to the code only. [sc name="part_of_series" headline="Recommended Articles"] The source code for this blog post can be found on this Github repo. Let’s have a look at how to create a .NET Core Web API application with the EF Core Code-First approach. The article is divided into the following sections:

      Setting Up the ASP.NET Core Web API Project

      As a first step, let’s set up an ASP.NET Core Web API Project. We have explained this in detail in one of our other articles: Creating and configuring a new ASP.NET Core Web API project The article linked above covers a lot of additional topics. You may go through the entire article if you want to, but the section linked above is quite enough to follow along with this article. Following the article linked above, let’s create a new project called EFCoreCodeFirstSample

      Configuring EF Core

      Once we have set up the project, the next step is to set up the EF Core. Following are the steps for configuring the EF Core:

      Defining the Model

      First, let's define the model. We will start by creating a folderModels within the root of the application. Let’s add a new class Employee.cs inside:
      using System;
      using System.ComponentModel.DataAnnotations;
      using System.ComponentModel.DataAnnotations.Schema;
      
      namespace EFCoreCodeFirstSample.Models
      {
          public class Employee
          {
              [Key]
              [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
              public long EmployeeId { get; set; }
              public string FirstName { get; set; }
              public string LastName { get; set; }
              public DateTime DateOfBirth { get; set; }
              public string PhoneNumber { get; set; }
              public string Email { get; set; }
          }
      }
      
      The code above defines the classEmployee with some properties. Additionally, we have decorated the EmployeeId property with Key and DatabaseGenerated attributes. We did this because we will be converting this class into a database table and the columnEmployeeId will serve as our primary key with the auto-incremented identity.

      Creating a Context File

      As the next step, let’s create a context class, define database connection and register the context. The process is explained in detail in one of our other articles: Context Class and the Database Connection Following the above article, let’s define the context file EmployeeContext.cs(it requires installed Microsoft.EntityFrameworkCore 3.0.0 package):
      using Microsoft.EntityFrameworkCore;
      
      namespace EFCoreCodeFirstSample.Models
      {
          public class EmployeeContext : DbContext
          {
              public EmployeeContext(DbContextOptions options)
                  : base(options)
              {
              }
      
              public DbSet<Employee> Employees { get; set; }
          }
      }
      
      and let’s define the database connection in the appsettings.json file as:
      {
        "Logging": {
          "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
          }
        },
        "ConnectionString": {
          "EmployeeDB": "server=MY_SERVER;database=EmployeeDB;User ID=MY_USER;password=MY_PASSWORD;"
        },
        "AllowedHosts": "*"
      }
      
      Of course, modify the ConnectionStringproperty to match with that of ours. Then let’s install the Microsoft.EntityFrameworkCore.SqlServer package and register our context in the Startup.cs:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext<EmployeeContext>(opts => opts.UseSqlServer(Configuration["ConnectionString:EmployeeDB"]));
          services.AddControllers();
      }

      Generating the Database from Code Using Migrations

      Our next step is to add Code-First Migrations. Migrations automate the creation of database based on our Model. The EF Core packages required for migration will be added with .NET Core project setup. Let's install the Microsoft.EntityFrameworkCore.Tools package and run the following command in the Package Manager console: PM> Add-Migration EFCoreCodeFirstSample.Models.EmployeeContext This will create the classes for supporting migrations. Now let’s apply those changes to the database. Run the following command: PM> update-database This will update the database based on our models. Now let’s verify that the database and tables are created by opening SQL Server Management Studio or Visual Studio Server Explorer: Database after migration We can see the database EmployeeDBis created with a table Employeeswhich contains the columns based on the fields we defined in our model. Each time we make changes to our entities and do a migration, we can see new migration files created in our solution and new entries in the table__EFMigrationsHistory. When using the EF Core Code-First approach the best practice is to make all modifications to the database through the model and then update the database by doing the migration. Ideally, we should not make any manual changes to the database. With that, the EF Core setup is complete.

      Seeding Data, Reverting Migrations and Creating DB Scripts

      Seeding Data

      Data seeding allows us to provide initial data during the creation of a database. Then, EF Core migrations will automatically determine what insert, update or delete operations need to be applied when upgrading the database to a new version of the model. So let's create some seed data now. For this, we need to override the OnModelCreating method in the EmployeeContext class:
        protected override void OnModelCreating(ModelBuilder modelBuilder)
              {
                  modelBuilder.Entity<Employee>().HasData(new Employee
                  {
                      EmployeeId = 1,
                      FirstName = "Uncle",
                      LastName = "Bob",
                      Email = "uncle.bob@gmail.com",
                      DateOfBirth = new DateTime(1979, 04, 25),
                      PhoneNumber = "999-888-7777"
      
                  }, new Employee
                  {
                      EmployeeId = 2,
                      FirstName = "Jan",
                      LastName = "Kirsten",
                      Email = "jan.kirsten@gmail.com",
                      DateOfBirth = new DateTime(1981, 07, 13),
                      PhoneNumber = "111-222-3333"
                  });
              }
      Here we have provided two Employee records that will be inserted into the database as part of the migration. Let's run the migration commands once again: Add-Migration EFCoreCodeFirstSample.Models.EmployeeContextSeed update-database This will create a new migration file in our Migrations folder and update the database with the seed data we provided: Now the Employee table in our database will look like this: Employee table after seeding

      Reverting Migrations

      After making changes to our EF Core model, the database schema will be out of sync. To bring it to sync with the model, let's add another migration. Let's add a new property Gender in our employee model and then do a migration. It is a good practice to give meaningful names to the migration like a commit message in a version control system. For example, if we add a new field Gender to the Employee model, we may give a name like AddEmployeeGender. Add-Migration EFCoreCodeFirstSample.Models.AddEmployeeGender Sometimes we add a migration and then realize we need to make additional changes to our model before applying it. To remove the last migration, we can use the command: Remove-Migration If we already applied a migration (or several migrations) to the database but need to revert it, we can use the same command to apply migrations, but specify the name of the migration we want to roll back to. Let's say we already applied the migration to add the Gender column to the database by using the below command. update-database Now we can see the new column Gender added to the Employee table: Employee table with gender Now let's say we want to revert this migration. We can use the same command by specifying the name of the previous migration: update-database EFCoreCodeFirstSample.Models.EmployeeContextSeed Once this is executed, we can see that the column Gender is removed from the Employee table: Employee table without gender We should remove the Gender property from the Employee class as well.

      Creating DB Scripts

      While deploying our migrations to a production database, it's useful to generate a SQL script. We can further tune the script to match the production database. Also, we can use the script along with various deployment tools. The command to generate the script is: Script-Migration Once we apply this command, we can see a SQL script generated with all changes related to our migrations.

      Recommendation

      If you want to learn in great detail about Entity Framework Core and many of its features, we recommend going through our Entity Framework Core series. Through the entire series, we talk about different EF Core features, from the Context classes and DbSet properties, relationships and none-relational configurations, additional migration information and querying the database. If you want, you have a place to learn a lot more about this topic.

      Creating the Repository

      Now that we have configured the EF Core, we need a mechanism to access the data context from our API. Directly accessing the context methods from the API controller is a bad practice and we should avoid that. So let’s implement a simple data repository using the repository pattern. We have explained this pattern in detail in one of our other articles: Implementing the repository pattern. Let’s add a new folder under Models and name itRepository. Then let’s create a new interface calledIDataRepository:
      namespace EFCoreCodeFirstSample.Models.Repository
      {
          public interface IDataRepository<TEntity>
          {
              IEnumerable<TEntity> GetAll();
              TEntity Get(long id);
              void Add(TEntity entity);
              void Update(TEntity dbEntity, TEntity entity);
              void Delete(TEntity entity);
          }
      }
      
      We will later inject this interface into our API Controller and API will be communicating with the data context using this interface. Next, let's create a concrete class that implements the interfaceIDataRepository. Let's add a new folder under Models calledDataManager. Then let's create a new class EmployeeManager:
      using System.Collections.Generic;
      using System.Linq;
      using EFCoreCodeFirstSample.Models.Repository;
      
      namespace EFCoreCodeFirstSample.Models.DataManager
      {
          public class EmployeeManager : IDataRepository<Employee>
          {
              readonly EmployeeContext _employeeContext;
      
              public EmployeeManager(EmployeeContext context)
              {
                  _employeeContext = context;
              }
      
              public IEnumerable<Employee> GetAll()
              {
                  return _employeeContext.Employees.ToList();
              }
      
              public Employee Get(long id)
              {
                  return _employeeContext.Employees
                        .FirstOrDefault(e => e.EmployeeId == id);
              }
      
              public void Add(Employee entity)
              {
                  _employeeContext.Employees.Add(entity);
                  _employeeContext.SaveChanges();
              }
      
              public void Update(Employee employee, Employee entity)
              {
                  employee.FirstName = entity.FirstName;
                  employee.LastName = entity.LastName;
                  employee.Email = entity.Email;
                  employee.DateOfBirth = entity.DateOfBirth;
                  employee.PhoneNumber = entity.PhoneNumber;
      
                  _employeeContext.SaveChanges();
              }
      
              public void Delete(Employee employee)
              {
                  _employeeContext.Employees.Remove(employee);
                  _employeeContext.SaveChanges();
              }
          }
      }
      
      The classEmployeeManager handles all database operations related to the employee. The purpose of this class is to separate the actual data operations logic from our API Controller. This class has the following methods for supporting CRUD operations: GetAll() - Gets all employee records from the database. Get() - Gets a specific employee record from the database by passing an Id. Add() - Creates a new employee record in the database. Update() - Updates a specific employee record in the database. Delete() - Removes a specific employee record from the database based on the Id. As a next step, let’s configure the repository using dependency injection. This can be done in the ConfigureServices method in the Startup.cs as below:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext<EmployeeContext>(opts => opts.UseSqlServer(Configuration["ConnectionString:EmployeeDB"]));
          services.AddScoped<IDataRepository<Employee>, EmployeeManager>();
          services.AddControllers();
      }
      

      Creating the API Controller

      Now that our DataManager is all set, let's create the API Controller and create the endpoints for handling CRUD operations. We have covered this in detail in one of our other articles: Creating a .NET Core Web API Controller. Following the above article, let’s create the EmployeeController class in the Controllersfolder as below:
      using System.Collections.Generic;
      using EFCoreCodeFirstSample.Models;
      using EFCoreCodeFirstSample.Models.Repository;
      using Microsoft.AspNetCore.Mvc;
      
      namespace EFCoreCodeFirstSample.Controllers
      {
          [Route("api/employee")]
          [ApiController]
          public class EmployeeController : ControllerBase
          {
              private readonly IDataRepository<Employee> _dataRepository;
      
              public EmployeeController(IDataRepository<Employee> dataRepository)
              {
                  _dataRepository = dataRepository;
              }
      
              // GET: api/Employee
              [HttpGet]
              public IActionResult Get()
              {
                  IEnumerable<Employee> employees = _dataRepository.GetAll();
                  return Ok(employees);
              }
      
              // GET: api/Employee/5
              [HttpGet("{id}", Name = "Get")]
              public IActionResult Get(long id)
              {
                  Employee employee = _dataRepository.Get(id);
      
                  if (employee == null)
                  {
                      return NotFound("The Employee record couldn't be found.");
                  }
      
                  return Ok(employee);
              }
      
              // POST: api/Employee
              [HttpPost]
              public IActionResult Post([FromBody] Employee employee)
              {
                  if (employee == null)
                  {
                      return BadRequest("Employee is null.");
                  }
      
                  _dataRepository.Add(employee);
                  return CreatedAtRoute(
                        "Get", 
                        new { Id = employee.EmployeeId },
                        employee);
              }
      
              // PUT: api/Employee/5
              [HttpPut("{id}")]
              public IActionResult Put(long id, [FromBody] Employee employee)
              {
                  if (employee == null)
                  {
                      return BadRequest("Employee is null.");
                  }
      
                  Employee employeeToUpdate = _dataRepository.Get(id);
                  if (employeeToUpdate == null)
                  {
                      return NotFound("The Employee record couldn't be found.");
                  }
      
                  _dataRepository.Update(employeeToUpdate, employee);
                  return NoContent();
              }
      
              // DELETE: api/Employee/5
              [HttpDelete("{id}")]
              public IActionResult Delete(long id)
              {
                  Employee employee = _dataRepository.Get(id);
                  if (employee == null)
                  {
                      return NotFound("The Employee record couldn't be found.");
                  }
      
                  _dataRepository.Delete(employee);
                  return NoContent();
              }
          }
      }
      
      That's it. We have successfully created a Web API controller with endpoints for handling CRUD operations.

      Testing the API

      Now let’s do a quick round of testing around our API endpoints using Postman. First, let’s create a new Employee using a Post request: POST request Next, let’s do a Get request to get all Employees. We can see the new Employee record which was created in the previous request: GET request Now, let’s do a Put request to test the update functionality by changing the last name: PUT Request Once again let’s do a Get request and verify that the last name has changed: GET_after_update Now that we have successfully tested the API endpoints, let’s verify that the changes we made are actually persisted in the database. Let’s open the SQL Server management studio and verify that the record is created in the Employee table: SQL Results

      Conclusion

      Well, that’s all for now. In this article, we have learned the following topics.
      • EF Core Code-First approach and when to use it
      • Setting up a .NET Core Web API project with EF Core Code-First approach
      • Creating a database from code by using migrations
      • Setting up a repository to handle communication between API and the data context
      • Create API endpoints for handling CRUD operations and testing them
      Hope you enjoyed the article. Happy programming! [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4467 0 0 0 438 https://www.alvinashcraft.com/2018/10/15/dew-drop-october-15-2018-2823/ 0 0 452 { IEnumerable GetAll(); TEntity GetById(long id);//Modified TEntity GetByMail(string mail);//Added void Add(TEntity entity); void Update(Employee employe, TEntity entity); void Delete(Employee employe); } I used the route to call the relevant Get. I added a new class in the model, named Course. I updated the model, but I don't know how to proceed with the IDataRepository interface. Should I have this : public interface IDataRepository { IEnumerable GetAll(); TEntity GetById(long id); TEntity GetByMail(string mail); TEntity GetByCourseName(string name); //Added void Add(TEntity entity); void UpdateEmployee(Employee employe, TEntity entity);//Modified void UpdateCourse(Course course, TEntity entity);//Added void DeleteEmployee(Employee employe);//Modified void DeleteCourse(Course course);//Added } Thx for your answer.]]> 0 0 453 Coursees {get;set;} property in the current EmployeeContext class. Then you should start another migration, as we did, to create additional table in the database. After that, you don't have to change IDataRepository interface because it is generic one. It accepts any model you send to it. What you need to do is to create another CourseManager class that inherits from the IDataRepository interface and implements it as well. Then to register it inside the IOC container as we did in the ConfigureServices method like this: services.AddScoped, CourseManager>(); And then you can create another controller, inject your IDataRepository object and use it in your actions as we did in the EmployeeController. That should solve all of your problems. As I said, just do the same thing as we did in this article.]]> 452 0 528 http://denizzzd.byethost4.com/pro/2018/09/24/interesting-articles-sep-dec-2018/ 0 0 631 0 0 632 0 0 653 0 0 702 631 0 717 653 0 828 https://www.brandwebdirect.com/website-design-portfolio-samples/web-design-portfolio-sample 0 0 899 0 0 900 https://code-maze.com/ https://docs.microsoft.com/en-us/aspnet/core/tutorials/publish-to-azure-webapp-using-vs?view=aspnetcore-2.2 You can simulate the whole thing on Azure. You need to deploy the app, provision the database and then use that connection string instead of the one you've been using until now. You'll see the connection string in the database description once you provision it. Once you've set everything up, you can run migrations from your local machine using a remote database connection string, there is no difference. Here are some examples of connection strings and how to query remote databases on Azure: https://docs.microsoft.com/en-us/azure/sql-database/sql-database-connect-query-dotnet-core Hope that helps. If you need any more help, do feel free to write.]]> 899 0 1063 https://www.brandwebdirect.com/website-design-portfolio-samples/web-design-portfolio-sample 0 0 1064 http://www.brandwebdirect.com/website-design-services 0 0 1135 0 0 1138 1135 0 1140 1138 0 1141 1140 0 1142 1141 0 1209 0 0 1210 1209 0 1279 0 0 1280 1279 0 1281 1280 0 1282 1281 0
      C# Intermediate - Static Members, Constants and Extension Methods https://code-maze.com/csharp-static-members-constants-extension-methods/ Wed, 26 Sep 2018 07:00:25 +0000 https://code-maze.com/?p=4495
    • Classes and Constructors
    • Properties
    • Static Members, Constants, and Extension Methods (Current article)
    • Anonymous and Nullable Types
    • Structures
    • Enumerations
    • Inheritance
    • Interfaces
    • Abstract Classes
    • Generics
    • Queue, Stack, Hashtable
    • Generic List and Dictionary
    • Delegates
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Static Members in C# Source Code.  We are going to divide this article into the following sections:

      About Static Methods

      When we define a method in a class, it belongs to that class, and every instance of that class will be able to access it. One class can have many such methods. But there are some methods that are independent of the specific class instance. That kind of methods is called “static methods”. So, the static methods are the methods that don’t belong to an instance of a class, can interact only with other static elements and have the static keyword in the method description. Let’s take the Sqrt() method for example. This method calculates the square root of a number, and we don’t have to instantiate the Math class (which the Sqrt belongs in) because this method is a static method:
      int number = 4;
      Console.WriteLine(Math.Sqrt(number));
      
      So, why is the Sqrt method a static method and not a nonstatic one? Well, the Sqrt accepts only one argument and it is enough to do its job. We provide an argument number and the method returns a square root of that number. We didn’t mention the Math class at all. That’s because we don’t have to. The Math class doesn’t provide any support to the Sqrt method to do its job. It only provides a space for the Sqrt method to reside in. When we have a case like this one, it is usually a good solution to create a method as a static one.

      Working with a Static Method

      To call a static method, as we said, we don’t need an instance of a class. We can call it with the following syntax: ClassName.MethodName(arguments…); So, when we want to use the Sqrt method or any other method from the Math class, we can call it like this:  Math.Sqrt(16);

      Creating a Field by Using the Const Keyword

      If we prefix our field with the const keyword, we can declare such a field where its value can never change. The keyword const is short for constant. A constant member is defined at compile time and it can’t be modified at runtime. We can create a const variable in the following way: AccessModifier const Type Name = Value; Const member - Static Members in C#

      Static Class

      In C#, next to static methods we can declare static classes as well. The static class can contain only the static members. Its purpose is to act as a holder for the utility methods and fields. There is no point in instantiating this type of classes by using the new keyword. Furthermore, we can’t do that at all. But we can create a default constructor as long as it is a static one. Any other type of constructor is illegal:
      public static class TestClass
      {
          private static int number;
      
          static TestClass()
          {
              number = 54;
          }
       }
      

      About Extension Methods and How to Use Them

      Let’s suppose that we want to add a new feature to the string type, for example, the FirstLetterUpperCase functionality that always makes the first letter of a string with upper case. We can write a normal method for that purpose:
      public static string FirstLetterUpperCase(string word)
      {
           char letter = Char.ToUpper(word[0]);
           string remaining = word.Substring(1);
      
           return letter + remaining;
      }
      
      static void Main(string[] args)
      {
           string word = "football";
           string newWord = FirstLetterUpperCase(word);
      }
      
      But, as we can see, we need to send a word as a parameter every time and to accept returned value every time as well. This is not the wrong approach but we can do it even better. There's where the extension methods come in. An extension method enables us to extend an existing type with additional static methods. We must create that kind of methods inside a static class and they have the first parameter prefixed with the this keyword. But why do we have to place a prefix in front of the first parameter? Because that parameter is an indicator that tells to the compiler which type we extend. So here is the previous example but with the extension method:
      public static class StringExtensions
      {
          public static string FirstLetterUpperCase(this string word)
          {
              char letter = Char.ToUpper(word[0]);
              string remaining = word.Substring(1);
      
              return letter + remaining;
          }
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              string word = "football"
                            .FirstLetterUpperCase();
      
              Console.WriteLine(word);
              Console.ReadKey();
          }
      }
      Extension methods result - Static Members in C# Excellent.

      Conclusion

      We are done with the static members and now we have a great tool in our toolbox that we can use while developing our C# applications. In this article, we have learned:
      • How to use static classes
      • The way to use static methods
      • How to create extension methods
      • About const keywords and creating constants
      In the next article, we are going to talk about Anonymous Types and Nullable Types in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4495 0 0 0
      C# Intermediate - Anonymous and Nullable Types https://code-maze.com/csharp-anonymous-nullable-types/ Fri, 28 Sep 2018 07:00:23 +0000 https://code-maze.com/?p=4500
    • Classes and Constructors
    • Properties
    • Static Members, Constants, and Extension Methods
    • Anonymous and Nullable Types (Current article)
    • Structures
    • Enumerations
    • Inheritance
    • Interfaces
    • Abstract Classes
    • Generics
    • Queue, Stack, Hashtable
    • Generic List and Dictionary
    • Delegates
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Anonymous and Nullable Types in C# Source Code.  We are going to divide this article into the following sections:

      Anonymous Classes

      An anonymous class is a class that does not have a name. This sounds strange but sometimes an anonymous class can be useful, especially when using query expressions. Let’s see what we mean by that. We can create an object of the anonymous class simply by using the new keyword in front of curly braces:
      myAnonymousObj = new { Name = "John", Age = 32 };
      This object contains two properties the Name and the Age. The compiler will implicitly assign the types to the properties based on the types of their values. So what this means basically is that the Name property will be of the string type and the Age property of the int type. But now, we can ask, what type the myAnonymousObj is? And the answer is that we don’t know, which is the point of anonymous classes. But in C# this is not a problem, we can declare our object as an implicitly typed variable by using the var keyword:
      var myAnonymousObj = new { Name = "nesto", Age = 32 };
      The var keyword causes the compiler to create a variable of the same type as the expression that we use to initialize that object. So let’s see a couple of examples of well-known types:
      var number = 15; // the number is of type int
      var word = "example"; //the word is of type string
      var money = 987.32; //the money is of type double
      
      We can access the properties of our anonymous object the same way we did with regular objects:
      Console.WriteLine($"The name of myAnonymousObject is {myAnonymousObj.Name}, the age is {myAnonymousObj.Age}");

      Nullable Types

      The null value is useful for initializing reference types. So, it is logical that we can’t assign the null value to the value type because the null is itself a reference. That being said, we can see that the following statement will throw an error: nullable type error - Anonymous and Nullable types in C# However, C# provides us with a modifier that we can use to declare a value type as a nullable value type. We can use the ? sign to indicate that value type is nullable:
      int? number = null;
      We can still assign an integer value to our nullable value type:
      int? number = null;
      int another = 200;
      
      number = 345;
      number = another;
      
      This is all valid. But if we try to assign the variable of an int type with a value of our nullable type, we are going to have a problem:
      int? number = null;
      int another = 200;
      
      another = number; //this is the problem
      
      This makes sense if we consider that the variable number might contain the null but the variable another can’t contain null at all.

      Properties of Nullable Types

      The nullable types expose a few properties which can come in handy while working on our projects. The HasValue property indicates whether a nullable type contains a value or it is a null. The Value property enables us to retrieve the value of the nullable type if it is not null:
      int? number = null;
      number = 234; //comment this line to print out the result from the else block
      
      if(number.HasValue)
      {
          Console.WriteLine(number.Value);
      }
      else
      {
           Console.WriteLine("number is null");
      }
      

      Conclusion

      In this article, we have learned:
      • How to use anonymous classes
      • What the nullable types are
      • About properties of nullable types
      In the next article, we are going to talk about Structures in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4500 0 0 0 411 https://www.alvinashcraft.com/2018/09/28/dew-drop-september-28-2018-2813/ 0 0
      C# Intermediate - Structures in C# https://code-maze.com/csharp-structures/ Wed, 03 Oct 2018 08:00:01 +0000 https://code-maze.com/?p=4559
    • Classes and Constructors
    • Properties
    • Static Members, Constants, and Extension Methods
    • Anonymous and Nullable Types
    • Structures (Current article)
    • Enumerations
    • Inheritance
    • Interfaces
    • Abstract Classes
    • Generics
    • Queue, Stack, Hashtable
    • Generic List and Dictionary
    • Delegates
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Structures in C# Source Code.  We are going to split this article into the following sections:

      Working with Structures

      A structure is a value type, in the opposite of a class which is a reference type, and it has its own fields, methods, and constructors like a class. Maybe you didn’t realize, but we have worked with structures in our previous articles, especially in module 1 C# basics. Int, double, decimal, bool type etc. are all aliases for the structures System.Int32, System.Int64 etc. In a table below, we can see the primitive types and what are they built from (class or structure): Table - Structures in C#

      Structure Declaration

      To declare our own structure, we need to use the struct keyword followed by the name of the type and then the body of the structure between two curly braces:
      public struct Time
      {
          private int _hours, _minutes, _seconds;
      }
      
      We can create our own constructor to initialize our private fields:
      public struct Time
      {
          private int _hours, _minutes, _seconds;
      
          public Time(int hours, int minutes, int seconds)
          {
              _hours = hours;
              _minutes = minutes;
              _seconds = seconds;
          }
      
          public void PrintTime()
          {
              Console.WriteLine($"Hours: {_hours}, Minutes: {_minutes}, Seconds: {_seconds}");
          }
      }
      
      To access our structure we can use this syntax:
      static void Main(string[] args)
      {
          Time time = new Time(3, 30, 25);
          time.PrintTime();
      
          Console.ReadKey();
      }
      

      Differences Between Classes and Structures

      • The structure is a value type, while the class is a reference type
      • We can’t declare our own default constructor in a structure. That’s because a structure is always generating a default constructor for us. In a class, we can create a default constructor because a class won’t generate then one for us
      • We can initialize fields in our structure by creating a non-default constructor, but we must initialize all of the fields inside that constructor. It is not allowed to left a single field without a value:
      Error - Structures in C# With a class, this is not a case
      • In a class, we can initialize instance fields at their point of declaration. In a structure, we cannot do that:
      Instance error - Structures in C#
      • An instance of a class lives on a heap memory while the instance of a structure lives on a stack
      • In a structure, we can create a non-default constructor, but nevertheless, the compiler will always generate the default one. This is not the case with a class.

      When to Use Structure Instead of a Class

      The general rule that we can follow is that our structures need to be small and simple types and above all immutable. For anything else, we should use a class. Why is immutability so important? Well let’s take a look at this example:
      class Test
      {
          public int Number { get; set; }
      
          public Test(int number)
          {
              Number = number;
          }
      }
      class Program
      {
          static void Main(string[] args)
          {
              Test test = new Test(10);
              Console.WriteLine(test.Number);
      
              ChangeNumber(test);
              Console.WriteLine(test.Number);
          }
      
          public static void ChangeNumber(Test test)
          {
              test.Number = 45;
          }
      }
      
      If we inspect the result, we will see printed out 10 and 45. And that is the correct result. But if we change our Test class to be a structure and then inspect the result, we will see 10 and 10. This can lead to the confusion and problems as well, because the consumer may expect that the ChangeNumber method would modify the Number property because we allowed it in the code. But if we create properties or fields immutably (as read-only in a structure) then we can avoid this kind of confusion. The consumer can assign values to the properties by calling the constructor method but after that those properties need to stay immutable.

      Conclusion

      In this article, we have learned:
      • What is structure and how to create one
      • What are the limitations while using structures
      • When to use structures in your code
      In the next article, we are going to talk about Enumerations in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4559 0 0 0 413 https://www.alvinashcraft.com/2018/10/03/dew-drop-october-3-2018-2816/ 0 0 414 0 0 415 414 0
      C# Intermediate - Enumerations in C# https://code-maze.com/csharp-enumerations/ Fri, 05 Oct 2018 08:43:53 +0000 https://code-maze.com/?p=4620
    • Classes and Constructors
    • Properties
    • Static Members, Constants, and Extension Methods
    • Anonymous and Nullable Types
    • Structures
    • Enumerations (Current article)
    • Inheritance
    • Interfaces
    • Abstract Classes
    • Generics
    • Queue, Stack, Hashtable
    • Generic List and Dictionary
    • Delegates
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Enumerations in C# Source Code.  We are going to split this article into the following sections:

      Working with Enumerations in C#

      Suppose we need to represent days in a week in our C# project. We can use an integer number to represent every single day in a week (from 0 to 6), and even though that would work just fine it is not readable at all. This is where enumerations excel. To declare enumeration we can use the following syntax:
      public enum DaysInWeek
      {
          Monday,
          Tuesday,
          Wednesday,
          Thursday,
          Friday,
          Saturday,
          Sunday
      }
      
      After we have declared our enumeration, we can use it in exactly the same way as any other type:
      static void Main(string[] args)
      {
          DaysInWeek monday = DaysInWeek.Monday;
      
          Console.WriteLine(monday); // It is going to print out Monday
          Console.ReadKey();
      }
      
      As we can see, we must write DaysInWeek.Monday and not just Monday because all enumeration literal names are in scope of their enumeration type.

      Choosing Enumeration Literal Values

      Internally, an enumeration type assigns the integer value to every element inside that enumeration. Those numbers start at 0 and increase by 1 for every other element. In our previous example, we print out the value that matches with the exact element of an enumeration. But we can print the integer value as well by casting it into its underlying type:
      static void Main(string[] args)
      {
           DaysInWeek monday = DaysInWeek.Monday;
      
           Console.WriteLine((int)monday); //prints out the 0
      
           Console.ReadKey();
      }
      
      If we prefer, we can assign a specific integer constant to the enumeration elements:
      public enum DaysInWeek
      {
          Monday=1,
          Tuesday,
          Wednesday,
          Thursday, Friday,
          Saturday,
          Sunday
      }
      
      If we do it like this, Monday will have the value 1 and all the others will be increased by one (Tuesday=2, Wednesday=3…). But we can assign a random value to each of the elements:
      public enum DaysInWeek
      {
          Monday=10,
          Tuesday=20,
          Wednesday=35,
          Thursday=48,
          Friday=74,
          Saturday=12,
          Sunday=154
      }
      
      Of course, it is always a better way to assign integer values with the equal progression (1, 2, 3… or 10, 20, 30…).

      Choosing an Enumerations Underlying Type

      When we declare an enumeration, the compiler assigns integer values to all of the elements. But we can change that. We can provide a different type right after the name of an enumeration:
      public enum DaysInWeek: short
      {
          Monday,
          Tuesday,
          Wednesday,
          Thursday,
          Friday,
          Saturday,
          Sunday
      }
      
      By doing this, we save our memory because the int type is taking more memory than the short, and we don’t need for our example, greater capacity of the short data type.

      Conclusion

      In this article, we have learned:
      • What is enumeration and how to create one
      • How to work with literal values in enumerations
      In the next article, we are going to talk about Inheritance in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4620 0 0 0 425 https://www.alvinashcraft.com/2018/10/08/dew-drop-october-8-2018-2818/ 0 0
      C# Intermediate - Inheritance in C# https://code-maze.com/csharp-inheritance/ Wed, 10 Oct 2018 08:53:41 +0000 https://code-maze.com/?p=4632
    • Classes and Constructors
    • Properties
    • Static Members, Constants, and Extension Methods
    • Anonymous and Nullable Types
    • Structures
    • Enumerations
    • Inheritance (Current article)
    • Interfaces
    • Abstract Classes
    • Generics
    • Queue, Stack, Hashtable
    • Generic List and Dictionary
    • Delegates
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Inheritance in C# Source Code.  We are going to split this article into the following sections:

      Using Inheritance

      We can define inheritance between two classes by using the following syntax:
      class DerivedClass: BaseClass
      {
             
      }
      
      class DerivedSubClass: DerivedClass
      {
      
      }
      
      What this means is that DerivedSubClass inherits from the DerivedClass and from the BaseClass as well, because DerivedClass inherits from the BaseClass. That way, we can share the class features between multiple classes, even though the one class can inherit only from one base class. So, let’s create some basic inheritance structure:
      public class Writer
      {
          public void Write()
          {
              Console.WriteLine("Writing to a file");
          }
      }
      public class XMLWriter: Writer
      {
          public void FormatXMLFile()
          {
              Console.WriteLine("Formating XML file");
          }
      }
      public class JSONWriter: Writer
      {
          public void FormatJSONFile()
          {
              Console.WriteLine("Formating JSON file");
          }
      }
      
      In this example, the XMLWriter and JSONWriter classes have they own methods but both of them share the Write() method from the base Writer class. So, if we create an object of type XMLWriter, we will be able to access its own method and the method from the base class:
      class Program
      {
          static void Main(string[] args)
          {
              XMLWriter xmlWriter = new XMLWriter();
              xmlWriter.FormatXMLFile();
              xmlWriter.Write();
          }
      }
      
      It goes the same for the JSONWriter class.

      Calling Constructors from the Base Class

      From the derived classes, we can access the constructor of a base class. This is used quite common, due to initialization of some properties that are shared between derived classes. We can use the base keyword to execute that:
      public class Writer
      {
          public string FileName { get; set; }
      
          public Writer(string fileName)
          {
              FileName = fileName;
          }
      
          public void Write()
          {
              Console.WriteLine("Writing to a file");
          }
      }
      public class XMLWriter: Writer
      {
          public XMLWriter(string fileName)
              :base(fileName)
          {
          }
      
          public void FormatXMLFile()
          {
              Console.WriteLine("Formating XML file");
          }
      }
      
      public class JSONWriter: Writer
      {
          public JSONWriter(string fileName)
              :base(fileName)
          {
          }
      
          public void FormatJSONFile()
          {
              Console.WriteLine("Formating JSON file");
          }
      }
      class Program
      {
          static void Main(string[] args)
          {
              XMLWriter xmlWriter = new XMLWriter("xmlFileName");
              xmlWriter.FormatXMLFile();
              xmlWriter.Write();
              Console.WriteLine(xmlWriter.FileName);
      
              JSONWriter jsonWriter = new JSONWriter("jsonFileName");
              jsonWriter.FormatJSONFile();
              jsonWriter.Write();
              Console.WriteLine(jsonWriter.FileName);
          }
      }
      
      As we can see, we pass a string value to the derived class's constructors and by using the basekeyword, we are passing that string value to the constructor of the base class. In there, we set up the value for the Name property.

      Accessing Classes

      The inheritance hierarchy means that our XMLWriter (or JSONWriter) class is a special type of the Writer, it has all the Writer’s non-private members, and additional features declared inside the XML(JSON)Writer class. But there are some limitations to this hierarchy. Let’s look at the following example:
      XMLWriter xml = new XMLWriter("file.xml");
      Writer writer = xml;
      writer.Write(); //ok Write is part of the Writer class
      writer.FormatXML(); //error FormatXML is not part of the Writer class
      
      This means if we refer to the XMLWriter or JSONWriter object with the Writer object, we can just access the methods declared inside the Writer class. There is one more limitation. We can’t assign a higher rank object to a lower rank object:
      Writer writer = new Writer("any name");
      XMLWriter xml = writer; //error
      
      But we can solve this problem by using the “as” keyword:
      XMLWriter xml = new XMLWriter("any name");
      Writer writer = xml; //writer points to xml
      
      XMLWriter newWriter = writer as XMLWriter; //this is ok now because writer was xml
      newWriter.FormatXMLFile();
      

      Declaring Methods with the New Keyword

      In the real world project, we often need to have so many different functionalities, and that usually leads to the existence of many different methods, properties etc. Sometimes it is pretty hard to come up with the unique and meaningful name for our identifiers, especially if we have the inheritance hierarchy. Sooner or later we are going to try to reuse a name that is already in use by one of the classes in the higher hierarchy level. If it comes to that (we have two methods with the same name in derived and base class) we are going to receive a warning: Hiding implementation - C# Inheritance

      Using the New Keyword

      A method in a derived class hides a method in a base class with the same signature. So, as you can see in the picture above, our method SetName exists in the XMLWriter class and Writer class. Since the XMLWriter class inherits from the Writer class it hides an implementation of the SetName method from the Writer class. Although our code will compile and run, we should take this warning seriously. It can happen that another class inherits from the XMLWriter class and implements the SetName method. The developer may expect to execute the SetName method from the Writer class (because XMLWriter inherits from the Writer) but this is not a case. The SetName method from the Writer class is hidden by the SetName method from the XMLWriter class. If we find ourselves in this kind of situation the best way is to change the method signatures. But if we are sure that we want a behavior like this, we can use the new keyword. The new keyword will simply tell the compiler that we are hundred percent sure in what we are doing and that we don’t want a warning message to appear anymore:
      public class Writer
      {
          public string FileName { get; set; }
      
          public Writer(string fileName)
          {
              FileName = fileName;
          }
      
          public void Write()
          {
              Console.WriteLine("Writing to a file");
          }
      
          public void SetName()
          {
              Console.WriteLine("Setting name in the base Writer class");
          }
      }
       
      public class XMLWriter: Writer
      {
          public XMLWriter(string fileName)
              :base(fileName)
          {
          }
      
          public void FormatXMLFile()
          {
              Console.WriteLine("Formating XML file");
          }
      
          public new void SetName()
          {
              Console.WriteLine("Setting name in the XMLWriter class");
          }
      }
      
      Now we don’t have a warning message anymore.

      Declaring Methods with the Virtual Keyword

      Sometimes, we don’t want to hide an implementation of a method from a base class with the same signature as a method from a derived class. What we want is to provide an opportunity for a different implementation of a method with the same signature in a derived class. So, we want to override our method from a base class with the method inside a derived class. A method that is intended to be overridden is called a virtual method. When we talk about overriding and hiding, we need to be clear with those terms. The hide means that we want completely to hide the implementation of a method from the base class, but the override means that we want a different implementation of a method from a base class. To create a virtual method we use the virtual keyword:
      public class Writer
      {
          public string FileName { get; set; }
      
          public Writer(string fileName)
          {
              FileName = fileName;
          }
      
          public void Write()
          {
              Console.WriteLine("Writing to a file");
          }
      
          public void SetName()
          {
              Console.WriteLine("Setting name in the base Writer class");
          }
      
          public virtual void CalculateFileSize()
          {
              Console.WriteLine("Calculating file size in a Writer class");
          }
      }
      

      Declaring Methods with the Override Keyword

      If we declare a method as a virtual in our base class, we can create a method in a derived class with the keyword override to declare another implementation of that method:
      public class XMLWriter: Writer
      {
          public XMLWriter(string fileName)
              :base(fileName)
          {
          }
      
          public void FormatXMLFile()
          {
              Console.WriteLine("Formating XML file");
          }
      
          public new void SetName()
          {
              Console.WriteLine("Setting name in the XMLWriter class");
          }
      
          public override void CalculateFileSize()
          {
              Console.WriteLine("Calculating file size in the XMLWriter class");
          }
      }
      
      If we want, we can call an original implementation of that method in a derived class by using the base keyword:
      public class XMLWriter: Writer
      {
          ...
      
          public override void CalculateFileSize()
          {
              base.CalculateFileSize();
              Console.WriteLine("Calculating file size in the XMLWriter class");
          }
      }
      
      All these inheritance actions and different method implementations with the mentioned keywords has its own unique name polymorphism.

      Rules to Follow While Working With Polymorphic Methods

      There are some important rules which we need to follow when declaring polymorphic methods by using the virtual and override keywords:
      • We can’t declare a virtual method as private. Its purpose is to be exposed to a derived class, so making it private is meaningless. Similarly, overridden methods can’t be private because a derived class can’t change the protection level of a method that it inherits
      • The signatures of virtual and overridden methods must be identical
      • We can override only a virtual method. If we try to override a method that has no virtual keyword, we will get an error
      • If we don’t use the override keyword we are not overriding the method we are just hiding it. If this is the behavior we want, we should use the new keyword
      • An overridden method is a virtual one as well, so it can be overridden in a further derived class

      Conclusion

      In this article, we have learned:
      • What is inheritance and how to use it
      • How to use the new, virtual and override keywords
      • Rules of polymorphism in the C# language
      In the next article, we are going to talk about Interfaces in C#Interfaces in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4632 0 0 0 433 https://www.alvinashcraft.com/2018/10/10/dew-drop-october-10-2018-2820/ 0 0 434 0 0 435 https://www.alvinashcraft.com/2018/10/11/dew-drop-october-11-2018-2821/ 0 0 580 https://www.productivecsharp.com/2019/01/best-csharp-dotnet-2018/ 0 0
      C# Intermediate - Interfaces in C# https://code-maze.com/csharp-interfaces/ Fri, 12 Oct 2018 08:00:39 +0000 https://code-maze.com/?p=4648
    • Classes and Constructors
    • Properties
    • Static Members, Constants, and Extension Methods
    • Anonymous and Nullable Types
    • Structures
    • Enumerations
    • Inheritance
    • Interfaces (Current article)
    • Abstract Classes
    • Generics
    • Queue, Stack, Hashtable
    • Generic List and Dictionary
    • Delegates
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Interfaces in C# Source Code.  We are going to split this article into the following sections:

      Defining an Interface

      To define an interface we need to use the interface keyword. It is quite similar to defining a class just we use another keyword. Inside that interface, we specify our members without access modifier and implementation. So, we just provide a declaration for members, an implementation is a job for a class which implements that interface:
      interface InterfaceName
      {
          returnType methodName(paramType paramName...);
      }
      

      Implementing an Interface

      To implement an interface, we declare a class or structure that inherits from the interface and implements all the members from it:
      class ClassName: InterfaceName
      {
          //members implementation
      }
      
      Let’s see all of this through the example:
      public interface IWriter
      {
          void WriteFile();
      }
      
      public class XmlWritter: IWriter
      {
          public void WriteFile()
          {
              Console.WriteLine("Writing file in the XmlWriter class.");
          }
      }
      
      public class JsonWriter: IWriter
      {
          public void WriteFile()
          {
              Console.WriteLine("Writing file in the JsonWritter class.");
          }
      }
      
      As we can see, after our classes inherit from an interface, they must implement the member WriteFile(). Otherwise, we would get a compiler error. When we implement an interface, we must ensure to provide method implementation by following these rules:
      • The method names and return types must match exactly
      • Any parameters must match exactly
      • All the methods must be public during implementation. This is only not the case with the explicit interface implementation(we will talk about that a little later)
      A class can inherit from a class and implement an interface at the same time. But if this is a case, we must specify a base class first and then an interface comma separated:
      public interface IWriter
      {
          void WriteFile();
      }
      
      public class FileBase
      {
          public virtual void SetName()
          {
              Console.WriteLine("Setting name in the base Writer class.");
          }
      }
      
      public class XmlWritter: FileBase, IWriter
      {
          public void WriteFile()
          {
              Console.WriteLine("Writing file in the XmlWriter class.");
          }
      
          public override void SetName()
          {
              Console.WriteLine("Setting name in the XmlWriter class.");
          }
      }
      
      public class JsonWriter: FileBase, IWriter
      {
          public void WriteFile()
          {
              Console.WriteLine("Writing file in the JsonWritter class.");
          }
      
          public override void SetName()
          {
              Console.WriteLine("Setting name in the JsonWriter class.");
          }
      }
      

      Referencing Classes Through Interfaces

      In the same way that we can reference an object by using a class variable, we can define an object by using an interface variable:
      XmlWriter writer = new XmlWriter();
      writer.SetName(); //overridden method from a base class
      writer.WriteFile(); //method from an interface
      
      As we can see, all the methods are available through the writer object. But let’s now use an interface object for referencing action:
      IWriter writer = new XmlWriter();
      writer.WriteFile(); //method from an interface
      writer.SetName(); //error the SetName method is not part of the IWriter interface
      
      If we use an interface to create an object, we can access only those members declared in that interface. As we mentioned above, the interface provides a contract for the class that inherits from it. And this is a great advantage of using interfaces, we can always be sure when a class inherits from our interface it will implement all of its members. But the interface implementation has even more advantages. One of them is object decoupling.

      Using an Interface to Decouple Classes

      When one class depends on another class those classes are coupled. This is something we want to avoid because if something changes in class A and Class B depends heavily on Class A, there is a great possibility that we would have to change a Class B as well. Or at least, we won’t be sure if Class B still works properly. Consequently, we want our classes to be loosely coupled or “decoupled”. Let’s see what would happen if we create our classes as strongly coupled:
      public class XmlFileWriter
      {
          private XmlWriter _xmlWriter;
      
          public XmlFileWriter(XmlWriter xmlWriter)
          {
              _xmlWriter = xmlWriter;
          }
      
          public void Write()
          {
              _xmlWriter.WriteFile();
          }
      }
      
      This XmlFileWriter is a class which has a purpose of writing to an xml file. Now we can instantiate our XmlWriter class, send the object through the XmlFileWriter constructor and call the Write method:  
      class Program
      {
          static void Main(string[] args)
          {
              XmlWriter xmlWriter = new XmlWriter();
              XmlFileWriter fileWriter = new XmlFileWriter(xmlWriter);
              fileWriter.Write();
          }
      }
      
      Ok, everything works great for now. But we have a couple of problems here. Our XmlFileWriter class is strongly coupled to the XmlWriter class. If we change the WriteFile method inside the XmlWriter class, we must change it in the XmlFileWriter class as well. So, the change in one class leads to change in another. That’s not how we want our code to work. Another thing. We surely want to have the same behavior for our JsonWriter class. We can’t use this XmlFileWriter (because it accepts only the XmlWriter object), we must create another class and repeat all of our actions. This is pretty bad as well. Finally, we can ask ourselves, if we really need two classes for the same job. Why can’t we use just one? Well, that’s where interfaces come in. Let’s modify the XmlFileWriter class:
      public class FileWriter
      {
          private readonly IWriter _writer;
      
          public FileWriter(IWriter writer)
          {
              _writer = writer;
          }
      
          public void Write()
          {
              _writer.WriteFile();
          }
      }
      
      Excellent. This is so much better. Now our class name tells us that this class doesn’t write only xml files. Furthermore, we are not restricting our constructor to accept just XmlWiter class, but all the classes that inherit from the IWriter interface. Our method WriteFile can’t be renamed now because of our interface IWritter, which states that all classes must implement a method with an identical name. We can see now that FileWriter class are decoupled from the XmlWriter or from the JsonWriter, and that we can send objects of both classes to the FileWriter class:
      class Program
      {
          static void Main(string[] args)
          {
              XmlWriter xmlWriter = new XmlWriter();
              JsonWriter jsonWriter = new JsonWriter();
      
              FileWriter fileWriter = new FileWriter(xmlWriter);
              fileWriter.Write();
      
              fileWriter = new FileWriter(jsonWriter);
              fileWriter.Write();
      
              Console.ReadKey();
          }
      }
      
      Decoupled objects - Interfaces in C# Isn’t this so much better? Now we have one class that does its job for any class that inherits from the IWriter interface. This feature is well known as a Dependency Injection.

      Working with Multiple Interfaces

      A class can inherit just from one base class, but it can implement multiple interfaces. The class must implement all the methods defined in those interfaces:
      public interface IFormatter
      {
          void FormatFile();
      }
      
      public class XmlWriter: FileBase, IWriter, IFormatter
      {
          public void WriteFile()
          {
              Console.WriteLine("Writing file in the XmlWriter class.");
          }
      
          public override void SetName()
          {
              Console.WriteLine("Setting name in the XmlWriter class.");
          }
      
          public void FormatFile()
          {
              Console.WriteLine("Formatting file in XmlWriter class.");
          }
      }
      

      Explicit Interface Implementation

      As we already said, a class can implement more than one interface. It’s not unusual that two of those interfaces have a method with the same name, but we still need to implement them in our class. To do that we do not implement a method as we did before, but we need to state the name of the interface first and then the name of a method with parameters:
      public interface Interface1
      {
          void MethodExample();
      }
      
      public interface Interface2
      {
          void MethodExample();
      }
      
      public class ExampleClass: Interface1, Interface2
      {
          void Interface1.MethodExample()
          {
              Console.WriteLine("");
          }
      
          void Interface2.MethodExample()
          {
              Console.WriteLine("");
          }
      
      }
      
      As we can see, we are not using an access modifier in the method implementation.

      Conclusion

      In this article, we have learned:
      • How to define and implement an interface
      • How to reference a class through the interface
      • The way to decouple our objects with interfaces and dependency injection
      • To explicitly implement our interfaces
      In the next article, we are going to talk about Abstract Classes in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4648 0 0 0 454 0 0 455 454 0
      C# Intermediate - Abstract Classes in C# https://code-maze.com/csharp-abstract-classes/ Wed, 17 Oct 2018 07:00:52 +0000 https://code-maze.com/?p=4696
    • Classes and Constructors
    • Properties
    • Static Members, Constants, and Extension Methods
    • Anonymous and Nullable Types
    • Structures
    • Enumerations
    • Inheritance
    • Interfaces
    • Abstract Classes (Current article)
    • Generics
    • Queue, Stack, Hashtable
    • Generic List and Dictionary
    • Delegates
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Abstract Classes in C# Source Code.  We are going to split this article into the following sections:

      Creating Abstract Classes

      To create an abstract class, we use the abstract keyword. The only purpose of the abstract class is to be inherited from and it cannot be instantiated: Abstract instance error - Abstract Classes in C# An abstract class can contain abstract methods. An abstract method doesn’t contain implementation just a definition with the abstract keyword:
      public abstract void Print(string text);
      To implement an abstract method in the class that derives from an abstract class, we need to use the override keyword:
      public override void Print()
      {
          //method implementation
      }
      As we could see from a previous picture, an abstract class doesn’t have to have any abstract member but the more important thing is if a class have at least one abstract member, that class must be an abstract class. Otherwise, the compiler will report an error: Abstract method error - Abstract Classes in C#

      Sealed Classes

      If we want to prevent our class to be inherited from, we need to use the sealed keyword. If anyone tries to use a sealed class as a base class, the compiler will throw an error: Sealed classes error - Abstract Classes in C#

      Conclusion

      In this article, we have learned:
      • How to create an abstract class
      • How to use abstract members and how to implement them
      • What a sealed class is and its purpose
      In the next article, we are going to talk about Generics in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4696 0 0 0 447 https://www.alvinashcraft.com/2018/10/17/dew-drop-october-17-2018-2825/ 0 0 1060 https://brandwebdirect.com/website-design-portfolio-samples/website-design-for-ecommerce-online-sales-web-development-service 0 0 1463 0 0 1464 abstract members of an abstract class. Otherwise, you would get an error. It is important to notice that if abstract class has a mixture of abstract and non-abstract members, the derived class must implement only abstract members. The other, non-abstract members, are inherited.]]> 1463 0 1465 1464 0
      C# Intermediate - Generics in C# https://code-maze.com/csharp-generics/ Fri, 19 Oct 2018 07:00:14 +0000 https://code-maze.com/?p=4710
    • Classes and Constructors
    • Properties
    • Static Members, Constants, and Extension Methods
    • Anonymous and Nullable Types
    • Structures
    • Enumerations
    • Inheritance
    • Interfaces
    • Abstract Classes
    • Generics (Current article)
    • Queue, Stack, Hashtable
    • Generic List and Dictionary
    • Delegates
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Generics in C# Source Code.  We are going to split this article into the following sections:

      Generic Type T

      To create a generic class, we need to provide a type between angle brackets:
      public class CollectionInitializer<T>
      {
          ...
      }
      
      The T in this example acts as a placeholder for a type we want to work with. We need to provide that type once we instantiate this generic class. So let’s see this with a simple example:
      public class CollectionInitializer<T>
      {
          private T[] collection;
      
          public CollectionInitializer(int collectionLength)
          {
              collection = new T[collectionLength];
          }
      
          public void AddElementsToCollection(params T[]elements)
          {
              for(int i=0; i<elements.Length; i++)
              {
                  collection[i] = elements[i];
              }
          }
      
          public T[] RetrieveAllElements()
          {
              return collection;
          }
      
          public T RetreiveElementOnIndex(int index)
          {
              return collection[index];
          }
      }
      
      And to use this generic class:
      class Program
      {
          static void Main(string[] args)
          {
              CollectionInitializer<int> initializer = new CollectionInitializer<int>(5);
      
              initializer.AddElementsToCollection(5, 8, 12, 74, 13);
              int[] collection = initializer.RetrieveAllElements();
              int number = initializer.RetreiveElementOnIndex(3);
      
              foreach (int element in collection)
              {
                  Console.WriteLine(element);
              }
      
              Console.WriteLine();
              Console.WriteLine($"Element on the selected index is: {number}");
      
              Console.ReadKey();
          }
      }
      
      As we can see in our CollectionInitializer class, we need to provide the type which we want to work with. Then, we can just call the methods implemented within our generic class. Of course, we didn’t implement safety checks (if we send more elements than the array length is etc) just for a sake of simplicity. Now we can see the result:   Generic example - Generics in C# A generic class can have more than one type parameter:
      public class CollectionKeyValueInitializer<TKey, TValue>

      Constraints with Generics

      Sometimes, we want to ensure that just certain types can be invoked with our generic class. It is often useful while working with classes or interfaces. We can do that by using the where keyword:
      public class CollectionInitializer<T> where T: Student
      or we can limit our generic class to work only with classes:
      public class CollectionInitializer<T> where T: class
      There are different variations for this constraints, they depend on the situation we are working in. It is important to know that if we constraint our generic class to work only with classes, we will get an error if we provide value type. If we want to work only with value types, we can constraint our generic class like this:
      public class CollectionInitializer<T> where T: struct

      Generic Methods

      In the same way that we can create a generic class, we can create a generic method. We just need to set a type parameter in angle brackets right behind a method name:
      public void ExampleMethod<T>(T param1, T param2)
      {
          //Methods body
      }
      
      We must pay attention to the type parameter identifier if our generic method exists inside a generic class. If that class has a type T then, our method needs to have a different type (U, Y, R…). Otherwise, the type T from a method will hide the type T from a class.

      Conclusion

      In this article, we have learned:
      • How to use Generics in C#
      • How to implement constraints in our generic classes
      • The way to create generic methods
      In the next article, we are going to talk about Queue, Stack, and Hashtable in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4710 0 0 0 549 0 0
      C# Intermediate - Queue, Stack, And Hashtable in C# https://code-maze.com/csharp-queue-stack-hashtable/ Wed, 24 Oct 2018 08:10:45 +0000 https://code-maze.com/?p=4733
    • Classes and Constructors
    • Properties
    • Static Members, Constants, and Extension Methods
    • Anonymous and Nullable Types
    • Structures
    • Enumerations
    • Inheritance
    • Interfaces
    • Abstract Classes
    • Generics
    • Queue, Stack, Hashtable (Current article)
    • Generic List and Dictionary
    • Delegates
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Collections in C# Source Code.  So, let's start. We are going to split this article into the following sections:

      Queue Collection

      The queue collection represents a first-in, first-out collection of objects. This means that we can place our objects in a queue collection in a certain order and remove those objects by the same order. So, the first object which goes in is the first object to go out. To create an object instance of a queue collection we can use two different statements. By using System.Collection.Generic namespace:
      Queue<int> intCollection = new Queue<int>();
      And by using System.Collection namespace:
      Queue queueCollection = new Queue();
      If we declare an object by providing a type (in our example an int), we can store only integer numbers inside. On the other hand, if we use the second example we can store different data types in a collection because it stores objects.

      The Most Common Methods and Properties

      The Enqueue method adds an element inside a collection:
      Queue queueCollection = new Queue();
      queueCollection.Enqueue(54);
      queueCollection.Enqueue("John");
      queueCollection.Enqueue(54.10);
      
      foreach (var item in queueCollection)
      {
            Console.WriteLine(item);
      }
      
      When we want to  remove an element at the beginning of the collection and return it, we are going to use the Dequeue method:
      Queue queueCollection1 = new Queue();
      queueCollection1.Enqueue(54);
      queueCollection1.Enqueue("John");
      queueCollection1.Enqueue(54.10);
      
      int number = Convert.ToInt32(queueCollection1.Dequeue());
      Console.WriteLine($"Removed element is: {number}");
      Console.WriteLine();
      
      foreach (var item in queueCollection1)
      {
          Console.WriteLine(item);
      }
      
      The Peek method returns the element at the beginning of the collection but does not remove it:
      Queue queueCollection2 = new Queue();
      queueCollection2.Enqueue(54);
      queueCollection2.Enqueue("John");
      queueCollection2.Enqueue(54.10);
                  
      int peekNumber = Convert.ToInt32(queueCollection2.Peek());
      Console.WriteLine($"Returned element is: {number}");
      Console.WriteLine();
      
      foreach (var item in queueCollection2)
      {
          Console.WriteLine(item);
      }
      
      The Clear method removes all the elements from a collection. If we want to check how many elements we have inside a collection, we can use the Count property:
      queueCollection2.Clear();
      Console.WriteLine(queueCollection2.Count);
      

      Stack Collection

      A stack collection represents a simple last-in, first-out collection. It means that an element which enters first in a collection will exit last. As with a Queue collection, we can use the System.Collection and System.Collection.Generic namespaces:
      Stack stack = new Stack();
      Stack<int> stackInt = new Stack<int>();
      

      Related Methods and Properties

      The Push method inserts an object at the top of the collection:
      Stack stack1 = new Stack();
      stack1.Push(328);
      stack1.Push("Fifty Five");
      stack1.Push(124.87);
      
      foreach (var item in stackCollection1)
      {
          Console.WriteLine(item);
      }
      
      Pop removes the element which was included last in a collection and returns it:
      Stack stackCollection2 = new Stack();
      stackCollection2.Push(328);
      stackCollection2.Push("Fifty Five");
      stackCollection2.Push(124.87);
      
      double number = Convert.ToDouble(stackCollection2.Pop());
      Console.WriteLine($"Element removed from a collection is: {number}");
      
      foreach (var item in stackCollection2)
      {
          Console.WriteLine(item);
      }
      
      Peek returns an object ready to exit the collection, but it doesn’t remove it:
      Stack stackCollection3 = new Stack();
      stackCollection3.Push(328);
      stackCollection3.Push("Fifty Five");
      stackCollection3.Push(124.87);
      
      double number1 = Convert.ToDouble(stackCollection3.Peek());
      Console.WriteLine($"Element returned from a collection is: {number}");
      
      foreach (var item in stackCollection3)
      {
          Console.WriteLine(item);
      }
      
      To remove all objects from a collection, we use the Clear method. If we want to count the number of elements, we use the Count property:
      stackCollection3.Clear();
      Console.WriteLine(stackCollection3.Count);
      

      Hashtable

      The Hashtable represents a collection of a key-value pair that is organized based on the hash code of the key. Differently, from the queue and stack collections, we can instantiate a hashtable object by using the only System.Collections namespace:
      Hashtable hashTable = new Hashtable();
      A Hashtable's constructor has a fifteen overloaded constructors.

      Common Methods In The Hashtable Collection

      The Add method adds an element with the specified key and value into the collection:
      Hashtable hashTable = new Hashtable();
      hashTable.Add(Element.First, 174);
      hashTable.Add(Element.Second, "Sixty");
      hashTable.Add(Element.Third, 124.24);
      foreach (var key in hashTable.Keys)
      {
          Console.WriteLine($"Key: {key}, value: {hashTable[key]}");
      }
      
      The Remove method removes the element with the specified key from a collection:
      Hashtable hashTable1 = new Hashtable();
      hashTable1.Add(Element.First, 174);
      hashTable1.Add(Element.Second, "Sixty");
      hashTable1.Add(Element.Third, 124.24);
      
      hashTable1.Remove(Element.Second);
      
      foreach (var key in hashTable1.Keys)
      {
          Console.WriteLine($"Key: {key}, value: {hashTable[key]}");
      }
      
      ContainsKey  determines whether a collection contains a specific key:
      if (hashTable.ContainsKey(Element.Second))
      {
            Console.WriteLine($"Collection contains key: {Element.Second} and its value is {hashTable[Element.Second]}");
      }
      
      The ContainsValue method determines whether a collection contains a specific value. Clear removes all elements from a collection:
      hashTable.Clear();

      Common Properties in the Hashtable Collection

      Count property counts the number of elements inside a collection:
      Console.WriteLine(hashTable.Count);
      Keys property returns all the keys from a collection and the Value property returns all the values from a collection:
      Hashtable hashTable2 = new Hashtable();
      hashTable2.Add(Element.First, 174);
      hashTable2.Add(Element.Second, "Sixty");
      hashTable2.Add(Element.Third, 124.24);
      
      var keys = hashTable2.Keys;
      foreach (var key in keys)
      {
           Console.WriteLine(key);
      }
      Console.WriteLine();
      
      var values = hashTable2.Values;
      foreach (var value in values)
      {
           Console.WriteLine(value);
      }
      

      Conclusion

      In this article, we have learned:
      • To use the Queue collection with its methods
      • To use the Stack collection with its methods
      • How to use Hashtable collection with its methods
      In the next article, we are going to talk about List and Dictionary in C#List and Dictionary in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4733 0 0 0 458 https://www.alvinashcraft.com/2018/10/24/dew-drop-october-24-2018-2830/ 0 0 459 http://www.MyHebrewBible.com/ 0 0 460 459 0 461 0 0 462 0 0 464 0 0 466 464 0 467 462 0 468 467 0 469 468 0
      C# Intermediate - Generic List and Dictionary in C# https://code-maze.com/csharp-generic-list-dictionary/ Fri, 26 Oct 2018 07:20:35 +0000 https://code-maze.com/?p=4771 List<T> and Dictionary are very useful collections in C#, and we are going to discover its features in the rest of the article. [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit the Generic List and Dictionary in C# Source Code.  We are going to split this article into the following sections:

      List<T>

      A List<T> represents a strongly typed collection of objects that can be accessed by index. To instantiate a List<T> we need to provide a type between the angle brackets:
      List<int> numberList = new List<int>();
      List<Student> students = new List<Student>();
      
      It has two more constructors that we can use to initialize a List object. With the first one, we can set initial capacity:
      List<int> numbers = new List<int>(2);
      With the second one, we can populate our list with the IEnumerable collection:
      int[] nums = new int[5] { 1, 2, 3, 4, 5 };
      List<int> numbers = new List<int>(nums);
      
      To access any element we can specify its index position:
      int number = numbers[1];

      Methods and Properties

      The Add method adds the element inside a list:
      List<int> numbers = new List<int>();
      numbers.Add(34);
      numbers.Add(58);
      numbers.Add(69);
      
      foreach (int number in numbers)
      {
          Console.WriteLine(number);
      }
      
      AddRange adds the elements of the specified collection to the end of a list:
      List<int> numbers = new List<int>();
      numbers.Add(34);
      numbers.Add(58);
      numbers.Add(69);
      
      int[] nums = new int[] { 1, 22, 44 };
      
      numbers.AddRange(nums);
      
      foreach (int number in numbers)
      {
           Console.WriteLine(number);
      }
      
      Contains determines whether an element exists in the list:
      if(numbers.Contains(34))
      {
           Console.WriteLine("The number 34 exists in a list");
      }
      
      The IndexOf method returns the position of an element as an integer number. If an element couldn’t be found, this method returns -1:
      int index;
      if((index = numbers.IndexOf(58)) != -1)
      {
          Console.WriteLine($"The number 58 is on the index: {index}");
      }
      
      LastIndexOf is similar to a previous method except it returns a last occurrence of the element. CopyTo method copies the entire collection to a compatible array, starting from the beginning of that array:
      int[] copyArray = new int[6];
      
      numbers.CopyTo(copyArray);
      
      foreach (int copyNumber in copyArray)
      {
           Console.WriteLine(copyNumber);
      }
      
      The Remove method removes the first occurrence of a specific element from the list:
      numbers.Remove(69);
      The Clear method clears all the elements from a list:
      numbers.Clear();
      We can check how many elements a list has by using the Count property:
      Console.WriteLine(numbers.Count);

      Dictionary

      Dictionary represents a collection of keys and values. To instantiate an object we can use the following syntax:
      Dictionary<KeyType, ValueType> Name = new Dictionary<KeyType, ValueType>();
      The KeyType represents a type for our key in a collection. The ValueType represents the value assigned to the key. So, we can extract our value from a collection by using the key inside the square brackets:
      DictionaryName[key];
      Dictionary has several constructors we can use to instantiate objects:
      Dictionary<string, int> dictExample = new Dictionary<string, int>();
      
      Dictionary<string, int> dictExample1 = new Dictionary<string, int>(5); //to set initial size
      
      Dictionary<string, int> dictExample2 = new Dictionary<string, int>(dictExample1); //accepts all the elements from created Key-Value collection
      

      Methods and Properties

      The Add method adds the key-value pair inside a collection:
      Dictionary<string, int> dictExample = new Dictionary<string, int>();
      
      dictExample.Add("First", 100);
      dictExample.Add("Second", 200);
      dictExample.Add("Third", 300);
      
      foreach (var item in dictExample)
      {
           Console.WriteLine(dictExample[item.Key]);
      }
      
      Remove removes the key-value pair from a collection based on the specified key:
      dictExample.Remove("Second");
      foreach (var item in dictExample)
      {
           Console.WriteLine(dictExample[item.Key]);
      }
      
      ContainsKey determines if a collection contains a specific key. ContainsValue determines if a collection contains a specific value:
      if(dictExample.ContainsKey("First"))
      {
           Console.WriteLine("It contains key");
      }
      
      if(dictExample.ContainsValue(300))
      {
            Console.WriteLine("It contains value");
      }
      
      The Clear method removes all key-value pairs from a collection:
      dictExample.Clear();
      If we want to count all of our elements inside a collection, we can use the Count property. If we want to get a collection of containing Keys or containing Values from a dictionary, we can use the Keys and Values properties:
      Console.WriteLine(dictExample.Count);
      
      foreach (var key in dictExample.Keys)
      {
           Console.WriteLine(key);
      }
      
      foreach (var value in dictExample.Values)
      {
           Console.WriteLine(value);
      }
      

      Conclusion

      In this article, we have learned:
      • To use the List<T> collection with its methods
      • To use a Dictionary with its methods and properties
      In the next article, we are going to talk about Delegates in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4771 0 0 0
      C# Intermediate - Delegates in C# https://code-maze.com/csharp-delegates/ Wed, 31 Oct 2018 07:00:34 +0000 https://code-maze.com/?p=4801
    • Classes and Constructors
    • Properties
    • Static Members, Constants, and Extension Methods
    • Anonymous and Nullable Types
    • Structures
    • Enumerations
    • Inheritance
    • Interfaces
    • Abstract Classes
    • Generics
    • Queue, Stack, Hashtable
    • Generic List and Dictionary
    • Delegates (Current article)
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial. To download the source code, you can visit Delegates in C# Source Code.  We are going to divide this article into the following sections:

      Delegate Syntax

      A base syntax to create a delegate object is:
      delegate Result_Type identifier([parameters]);
      There are three steps in defining and using delegates:
      • Declaration of our delegate
      • Instantiation, creating the delegate’s object
      • Invocation, where we call a referenced method
      //Declaration
      public delegate void WriterDelegate(string text);
      class Program
      {
          public static void Write(string text)
          {
              Console.WriteLine(text);
          }
      
          static void Main(string[] args)
          {
              //Instantiation
              WriterDelegate writerDelegate = new WriterDelegate(Write);
      
              //Invocation
              writerDelegate("Some example text.");
          }
      }
      
      It is important to understand that return type of a method and the number of parameters must match delegate's return type and the number of parameters. Otherwise, we will get a compiler error. We can see in our example that our Write method has a void as return type and only one string parameter as well as our delegate. Delegates are very useful in the encapsulation of our methods. C# has the two built-in delegates: Func<T> and Action<T>, there are widely used, so let’s talk more about them.

      Func<T> Delegate

      This delegate encapsulates a method that has up to sixteen parameters and returns a value of the specified type. So, in other words, we use the Func delegate only with a method that has a return type other than void. We can instantiate the Func delegate with this syntax:
      Func<Type1, Type2..., ReturnType> DelegateName = new Func<Type1, Type2..., ReturnType>(MethodName);
      We can see that the last parameter inside square brackets is a return type. Of course, we don’t have to initialize a delegate object like this, we can do it in another way:
      Func< Type1, Type2..., ReturnType> name = MethodName;
      Let’s see how to use Func delegate with an example:  
      class Program
      {
          public static int Sum(int a, int b)
          {
              return a + b;
          }
      
          static void Main(string[] args)
          {
              Func<int, int, int> sumDelegate = Sum;
              Console.WriteLine(sumDelegate(10, 20));
          }
      }
      

      Action<T> Delegate

      This delegate encapsulates a method that has up to sixteen parameters and doesn’t return any result. So we can assign to this delegate only methods with the void return type. We can instantiate the Action object with this syntax:
      Action<Type1, Type2...> DelegateName = new Action<Type1, Type2...>(MethodName);
      Or, we can use another way:
      Action < Type1, Type2...> DelegateName = MethodName;
      Let’s see how to use Action delegate with an example:
      public static void Write(string text)
      {
          Console.WriteLine(text);
      }
      
      static void Main(string[] args)
      {
          Action<string> writeDelegate = Write;
          writeDelegate("String parameter to write.");
      }
      

      Practical Example

      In this example, we are going to create an application which executes one of three methods (Sum, Subtract, Multiply) based on a single provided parameter. Basically, if we send Sum as a parameter, the Sum method will be executed and so on. First, we will write this example without delegates and then we will refactor that code by introducing delegates. So let’s start with the first part:
      public enum Operation
      {
          Sum,
          Subtract,
          Multiply
      }
      
      public class OperationManager
      {
          private int _first;
          private int _second;
          public OperationManager(int first, int second)
          {
              _first = first;
              _second = second;
          }
      
          private int Sum()
          {
              return _first + _second;
          }
      
          private int Subtract()
          {
              return _first - _second;
          }
      
          private int Multiply()
          {
              return _first * _second;
          }
      
          public int Execute(Operation operation)
          {
              switch (operation)
              {
                  case Operation.Sum:
                      return Sum();
                  case Operation.Subtract:
                      return Subtract();
                  case Operation.Multiply:
                      return Multiply();
                  default:
                      return -1; //just to simulate
              }
          }
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              var opManager = new OperationManager(20, 10);
              var result = opManager.Execute(Operation.Sum);
              Console.WriteLine($"The result of the operation is {result}");
      
              Console.ReadKey();
          }
      }
      
      If we start this application, we will get the correct response for any operation we send to the Execute method. But this code could be much better and easier to read without switch-case expression. If we are going to have more than ten operations (for example), this switch block would be very ugly to read and maintain as well. So, let’s change our code to make it readable, maintainable and more object-oriented. Let’s introduce a new class ExecutionManager:
      public class ExecutionManager
      {
          public Dictionary<Operation, Func<int>> FuncExecute { get; set; }
          private Func<int> _sum;
          private Func<int> _subtract;
          private Func<int> _multiply;
      
          public ExecutionManager()
          {
              FuncExecute = new Dictionary<Operation, Func<int>>(3);
          }
      
          public void PopulateFunctions(Func<int> Sum, Func<int> Subtract, Func<int> Multiply)
          {
              _sum = Sum;
              _subtract = Subtract;
              _multiply = Multiply;
          }
      
          public void PrepareExecution()
          {
              FuncExecute.Add(Operation.Sum, _sum);
              FuncExecute.Add(Operation.Subtract, _subtract);
              FuncExecute.Add(Operation.Multiply, _multiply);
          }
      }
      
      In here, we create a dictionary which will hold all the operations and all the references towards our methods (Func delegates). Now we can inject this class into the OperationManager class and change the Execute method:
      public class OperationManager
      {
          private int _first;
          private int _second;
          private readonly ExecutionManager _executionManager;
      
          public OperationManager(int first, int second, ExecutionManager executionManager)
          {
              _first = first;
              _second = second;
              _executionManager = executionManager;
              _executionManager.PopulateFunctions(Sum, Subtract, Multiply);
              _executionManager.PrepareExecution();
          }
      
          private int Sum()
          {
              return _first + _second;
          }
      
          private int Subtract()
          {
              return _first - _second;
          }
      
          private int Multiply()
          {
              return _first * _second;
          }
      
          public int Execute(Operation operation)
          {
              return _executionManager.FuncExecute.ContainsKey(operation) ?
                  _executionManager.FuncExecute[operation]() :
                  -1;
          }
      }
      
      Now, we are configuring all in the constructor of the OperationManager class and executing our action in the Execute method if it contains required operation. At the first look, we can see how much better this code is. Finally, we need to change the Program class:
      class Program
      {
          static void Main(string[] args)
          {
              var executionManager = new ExecutionManager();
              var opManager = new OperationManager(20, 10, executionManager);
              var result = opManager.Execute(Operation.Sum);
              Console.WriteLine($"The result of the operation is {result}");
      
              Console.ReadKey();
          }
      }
      

      Conclusion

      In this article, we have learned:
      • How to instantiate a delegate
      • The way to use Func and Action delegates
      • How to write a better code by using delegates
      If you have been with us along this entire intermediate series, you have a great knowledge about oop concepts in C#. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4801 0 0 0 478 0 0
      Consuming GitHub API (REST) With Flurl https://code-maze.com/consuming-github-api-rest-with-flurl/ Mon, 10 Dec 2018 06:00:58 +0000 https://code-maze.com/?p=4822 A Few Great Ways to Consume RESTful API in C# we introduced a few different ways to consume a Restful API. This article is about going into details of Flurl library and giving a few examples of how to authenticate and consume a restful API such as GitHub's. The source code for this article is located here: Flurl Examples. Be sure to fork it and follow along with this article to make the most out of it. In this article you'll learn: So, let's drill down into it.

      So, What is Flurl?

      Flurl stands for the Fluent URL. Quoting Flurl's home page: Flurl is a modern, fluent, asynchronous, testable, portable, buzzword-laden URL builder and HTTP client library for .NET.  It's simple as that. Flurl has been recommended by a few of our readers, and upon further investigation and usage, we've concluded that it is indeed library worth using or at least trying. Our experience with Flurl was positive, it's really easy to set up and use, and the documentation is digestible and on point. Under the polished hood of a modern library lies HttpClient. That means all methods are by default asynchronous, but with the added goodness of fluent execution. Flurl also has its own mechanisms for testing, so writing complicated mocks and test cases has become a non-issue. We'll get to that later on. Since Flurl is completely fluent and relies on extension methods, that means, you've guessed it, it's completely extensible. It has it's own configuration and error handling mechanisms, which makes it a complete and very intuitive library. So, that's about it in short terms, now let's get started, and see what Flurl can do for us.

      Setting up Flurl

      You can add Flurl to your project by typing Install-Package Flurl -Version 2.8.0 in your package manager console, or by using .NET CLI: dotnet add package Flurl --version 2.8.0. And you're good to go! Easy as that.

      What does Flurl Offer?

      Flurl consists of two main modules: URL builder and some utility methods as well as Flurl.HTTP which is the main part and we'll be using that to consume our APIs.

      URL Builder

      Flurl's URL API can help you easily construct your URLs. For example, you can do something like this to construct a complicated URL:
      using Flurl;
      
      var url = "http://www.some-api.com"
          .AppendPathSegment("endpoint")
          .SetQueryParams(new {
              api_key = ConfigurationManager.AppSettings["SomeApiKey"],
              max_results = 20,
              q = "Don't worry, I'll get encoded!"
          })
          .SetFragment("after-hash");
      Flurl offers great ways to parse, encode and combine URLs. There is a list of methods you can utilize here. So that's how you manipulate URLs with Flurl.

      HTTP

      Once that you have your URL neatly prepared, you can dive into Flurl's HTTP features. With Flurl, you can consume any REST endpoint, whether public or private. You can send GET, POST, PUT, PATCH or DELETE requests and set up headers, cookies and much more. All responses are awaitable and you can force a strongly-typed response or just return a result as a dynamic object if you prefer it that way. Flurl works well with strings, bytes, streams or any other media type, and you can both return HttpResponseMessage or the data from its body. We'll get to that in the examples section.

      Configuration

      Let's get on with the configuration. Now, the configuration of Flurl is available at different levels. While most people don't need to configure their applications and are pretty much satisfied with the default library behavior, there are different levels of configuration in Flurl that will let you mold it exactly how you like it. The stuff you can configure:
      • Client settings
      • Requests settings
      • Tests settings
      • The way HttpClient is handled
      • Serializers
      • Caching strategy
      As we mentioned, it's highly unlikely that you'll need to configure some of these settings, especially if you are not sure what they do. This goes beyond the scope of this article and it can be a whole topic to itself. But this goes to show just how powerful Flurl can be despite the simplistic view on the surface.

      Eventing

      Besides these settings, Flurl provides a way to decouple your business logic from Flurl itself through the Event mechanism. Events are available through the HttpCall class. There is an example in official docs that show how to implement a global error handling mechanism with events (very useful).
      private async Task HandleFlurlErrorAsync(HttpCall call) {
          await LogErrorAsync(call.Exception.Message);
          MessageBox.Show(call.Exception.Message);
          call.ExceptionHandled = true;
      }
      
      FlurlHttp.Configure(settings => settings.OnErrorAsync = HandleFlurlErrorAsync);
      Once you start changing different settings all over the place, you can revert them to default values using Settings.ResetDefaults() method.

      Extensibility

      Since Flurl is fluent by nature, which means it uses extension methods, we can add our own extensions as well if needed. This gives Flurl almost god-like flexibility and natural extensibility that can fit everyone's needs. You can extend both the Url builder and Flurl.Http parts of the Flurl library. Let's extend Url builder:
      public static Url MyExtensionMethod(this Url url) {
          // do something interesting with url
          return url;
      }
      Now we can do something like this:
      url = "https://api.github.com/"
          .AppendPathSegment("endpoint")
          .DoMyThing(); // uses Url extension
      Pretty slick. We can also extend the Flurl.Http part. Flurl chainable methods come in sets of 3 or 4, so we can do something like this:
      public static IFlurlRequest DoMyThing(this IFlurlRequest req) {
          // do something interesting with req.Settings, req.Headers, req.Url, etc.
          return req;
      }
      
      public static IFlurlRequest DoMyThing(this Url url) => new FlurlRequest(url).DoMyThing();
      
      public static IFlurlRequest DoMyThing(this string url) => new FlurlRequest(url).DoMyThing();
      And then, our method is available through the fluent interface as a string, Url or IFlurlRequest extension:
      result = await "http://api.com"
          .DoMyThing() // string extension
          .GetAsync();
      
      result = "http://api.com"
          .AppendPathSegment("endpoint")
          .DoMyThing() // Url extension
          .GetAsync();
      
      result = "http://api.com"
          .AppendPathSegment("endpoint")
          .WithBasicAuth(u, p)
          .DoMyThing() // IFlurlRequest extension
          .GetAsync();
      If you are unfamiliar with extension methods, check out our tutorial about them.

      Two Ways To Authenticate in Flurl

      Of course, many APIs hide confidential resources behind protected endpoints. To get to that endpoints you need to have certain rights (authorization), and you do that by authenticating yourself. Flurl offers two ways of authenticating: Basic Authentication and OAuth 2.0 bearer token. Here's how you do it:
      await url.WithBasicAuth("username", "password").GetJsonAsync();
      await url.WithOAuthBearerToken("mytoken").GetJsonAsync();
      A very easy and intuitive way to authenticate! Here is how it might look like in a real example:
      public class FlurlRequestHandler : IRequestHandler
      {
          private static string _githubToken;
      
          public FlurlRequestHandler()
          {
              _githubToken = Environment.GetEnvironmentVariable("GITHUB_TOKEN");
          }
      
          public Task<List<Repository>> GetRepositories()
          {
              var result = RequestConstants.BaseUrl
                  .AppendPathSegments("user", "repos")
                  .WithHeader(RequestConstants.UserAgent, RequestConstants.UserAgentValue)
                  .WithOAuthBearerToken(_githubToken)
                  .GetJsonAsync<List<Repository>>();
      
              return result;
          }
      }
      This example shows us how to authenticate using Bearer token and retrieve the list of GitHub repositories. The important thing to note here is that we store token in the environment variable. You should never, ever, store sensitive information in your code or configuration files. That's a serious security risk! Here is how authentication looks like using the Basic Authentication mechanism:
      public class FlurlRequestHandler : IRequestHandler
      {
          private static string _githubUsername;
          private static string _githubPassword;
      
          public FlurlRequestHandler()
          {
              _githubUsername = Environment.GetEnvironmentVariable("GITHUB_USERNAME");
              _githubPassword = Environment.GetEnvironmentVariable("GITHUB_PASS");
          }
      
          public Task<List<Repository>> GetRepositories()
          {
              var result = RequestConstants.BaseUrl
                  .AppendPathSegments("user", "repos")
                  .WithHeader(RequestConstants.UserAgent, RequestConstants.UserAgentValue)
                  .WithBasicAuth(_githubUsername, _githubPassword)
                  .GetJsonAsync<List<Repository>>();
      
              return result;
          }
      }
      You see how easily we changed the authentication mechanism? Very cool. The same warning applies to storing usernames and passwords too. Keep them safe in your environment variables!

      How to Write Get, Post, Put/Patch, and Delete Requests

      Now to the main event, writing requests in Flurl. This is where Flurl really shines. Fluent syntax, combined with the asynchronous execution and readability to match it really does the job. Writing requests is really easy and the learning curve is almost non-existent! Let's see what it looks like in our example (we will use GitHub bearer token, but you can use the other mechanism too):
      public class FlurlRequestHandler : IRequestHandler
      {
          private static string _githubToken;
      
          public FlurlRequestHandler()
          {
              _githubToken = Environment.GetEnvironmentVariable("GITHUB_TOKEN");
          }
      
          public async Task<List<Repository>> GetRepositories()
          {
              var result = await RequestConstants.BaseUrl
                  .AppendPathSegments("user", "repos")
                  .WithHeader(RequestConstants.UserAgent, RequestConstants.UserAgentValue)
                  .WithOAuthBearerToken(_githubToken)
                  .GetJsonAsync<List<Repository>>();
      
              return result;
          }
      
          public async Task<Repository> CreateRepository(string user, string repository)
          {
              var repo = new Repository
              {
                  Name = repository,
                  FullName = $"{user}/{repository}",
                  Description = "Generic description",
                  Private = false
              };
      
              var result = await RequestConstants.BaseUrl
                  .AppendPathSegments("user", "repos")
                  .WithHeader(RequestConstants.UserAgent, RequestConstants.UserAgentValue)
                  .WithOAuthBearerToken(_githubToken)
                  .PostJsonAsync(repo)
                  .ReceiveJson<Repository>();
      
              return result;
          }
      
          public async Task<Repository> EditRepository(string user, string repository)
          {
              var repo = new Repository
              {
                  Name = repository,
                  FullName = $"{user}/{repository}",
                  Description = "Modified repository",
                  Private = false
              };
      
              var result = await RequestConstants.BaseUrl
                  .AppendPathSegments("repos", user, repository)
                  .WithHeader(RequestConstants.UserAgent, RequestConstants.UserAgentValue)
                  .WithOAuthBearerToken(_githubToken)
                  .PatchJsonAsync(repo)
                  .ReceiveJson<Repository>();
      
              return result;
          }
      
          public async Task<HttpResponseMessage> DeleteRepository(string user, string repository)
          {
              var result = await RequestConstants.BaseUrl
                  .AppendPathSegments("repos", user, repository)
                  .WithHeader(RequestConstants.UserAgent, RequestConstants.UserAgentValue)
                  .WithOAuthBearerToken(_githubToken)
                  .DeleteAsync();
      
              return result;
          }
      }
      If you are unfamiliar with the GitHub tokens, there is a great resource about them. Essentially, you need to follow the link above and generate one here: Personal Access Tokens Alternatively, you can use basic authentication as we described in the authentication section.

      Exception Handling

      Flurl.Http throws FlurlHttpException for every non-200 response. You can easily deserialize Error response:
      catch (FlurlHttpException ex) {
          // For error responses that take a known shape
          TError e = ex.GetResponseJson<TError>();
      
          // For error responses that take an unknown shape
          dynamic d = ex.GetResponseJson();
      }
      Or even allow other status codes:
      url.AllowHttpStatus(HttpStatusCode.NotFound, HttpStatusCode.Conflict).GetAsync();
      url.AllowHttpStatus("400-404,6xx").GetAsync();
      url.AllowAnyHttpStatus().GetAsync();
      
      Let's see how a real-world example might look like:
      try
      {
          var newRepository = flurlRequestHandler.CreateRepository("CodeMazeBlog", "Test-Repository").Result;
      
          Console.ForegroundColor = color;
          Console.WriteLine("Repository created");
          Console.ResetColor();
      	
          Console.WriteLine($"Name: {newRepository.Name}");
          Console.WriteLine($"Full name: {newRepository.FullName}");
          Console.WriteLine($"Description: {newRepository.Description ?? "None"}");
          Console.WriteLine($"Url: {newRepository.Url}");
          Console.WriteLine($"Private: {newRepository.Private}");
      	
          Console.WriteLine();
      }
      catch (AggregateException ae)
      {
           // in case the repository already exists, handle FlurlHttpException
          ae.Handle(ex =>
          {
              if (ex is FlurlHttpException)
              {
                  Console.WriteLine(ex);
              }
              return ex is FlurlHttpException;
          });
      }
      In this scenario, we are trying to create a repository. If that repository already exists, we'll get a FlurlHttpException wrapped in Aggregate exception back from GitHub API. So we need to cover that scenario and write our exception so we can figure out what happened. And true enough, we get it: FlurlHttpException This is what we get as a result of trying to create the same repository twice. Sweet.

      Unit Testing in Flurl

      Flurl has some "dead simple" ways of testing its functionalities, right out-of-the-box. HttpTest class provides the mechanisms to test our API calls. Here are some of the examples of a few tests using MSTest framework:
      [TestClass]
      public class Tests
      {
          [TestMethod]
          public void GetRepositories_ShouldHaveBeenCalled_AtLeastOnce()
          {
              using (var httpTest = new HttpTest())
              {
                  var flurlRequestHandler = new FlurlRequestHandler();
                  var result = flurlRequestHandler.GetRepositories();
      
                  httpTest.ShouldHaveCalled(Url.Combine(RequestConstants.BaseUrl, "user", "repos"))
                      .WithVerb(HttpMethod.Get)
                      .Times(1);
              }
          }
      
          [TestMethod]
          public void CreateRepository_ShouldHaveBeenCalled_AtLeastOnce()
          {
              using (var httpTest = new HttpTest())
              {
                  var flurlRequestHandler = new FlurlRequestHandler();
                  var result = flurlRequestHandler.CreateRepository("CodeMazeBlog", "Test");
      
                  httpTest.ShouldHaveCalled(Url.Combine(RequestConstants.BaseUrl, "user", "repos"))
                      .WithVerb(HttpMethod.Post)
                      .Times(1);
              }
          }
      
          [TestMethod]
          public void EditRepository_ShouldHaveBeenCalled_AtLeastOnce()
          {
              using (var httpTest = new HttpTest())
              {
                  var flurlRequestHandler = new FlurlRequestHandler();
                  var result = flurlRequestHandler.EditRepository("CodeMazeBlog", "Test");
      
                  httpTest.ShouldHaveCalled(Url.Combine(RequestConstants.BaseUrl, "repos", "CodeMazeBlog", "Test"))
                      .WithVerb(new HttpMethod("PATCH"))
                      .Times(1);
              }
          }
      
          [TestMethod]
          public void DeleteRepository_ShouldHaveBeenCalled_AtLeastOnce()
          {
              using (var httpTest = new HttpTest())
              {
                  var flurlRequestHandler = new FlurlRequestHandler();
                  var result = flurlRequestHandler.DeleteRepository("CodeMazeBlog", "Test");
      
                  httpTest.ShouldHaveCalled(Url.Combine(RequestConstants.BaseUrl, "repos", "CodeMazeBlog", "Test"))
                      .WithVerb(HttpMethod.Delete)
                      .Times(1);
              }
          }
      }
      Despite what it looks like at first, these are all mock requests, and you won't hit any real endpoints. Very neat! You can write tests in any other popular testing framework for .NET as well (NUnit, Xunit...). The choice is all yours. If you prefer the notion of setting things up and tearing them down after the tests, you can do that too:
      private HttpTest _httpTest;
      
      [SetUp]
      public void CreateHttpTest() {
          _httpTest = new HttpTest();
      }
      
      [TearDown]
      public void DisposeHttpTest() {
          _httpTest.Dispose();
      }
      
      [Test]
      public void Test_Some_Http_Calling_Method() {
          // Flurl is in test mode
      }
      Arrange and assert phases are pretty flexible too. You can pretty much execute any scenario you can think of. As an exercise you can play around with HttpTest class more, to see what else it can do!

      Flurl Lifetime

      Since Flurl.Http uses HttpClient under the hood, it relies on its lifetime too. If you've ever worked with HttpClient, you are probably aware that it's recommended that you instantiate it once per application lifetime because it can easily exhaust the number of sockets available. Flurl (from the v2 up) handles that problem by caching the HttpClient for the same host. That means every succeeding request to the same host will use the same HttpClient instance. Note that you need to upgrade to version 2+ to have this functionality available (recommended). FlurlClient is a wrapper around HttpClient, so consequently, it's bound to its lifetime. You can have more control over HttpClient instances and their lifetimes you can do something like this:
      using (var cli = new FlurlClient("https://api.com").WithOAUthBearerToken(token))
      {
          await cli.Request("path", "to", "endpoioint").PostJsonAsync(thing);
          var stuff = await cli.Request("things").SetQueryParam("id", thing.Id).GetAsync();
      }

      Flurl and IoC

      Dependency Injection and IoC containers play a great role in modern application development. Flurl is well suited for dependency injection. Trying to register IFlurlClient as a singleton and services as transient, or any other combination can result in less than optimal results or bad practices. That's where IFlurlClientFactory comes in. As the official documentation states, the only reason it exists is to override Flurl's instance-per-host default behavior. So we can do something like this:
      public class MyService : IMyService
      {
          private readonly IFlurlClient _flurlClient;
      
          public MyService(IFlurlClientFactory flurlClientFac) {
              _flurlClient = flurlClientFac.Get(SERVICE_BASE_URL);
              // configure _flurlClient as needed
          }
      
          public Task<Thing> GetThingAsync(int id) {
              return _flurlClient.Request("things", id).GetAsync<Thing>();
          }
      }
      Now we can simply register IFlurlClientFactory as a singleton and get a single instance of FlurlClient for this service. Dependency Injection is a whole topic to itself, so we won't go any deeper into it in this article. This should be enough if you need to register Flurl through your IoC of choice.

      Conclusion

      This wraps it up for out Flurl library introduction. Flurl is a very a neat little library that gives us both simplicity and flexibility that we need to work comfortably with REST in our C# applications. It is simple enough to easily learn and use, and it is extensible enough to cover some pretty complex scenarios. So we hope this little demonstration will help you understand how Flurl works and maybe even persuade you to try it out. We will definitely use it in our newest projects that require REST communication. To learn about more useful ways to communicate with REST endpoints, you can check our article: A Few Great Ways to Consume RESTful API in C#. What you've learned in this post:
      • What Flurl is and what it can do for you
      • How to set it up, configure it, and extend it
      • How to build URLs and write requests with Flurl
      • A bit about Flurl lifetime (HttpClient)
      • How to unit test your Flurl methods
      [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      4822 0 0 0 524 https://www.alvinashcraft.com/2018/12/10/dew-drop-december-10-2018-2856/ 0 0
      Getting Started With Angular Material https://code-maze.com/get-started-angular-material/ Mon, 22 Oct 2018 07:01:56 +0000 https://code-maze.com/?p=4850
    • Getting started with Angular Material (Current article)
    • Navigation Menu - Sidebar, Main Navigation
    • Angular Material Table, Filter, Sort, Paging
    • Angular Material Progress Bar, Spinner, CheckBox, Card, Select, Expansion Panel
    • Material Inputs, DatePicker, Form Validation, Modals
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular Material series, check out: Introduction of the Angular Material series. The source code is available at GitHub Getting Started With Angular Material - Source Code We are going to divide this post into several sections:

      Project Creation

      We are going to use Angular CLI through the entire project (and we strongly advise you to do the same), thus creating our project is no exception to that. So, let’s open a command prompt window and create our Angular project:
      ng new ang-material-owneraccount
      Once the creation is done, we are going to start the Visual Studio Code editor and open our project.

      Angular Material Installation

      We are going to use npm to install the required packages. Besides installing Angular Material, we need to install CDK and Animations as well. So, let’s do that first by navigating to the root folder of our project and running the command:
      npm install --save @angular/material @angular/cdk @angular/animations
      After installation finishes, we should see this result: packages installed - getting started with angular material Now, we need to configure animations, by importing BrowserAnimationsModule into the app.module.ts file:
      import { BrowserModule } from '@angular/platform-browser';
      import { NgModule } from '@angular/core';
      import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
      
      import { AppComponent } from './app.component';
      
      @NgModule({
        declarations: [
          AppComponent
        ],
        imports: [
          BrowserModule,
          BrowserAnimationsModule
        ],
        providers: [],
        bootstrap: [AppComponent]
      })
      export class AppModule { }
      
      To continue, let’s include the prebuild theme for Angular Material. The theme is required and we can choose one of the available pre-built themes:
      • deeppurple-amber.css
      • indigo-pink.css
      • pink-blugrey.css
      • purple-green.css
      To include a theme, we need to open the styles.css file and include the following line:
      @import "~@angular/material/prebuilt-themes/indigo-pink.css";
      The next step is to install the hammerjs library for the gesture support. In order to have a full feature of some components, we need to install it:
      npm install --save hammerjs
      hammerjs - angular material installation After the installation, we are going to import it as a first line in the maint.ts file:
      import 'hammerjs';
      And the last step is to add Material Icons if we want to. This is an optional step, but since we are going to use those icons, we are going to add them as well in the index.html file:
      <!doctype html>
      <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>AngMaterialOwnerAccount</title>
        <base href="/">
      
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="icon" type="image/x-icon" href="favicon.ico">
        <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
      </head>
      <body>
        <app-root></app-root>
      </body>
      </html>
      

      Creating Material Module

      Even though we can import all the required components into the app.module.ts file, this is not recommended. A better solution is to create a separate module with all the required material imports, and then import that module into the app.module.ts file. That being said, let's do it:
      ng g module material --spec false
      This command will create a new folder material with the material.module.ts file inside. But this file is missing one thing and that’s the exports array. So, let’s add it:
      import { NgModule } from '@angular/core';
      import { CommonModule } from '@angular/common';
      
      @NgModule({
        imports: [
          CommonModule
        ],
        exports: [
          
        ],
        declarations: []
      })
      export class MaterialModule { }
      
      Finally, we need to import this MaterialModule int the app.module.ts file:
      import { MaterialModule } from './material/material.module';
      imports: [
          BrowserModule,
          BrowserAnimationsModule,
          MaterialModule
        ],
      
      That is it. We have prepared everything we need to use the Material components. Without further ado, we are going to start using them.

      Creating the Layout Component

      This component is going to be an entry point for our entire application, so let’s create it and import its selector inside the app.component.ts file:
      ng g component layout --spec false
      layout component - angular material installation This command will create our component files and import them into the app.module.ts file.

      Angular Flex Layout

      Before we modify the HTML component file, we need to install one more library: @angular/flex-layout. This library will help us create a responsive application. So, let’s install it:
      npm install @angular/flex-layout –-save
      angular flex - angular material installation And we need to register it inside the app.module.ts file:
      import {FlexLayoutModule} from '@angular/flex-layout';
      
      imports: [
          BrowserModule,
          BrowserAnimationsModule,
          MaterialModule,
          FlexLayoutModule
        ],
      
      Now, we can modify our layout.component.html file:
      <div fxLayout="row wrap" fxLayoutAlign="center center" class="layout-wrapper">
        <div fxFlex="80%" fxFlex.lt-md="100%" class="flex-wrapper">
            <ng-content></ng-content>
        </div>
      </div>
      
      As you can see, we use some angular/flex directives to create a responsive wrapper around our content. With the fxLayout element, we define the flow order of the child elements inside the container. The fxLayoutAlign will position children according to both the main-axis and the cross-axis. The fxFlex element resizes the child element to 80% of its parent, and if the screen goes below the medium than the child will take 100% of its parent. If you want to read more about flex-layout, you can find plenty of material here: Flex-Layout-Documentation. With the <ng-content> element, we are using angular content projection. We have two more classes: layout-wrapper and flex-wrapper, so let’s implement them inside the layout.component.css file:
      .layout-wrapper{
          height: 100%;
      }
      
      .flex-wrapper{
          height: 100%;
      }
      
      Excellent. All we have to do is to remove all the content from the app.component.html file and introduce this component by using its selector:
      <app-layout>
        Application works.
      </app-layout>
      
      We can start our application by typing ng serve and see that application actually works.

      Creating Home Component and Using Material Tabs

      Let’s create the Home component file structure first:
      ng g component home --spec false
      home component Now, let’s modify the home.component.html file:
      <section fxLayout="column" fxFlexAlign="stretch">
        <div fxFlexAlign="center">
          <p>Welcome to the Material Angular OwnerAccount Application</p>
        </div>
      
        <p>In this application we are going to work with:</p>
      </section>
      
      We need to modify the app.component.html file:
      <app-layout>
        <app-home></app-home>
      </app-layout>
      
      And we need to modify the home.component.css file as well:
      section div p{
          color: #3f51b5;
          font-size: 30px;
          text-shadow: 2px 3px 5px grey;
          margin: 30px 0;
      }
      
      section div + p{
          color: #3f51b5;
          font-weight: bold;
          font-size: 20px;
          padding-bottom: 20px;
      }
      
      To use our first material component, the mat-tab component, we need to register it inside the material.module.ts file:
      import { NgModule } from '@angular/core';
      import { CommonModule } from '@angular/common';
      import { MatTabsModule } from '@angular/material';
      
      @NgModule({
        imports: [
          CommonModule,
          MatTabsModule
        ],
        exports: [
          MatTabsModule
        ],
        declarations: []
      })
      export class MaterialModule { }
      
      And then to modify the home.component.html file:
      <section fxLayout="column" fxFlexAlign="stretch">
        <div fxFlexAlign="center">
          <p>Welcome to the Material Angular OwnerAccount Application</p>
        </div>
      
        <p>In this application we are going to work with:</p>
      
        <mat-tab-group>
          <mat-tab label="Material Components"> 
            <p>We are going to use different material components to create nice looking angular app.</p> 
          </mat-tab>
          <mat-tab label="Consume .NET Core Web API">
             <p>We will consume our .NET Core application. Basically, we will create complete CRUD client app.</p>
            </mat-tab>
          <mat-tab label="Fully responsive navigation menu">
             <p>By using material components, we are going to create fully responsive navigation menu, with its side-bar as well.</p>
            </mat-tab>
        </mat-tab-group>
      </section>
      
      Now, we can inspect our result: home component

      Additional Mat-Tab Features

      First of all, let’s style our tab content a little bit more, to center our text inside every tab:
      mat-tab-group {
          text-align: center;
      }
      
      mat-tab-group p {
          padding-top: 20px;
      }
      
      tab center content This control has its own events. The selectedTabChange event is emitted when the active tab changes. The focusChange event is emitted when the user navigates through tabs with keyboard navigation. So, let’s use the selectedTabChange event:
      <mat-tab-group (selectedTabChange)="executeSelectedChange($event)">
      And we need to modify the home.component.ts file:
      public executeSelectedChange = (event) => {
          console.log(event);
        }
      
      Right now, as soon as we switch our tabs, we will see the event object logged into the console window: log change event tab

      Conclusion

      Everything looks great. We have our first material component and in the following articles, we are going to work with other material components as well. In this article we have learned:
      • How to prepare the Angular project,
      • How to install Angular Material, CDK, and Animations
      • The way to install and use the @angular/flex-layout library
      • How to work with the Tab Material component
      In the next article, we are going to learn more about creating a navigation menu and menu side-bar with Angular routing. [sc name="subscribe" formNumber="47515" contentType="Angular Material"]]]>
      4850 0 0 0 456 0 0 457 456 0 824 0 0 825 824 0 1068 0 0 1259 ng add @angular/material simplifies installation. It installs all material components, browser animation module and icons. Installation process will ask you about hammer.js and preferred theme.]]> 0 0 1260 1259 0
      Angular Material Navigation Menu - Complete Responsive Navigation https://code-maze.com/angular-material-navigation/ Mon, 29 Oct 2018 07:00:58 +0000 https://code-maze.com/?p=4870 Angular Navigation And Routing. Because this series is all about angular material, this article won’t be an exception. We will focus on creating a navigation menu by using different material components. Once we are done, we will have a fully responsive and functional navigation menu with the routing logic to support the complete process. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular Material series, check out: Introduction of the Angular Material series. We strongly recommend reading our Angular Series prior to reading this article if you want to restore your knowledge about that topic or to learn Angular development overall. The source code is available at GitHub Angular Material Navigation Menu - Source Code We are going to divide this post into several sections:

      Creating Routes

      Let’s start with creating a new routing module:
      ng g module routing --spec false --module app
      routing module - Angular Material Navigation A next step is to modify the routing.module.ts file:
      import { NgModule } from '@angular/core';
      import { CommonModule } from '@angular/common';
      import { Routes, RouterModule } from '@angular/router';
      import { HomeComponent } from '../home/home.component';
      
      const routes: Routes = [
        { path: 'home', component: HomeComponent},
        { path: '', redirectTo: '/home', pathMatch: 'full' }
      
      ];
      
      @NgModule({
        imports: [
          CommonModule,
          RouterModule.forRoot(routes)
        ],
        exports: [
          RouterModule
        ],
        declarations: []
      })
      export class RoutingModule { }
      
      Finally, let’s modify the app.compnent.html file to complete the routing part for now:
      <app-layout>
        <main>
          <router-outlet></router-outlet>
        </main>
      </app-layout>
      
      We should be able to see our home component again, but this time it is served on the /home route. Angular Material provides different components which we can use to create nicely styled, responsive and effective navigation in our app. But we need to start with something, don't we? So, let’s start with the app.component.html file modification by using the mat-sidenav-container component:
      <app-layout>
        <mat-sidenav-container>
          <mat-sidenav #sidenav role="navigation">
            <!--this is a place for us to add side-nav code-->
          </mat-sidenav>
          <mat-sidenav-content>
            <!--in here all the content must reside. We will add a navigation header as well-->
            <main>
              <router-outlet></router-outlet>
            </main>
          </mat-sidenav-content>
        </mat-sidenav-container>
      </app-layout>
      
      We create a container for a side navigation bar and specify the part for our content. As you can see the <mat-sidenav> element defines a place for a side navigation and the <mat-sidenav-content>element defines a place for our content. We need to use the local reference #sidenav, and a little bit later, you will see why. Of course, this won’t work. We need to register the module in the material.module.ts file:
      import { MatTabsModule, MatSidenavModule } from '@angular/material';
      
      @NgModule({
        imports: [
          CommonModule,
          MatTabsModule,
          MatSidenavModule
        ],
        exports: [
          MatTabsModule,
          MatSidenavModule
        ],
      
      Now, we should have a working application again with some grayish background. Let’s style this a bit in the app.component.css file:
      mat-sidenav-container, mat-sidenav-content, mat-sidenav {
          height: 100%;
      }
      
      mat-sidenav {
          width: 250px;
      }
      
      main {
          padding: 10px;
      }
      
      And let’s modify the styles.css file:
      /* for sidenav to take a whole page */
      html, body { 
          margin: 0;
          height: 100%;
      }
      
      That is it. We have all prepared and it is time to start working on our navigation header component. To create a navigation header, we need to use the mat-toolbar element. But first thing first. This component has its own module, so we need to register that module inside the material.module.ts file:
      import { ..., MatToolbarModule } from '@angular/material';
      imports: [
          MatToolbarModule,
      
      exports: [
          MatToolbarModule,
      
      After that, we are going to create a new header component:
      ng g component navigation/header --spec false
      header component - Angular Material Navigation Now it is time to include this component inside the app.component.html file, right above the <main> tag:
      <mat-sidenav-content>
        <app-header></app-header>
        <main>
          <router-outlet></router-outlet>
        </main>
      </mat-sidenav-content>
      
      Then, let’s modify the header.component.html file:
      <mat-toolbar color="primary">
        <div fxHide.gt-xs>
            <button mat-icon-button (click)="onToggleSidenav()">
                <mat-icon>menu</mat-icon>
            </button>
        </div>
        <div>
            <a routerLink="/home">Owner-Account</a>
        </div>
        <div fxFlex fxLayout fxLayoutAlign="end" fxHide.xs>
            <ul fxLayout fxLayoutGap="15px" class="navigation-items">
                <li>
                    <a routerLink="/owner">Owner Actions</a>
                </li>
                <li>
                    <a routerLink="/account">Account Actions</a>
                </li>
            </ul>
        </div>
      </mat-toolbar>
      
      Basically, we create our navigation with the menu icon (we still need to register its own module), and the Owner-Account part that navigates to the home component. As you can see, we use the fxHide.gt-xs directive, which states that this part should be hidden only on the screen that is greater than extra small. We have another part of a navigation which is positioned on the end of the navbar and hidden only for the extra small screen. To continue, let’s register the MatIconModule and MatButtonModule inside the material module file:
      import { ... MatIconModule, MatButtonModule } from '@angular/material';
      imports: [
          MatButtonModule,
          MatIconModule,
      
      exports: [
          MatButtonModule,
          MatIconModule,
      
      Right now, our menu looks like this: nav menu started - Angular Material Navigation Looking beautiful right? :D :D Of course not, but we have the starting functionality in place and we are going to make it much nicer. To do that, let’s modify the header.component.css file:
      a {
          text-decoration: none;
          color: white;
      }
      
      a:hover, a:active{
          color: lightgray;
      }
      
      .navigation-items{
          list-style-type: none;
          padding: 0;
          margin: 0;
      }
      
      mat-toolbar{
          border-radius: 3px;
      }
      
      @media(max-width: 959px){
          mat-toolbar{
              border-radius: 0px;
          }
      }
      
      Now if we can have another look at our menu. It looks much nicer, isn’t it? header component styled - Angular Material Navigation If we take a look at the icon button code, we can see the onToggleSidenav() event. We need to implement it inside the header.component.ts file:
      import { Component, OnInit, Output, EventEmitter } from '@angular/core';
      
      @Component({
        selector: 'app-header',
        templateUrl: './header.component.html',
        styleUrls: ['./header.component.css']
      })
      export class HeaderComponent implements OnInit {
      
        @Output() public sidenavToggle = new EventEmitter();
      
        constructor() { }
      
        ngOnInit() {
        }
      
        public onToggleSidenav = () => {
          this.sidenavToggle.emit();
        }
      
      }
      
      If you want to learn more about @Output directives, you can read Angular Series Article About Decorators. Finally, we have to react to this event emitter inside our app.component.html file:
      <mat-sidenav-content>
        <app-header (sidenavToggle)="sidenav.toggle()"></app-header>
        <main>
          <router-outlet></router-outlet>
        </main>
      </mat-sidenav-content>
      
      Now it is obvious why we need the #sidenav local reference inside the mat-sidenav component. Our result should look like this: navigation completed - Angular Material Navigation Excellent. The time has come to implement the side navigation.

      Creating Side Navigation

      To create side navigation items, we are going to use the mat-nav-list element that resides inside MatListModule. So, let’s register this module first in the material.module.ts file:
      import { … MatListModule } from '@angular/material';
      imports: [
          MatListModule,
      
      exports: [
          MatListModule,
      
      Then let's create the sidenav-list component and modify the sidenav-list.component.html file:
      ng g component navigation/sidenav-list --spec false
      <mat-nav-list>
        <a mat-list-item routerLink="/home" (click)="onSidenavClose()">
            <mat-icon>home</mat-icon> <span class="nav-caption">Home</span>
        </a>
        <a mat-list-item routerLink="/owner" (click)="onSidenavClose()">
            <mat-icon>assignment_ind</mat-icon> <span class="nav-caption">Owner Actions</span>   
        </a>
        <a mat-list-item routerLink="#" (click)="onSidenavClose()">
            <mat-icon>account_balance</mat-icon><span class="nav-caption">Account Actions</span>
        </a>
      </mat-nav-list>
      
      As you can see, we use the mat-nav-list as a container with all the anchor tags containing the mat-list-item attributes. The click event is there for every link, to close the side-nav as soon as a user clicks on it. Finally, every link contains its own mat-icon. Let’s continue by adding some styles to the sidenav-list.component.css file:
      a {
          text-decoration: none;
          color: white;
      }
      
      a:hover, a:active{
          color: lightgray;
      }
      
      .nav-caption{
          display: inline-block;
          padding-left: 6px;
      }
      
      And finally let’s modify the sidenav-list.component.ts file:
      import { Component, OnInit, Output, EventEmitter } from '@angular/core';
      
      @Component({
        selector: 'app-sidenav-list',
        templateUrl: './sidenav-list.component.html',
        styleUrls: ['./sidenav-list.component.css']
      })
      export class SidenavListComponent implements OnInit {
        @Output() sidenavClose = new EventEmitter();
      
        constructor() { }
      
        ngOnInit() {
        }
      
        public onSidenavClose = () => {
          this.sidenavClose.emit();
        }
      
      }
      
      That’s it. We can now open the app.component.html file and modify it to add the side-nav component:
      <mat-sidenav #sidenav role="navigation">
        <app-sidenav-list (sidenavClose)="sidenav.close()"></app-sidenav-list>
      </mat-sidenav>
      
      In this code, we react on the event emitter from the sidenav-list component and close the side-nav by using the #sidenav local reference. Now, all we have to do is to take a look at our result: sidenav menu completed - Angular Material Navigation  

      Multi-Menu in Side-Nav

      There is one more thing we want to show you. For now, we only have a one clickable link per section, inside our sidenav. But what if we want to have a menu item and when we click that menu item other options appear? Well, we are going to show you how to do that as well. So, in the sidenav-list.component.html file, we need to add the following code below the last anchor tag:
      <mat-list-item [matMenuTriggerFor]="menu">
          <mat-icon>unfold_more</mat-icon>
          <a matline>Example</a>
      </mat-list-item>
      <mat-menu #menu="matMenu">
        <button mat-menu-item (click)="onSidenavClose()">View profile</button>
        <button mat-menu-item (click)="onSidenavClose()">Add contact</button>
      </mat-menu>
      
      For this to work, we need to register MatMenuModule:
      import { …, MatMenuModule } from '@angular/material';
      imports: [
          MatMenuModule,
      
      exports: [
          MatMenuModule,
      
      As a result, we should have a multi-menu option in our side navigation bar: multi menu  

      Conclusion

      Awesome. Now we have our own responsive navigation menu, built from scratch. In this article we have learned:
      • How to create the Angular Material Navigation
      • The way to create a side navigation material menu
      • How to implement multi-menu options in the side navigation
      In the next article, we are going to learn more about Material Table with Filter, Sort and Paging functionalities. [sc name="subscribe" formNumber="47515" contentType="Angular Material"]]]>
      4870 0 0 0 1161 1159 0 1162 1161 0 492 0 0 1066 0 0 1067 1066 0 1159 1158 0 1158 0 0 1069 1067 0 1070 1069 0 1071 1070 0 1110 0 0 1126 0 0 1127 1126 0 1412 1411 0 1413 1412 0 1252 0 0 1254 1252 0 1411 0 0 1261 1254 0 1262 1252 0 1263 1261 0 1264 1263 0 1284 0 0 1285 1110 0 1286 1284 0 1287 1286 0 1288 1287 0 1445 0 0 1557 0 0 1558 1557 0
      Angular Material Table, Filter, Sorting, Paging https://code-maze.com/angular-material-table/ Mon, 05 Nov 2018 07:00:35 +0000 https://code-maze.com/?p=4899 First part will consist of creating environment files, HTTP repository service and creating a new Owner module with the lazy loading feature. As you can see, everything is Angular specific, so we won’t dive too deep into this sections. We already have Angular Series in which we have talked about these topics in great detail. So if you are not familiar with these topics, we strongly recommend reading the mentioned series. In our source code, we can find the OwnerAccountServer folder which contains the entire .NET Core project, which we have created in .NET Core Series. In the same folder, we can find the _MySQL_Init_Script folder which contains a script to create a MySQL database with its tables. Just run that script in the MySQL database and you are ready to go. The second part will consist of creating a material table and populating that table with data from our server. Furthermore, we are going to create the filter, sorting and paging functionalities for that table. So, it’s time to start our job. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular Material series, check out: Introduction of the Angular Material series. The source code is available at GitHub Angular Material Table - Source Code We are going to divide this post into several sections:

      Environment, HTTP and Owner Module

      Let’s start with the environment files modification. We are going to modify the environment.prod.ts file first:
      export const environment = {
        production: true,
        urlAddress: 'http://www.ang-material-account-owner.com'
      };
      
      After that, let’s modify the environment.ts file:
      export const environment = {
        production: false,
        urlAddress: 'http://localhost:5000'
      };
      
      Having these environment files modified, it is time to create a service for sending the HTTP requests towards our server. To do that, we are going to create a service file first:
      ng g service shared/repository --spec false
      After creation, we have to modify that file:
      import { Injectable } from '@angular/core';
      import { HttpClient, HttpHeaders } from '@angular/common/http';
      import { environment } from './../../environments/environment';
      
      @Injectable({
        providedIn: 'root'
      })
      export class RepositoryService {
      
        constructor(private http: HttpClient) { }
      
        public getData = (route: string) => {
          return this.http.get(this.createCompleteRoute(route, environment.urlAddress));
        }
       
        public create = (route: string, body) => {
          return this.http.post(this.createCompleteRoute(route, environment.urlAddress), body, this.generateHeaders());
        }
       
        public update = (route: string, body) => {
          return this.http.put(this.createCompleteRoute(route, environment.urlAddress), body, this.generateHeaders());
        }
       
        public delete = (route: string) => {
          return this.http.delete(this.createCompleteRoute(route, environment.urlAddress));
        }
       
        private createCompleteRoute = (route: string, envAddress: string) => {
          return `${envAddress}/${route}`;
        }
       
        private generateHeaders = () => {
          return {
            headers: new HttpHeaders({'Content-Type': 'application/json'})
          }
        }
      }
      
      Excellent. We have prepared our service file. If you want to learn more about environment files, services, and HTTP, you can read that in the Angular Series Article which covers all of these topics. One more thing that we need to do is to register HttpClientModule in the app.module.ts file:
      import { HttpClientModule } from '@angular/common/http';
      imports: [
          …
          HttpClientModule
        ],
      

      Creating a New Owner Module

      Let’s create a new Owner module, and the routes for that module as well:
      ng g module owner --spec false
      We are going to register this module into the main routing module but in such a way to support the lazy loading feature:
      const routes: Routes = [
        { path: 'home', component: HomeComponent},
        { path: 'owner', loadChildren: "./../owner/owner.module#OwnerModule" }, 
        { path: '', redirectTo: '/home', pathMatch: 'full' }
      ];
      
      To read more about multiple modules and lazy loading in Angular, you can visit an article on the following link Lazy Loading in Angular. Right now, we have to create a new component to show the list of all the owners from the database:
      ng g component owner/owner-list --spec false
      owner list component - angular material table We need to have a routing for the components inside this module, so let’s create a new routing module for the Owner module components:
      ng g module owner/owner-routing --spec false --module owner
      owner routing module - angular material table And let’s modify that module file:
      import { NgModule } from '@angular/core';
      import { CommonModule } from '@angular/common';
      import { Routes, RouterModule } from '@angular/router';
      import { OwnerListComponent } from '../owner-list/owner-list.component';
      
      const routes: Routes = [
        { path: 'owners', component: OwnerListComponent }
      ];
      
      @NgModule({
        imports: [
          CommonModule,
          RouterModule.forChild(routes)
        ],
        exports: [
          RouterModule
        ],
        declarations: []
      })
      export class OwnerRoutingModule { }
      
      Finally, to make all this to work, we need to modify our routes in the sidenav-list.component.html file:
      <a mat-list-item routerLink="/owner/owners" (click)="onSidenavClose()">
         <mat-icon>assignment_ind</mat-icon> <span class="nav-caption">Owner Actions</span>
      </a>
      
      And the header.component.html file:
      <li>
          <a routerLink="/owner/owners">Owner Actions</a>
      </li>
      
      That is it. We can confirm now that our routing settings work as it supposed to: routing completed - angular material table   Excellent. Right now, we can dedicate our work to fetch some data from the database and show them in the material table component.

      Using Material Table to Display Data

      Because we have created another module in our Angular app, we need to import the Material module file inside the owner.module.ts file:
      import { MaterialModule } from './../material/material.module';
      imports: [
          …
          MaterialModule
        ],
      
      Once we create the Shared module, we will fix this code repetition (MaterialModule inside the App module and Owner module). For now, let’s continue by creating the _interface folder and inside it the owner.model.ts file:
      export interface Owner{
          id: string;
          name: string;
          dateOfBirth: Date;
          address: string;
      }
      
      Because we want to use the material table component, we need to register its own module in the material.module.ts file:
      import { …, MatTableModule } from '@angular/material';
      imports: [
          MatTableModule,
      
      exports: [
          MatTableModule,
      
      Then, let’s modify the owner-list.component file:
      <table mat-table [dataSource]="dataSource">
        <ng-container matColumnDef="name">
          <th mat-header-cell *matHeaderCellDef> Name </th>
          <td mat-cell *matCellDef="let element"> {{element.name}} </td>
        </ng-container>
      
        <ng-container matColumnDef="dateOfBirth">
          <th mat-header-cell *matHeaderCellDef> Date of Birth </th>
          <td mat-cell *matCellDef="let element"> {{element.dateOfBirth | date}} </td>
        </ng-container>
      
        <ng-container matColumnDef="address">
          <th mat-header-cell *matHeaderCellDef> Address </th>
          <td mat-cell *matCellDef="let element"> {{element.address}} </td>
        </ng-container>
      
        <ng-container matColumnDef="details">
          <th mat-header-cell *matHeaderCellDef> Details </th>
          <td mat-cell *matCellDef="let element">
            <button mat-icon-button color="primary" (click)="redirectToDetails(element.id)">
                <mat-icon class="mat-18">reorder</mat-icon>
            </button>
          </td>
        </ng-container>
      
        <ng-container matColumnDef="update">
            <th mat-header-cell *matHeaderCellDef> Update </th>
            <td mat-cell *matCellDef="let element">
              <button mat-icon-button color="accent" (click)="redirectToUpdate(element.id)">
                  <mat-icon class="mat-18">system_update</mat-icon>
              </button>
            </td>
          </ng-container>
      
          <ng-container matColumnDef="delete">
              <th mat-header-cell *matHeaderCellDef> Delete </th>
              <td mat-cell *matCellDef="let element">
                <button mat-icon-button color="warn" (click)="redirectToDelete(element.id)">
                    <mat-icon class="mat-18">delete</mat-icon>
                </button>
              </td>
            </ng-container>
      
        <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
        <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
      </table>
      
      The mat-table element transforms this table into a material one. With the dataSource attribute, we provide a data source for our table. Inside every ng-container tag, we define the column definition and the value to be displayed. It is very important to match the matColumnDef value with the property name of our Owner interface. Finally, in the last two tr tags, we define an order for our header columns and the row definitions. So, what we need to do right now is to create our datasource and displayedColumns properties in the ownerlist.component.ts file:
      import { RepositoryService } from './../../shared/repository.service';
      import { Component, OnInit } from '@angular/core';
      import { MatTableDataSource } from '@angular/material';
      import { Owner } from '../../_interface/owner.model';
      
      @Component({
        selector: 'app-owner-list',
        templateUrl: './owner-list.component.html',
        styleUrls: ['./owner-list.component.css']
      })
      export class OwnerListComponent implements OnInit {
      
        public displayedColumns = ['name', 'dateOfBirth', 'address', 'details', 'update', 'delete'
      ];
        public dataSource = new MatTableDataSource<Owner>();
      
        constructor(private repoService: RepositoryService) { }
      
        ngOnInit() {
          this.getAllOwners();
        }
      
        public getAllOwners = () => {
          this.repoService.getData('api/owner')
          .subscribe(res => {
            this.dataSource.data = res as Owner[];
          })
        }
      
        public redirectToDetails = (id: string) => {
          
        }
      
        public redirectToUpdate = (id: string) => {
          
        }
      
        public redirectToDelete = (id: string) => {
          
        }
      
      }
      
      If we change the order of elements inside the displayedColumns array, it will change the order of the columns inside our table. Right now, if we start our application and navigate to the Owner Actions menu, we are going to see a populated material table. But we are missing some styles, so let's add those in the owner-list.component.css file:
      table {
          width: 100%;
          overflow-x: auto;
          overflow-y: hidden;
          min-width: 500px;
      }
      
      th.mat-header-cell {
          text-align: left;
          max-width: 300px;
      }
      
      Now we should have a better-styled table: mat table - angular material table

      Sorting Data in Material Table

      We want to add the sorting functionality to our table, and for that purpose, we are going to use the matSort directive on the table tag. Moreover, we need to place the mat-sort-header directive for each header cell that will trigger sorting. So, let’s do that now. Modifying the table tag is going to be our first task:
      <table mat-table [dataSource]="dataSource" matSort>
      Then, we are going to add the mat-sort-header directive to the Name, DateOfBirth, and Address tags:
      <th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
      ...
      
      <th mat-header-cell *matHeaderCellDef mat-sort-header> Date of Birth </th>
      ...
      
      <th mat-header-cell *matHeaderCellDef mat-sort-header> Address </th>
      
      To make sorting functionality up and running, we need to modify the owner-list.component.ts file as well:
      export class OwnerListComponent implements OnInit, AfterViewInit {
      
        public displayedColumns = ['name', 'dateOfBirth', 'address', 'details', 'update', 'delete'];
        public dataSource = new MatTableDataSource<Owner>();
      
        @ViewChild(MatSort) sort: MatSort;
      
        constructor(private repoService: RepositoryService) { }
      
        ngOnInit() {
          this.getAllOwners();
        }
      
        ngAfterViewInit(): void {
          this.dataSource.sort = this.sort;
        }
        .
        .
        .
      
      Lastly, we need to add the MatSortModule inside of the material.module.ts file:
      import {..., MatSortModule } from '@angular/material';
      imports: [
          MatSortModule,
      
      exports: [
          MatSortModule,
      
      Now, we can check our result: Sorting mat table   By default, sorting starts with ascending order first and then descending. We can change that behavior by adding the matSortStart attribute to desc next to the matSort directive:
      <table mat-table [dataSource]="dataSource" matSort matSortStart="desc">
      If we don’t want to use MatTableDataSource for sorting, but to provide our own sorting logic, we can use the (matSortChange) event to receive the active sorting column and the sorting order as well:
      <table mat-table [dataSource]="dataSource" matSort (matSortChange)="customSort($event)">
      Once we click on the name column it will generate the following JSON object:
      {active: "name", direction: "asc"}
      
      1. active:"name"
      2. direction:"asc"
      3. __proto__:Object

      Filter Functionality in Material Table

      For this functionality, we need to provide our own input field and a custom function to filter our data. Only then, we can use MatTableDataSource’s filter property. To implement filtering, we are going to add the following code right above our table in the HTML file:
      <div fxLayout fxLayoutAlign="center center">
        <mat-form-field fxFlex="40%">
          <input matInput type="text" (keyup)="doFilter($event.target.value)" placeholder="Filter">
        </mat-form-field>
      </div>
      
      And then to write the following function in the component file:
      public doFilter = (value: string) => {
          this.dataSource.filter = value.trim().toLocaleLowerCase();
        }
      
      Finally, because we are using the matInput directive to transform regular input into the material input field, we need to register its modules inside the material.module.ts file:
      import { ..., MatFormFieldModule, MatInputModule } from '@angular/material';
      imports: [
          MatFormFieldModule,
          MatInputModule,
      
      exports: [
          MatFormFieldModule,
          MatInputModule,
      
      As we can see from the HTML file, we are using the fxLayout directive. But, because this component is part of a new Owner module, we need to import FlexLayoutModule into the Owner module file as well:
      import { FlexLayoutModule } from '@angular/flex-layout';
      imports: [
          ...
          FlexLayoutModule
        ],
      
      Of course, this code repetition will be solved as well as soon as we create a Shared module. Excellent. Now we can inspect the result: filter mat table - angular material table  

      Paging Functionality

      To implement paging with a material table, we need to use a <mat-paginator> bellow our table. So, let’s start implementation by adding MatPaginatorModule inside the Material module:
      import {..., MatPaginatorModule } from '@angular/material';
      imports: [
          MatPaginatorModule,
      
      exports: [
          MatPaginatorModule,
      
      Then, let’s add mat-paginator inside the HTML file:
      <mat-paginator [pageSize]="2" [pageSizeOptions]="[2, 4, 6, 10, 20]">
      </mat-paginator>
      
      And finally, let’s modify the owner-list.component.ts file:
      import { MatTableDataSource, MatSort, MatPaginator } from '@angular/material';
      
      ...
      
        @ViewChild(MatPaginator) paginator: MatPaginator;
        
        constructor(private repoService: RepositoryService) { }
      
        ngOnInit() {
          this.getAllOwners();
        }
      
        ngAfterViewInit(): void {
          this.dataSource.sort = this.sort;
          this.dataSource.paginator = this.paginator;
        }
        ...
      
      After these changes, we should have the following result: paging - angular material table If we want to write our custom pagination logic, we should use the (page) output event:
      <mat-paginator [pageSize]="2" [pageSizeOptions]="[2, 4, 6, 10, 20]" (page)="pageChanged($event)">
      </mat-paginator>
      
      For the custom pagination logic, you will have to write a pagination logic on the Web API part. We have a great article that explains How to Write Paging in ASP.NET Core Web API. So, feel free to read it and acquire a better knowledge of the server-side paging as well.

      Conclusion

      So, that’s it. Now we have our material table with all the features like sorting, paging and filtering data. In this article, we have learned:
      • How to use Environment files, HTTP client module, and Lazy Loading feature
      • To create a material table
      • To apply sorting, filtering, and pagination to the material table
      In the next article, we are going to create error pages by focusing on the material components and to create owner details component. [sc name="subscribe" formNumber="47515" contentType="Angular Material"]]]>
      4899 0 0 0 630 0 0 633 0 0 698 633 0 1057 owner-list.material.component should be owner-list.component.ts in the Using Material Table to Display Data section. Thanks for this fantastic article.]]> 0 0 1058 1057 0 1059 1058 0 1306 0 0 1307 1306 0 1308 1307 0 1309 1308 0 1310 1309 0 1313 ngAfterViewInit(): void { this.dataSource.sort = this.sort; this.dataSource.paginator = this.paginator; } Code is the same. Triple check. IS there any thing else need consider? Is the size(records) of table matter? I have like 35000 record in the table that I need to do pagination.]]> 1310 0 1314 1313 0 1315 1314 0 1316 1315 0 1902 0 0 1318 1316 0 1320 1318 0 1323 1320 0 1324 1323 0 1379 5) in a table? The app will mostly be used on a mobile device, but also on a big screen too. Which option is preferable, for example, many columns and a fixed column or several rows in each cell? In the mobile app, I did a few rows in each cell, it was convenient and looked good, but is it possible to use it in mat-table to keep the filter, sorting and pagination?]]> 0 0 1380 1379 0 1381 1380 0 1418 0 0 1419 1418 0 1432 0 0 1433 0 0 1434 1432 0 1450 0 0 1451 1450 0 1904 1902 0 1922 0 0 1924 1922 0 1926 1924 0 1927 1926 0 1928 1927 0 1929 Then you have to create that property in the .ts file and apply it a proper value: public length; . . . this.length = your number of items This will work for sure. I hope this helps, just confirm that to me. Best regards.]]> 1928 0
      Angular Material - Error and Details Pages With Material Components https://code-maze.com/angular-material-error-details-pages/ Mon, 12 Nov 2018 08:00:27 +0000 https://code-maze.com/?p=4928
    • Getting started with Angular Material
    • Navigation Menu - Sidebar, Main Navigation
    • Angular Material Table, Filter, Sort, Paging
    • Angular Material Progress Bar, Spinner, CheckBox, Card, Select, Expansion Panel (Current article)
    • Material Inputs, DatePicker, Form Validation, Modals
    • [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular Material series, check out: Introduction of the Angular Material series. The source code is available at GitHub Angular Material Error and Details Pages - Source Code We strongly recommend reading our Angular Series prior to reading this article, if you want to restore your knowledge about that topic or to learn Angular development overall. So, let’s start. We are going to divide this post into several sections:

      Not-Found Component and Material Progress Bar

      The first thing we need to do is to create a new not-found component:
      ng g component error-pages/not-found --spec false
      not found component creation - angular material progress bar After that, we are going to change the routes in the main routing module:
      import { NotFoundComponent } from '../error-pages/not-found/not-found.component';
      const ownerRoutes: Routes = [
        { path: 'home', component: HomeComponent},
        { path: 'owner', loadChildren: "./../owner/owner.module#OwnerModule" },
        { path: '404', component: NotFoundComponent}, 
        { path: '', redirectTo: '/home', pathMatch: 'full' },
        { path: '**', redirectTo: '/404', pathMatch: 'full'}
      ];
      
      Now, if we try to load some non-existing address, we will get the NotFound component instead, with the “not-found works”. Of course, we don’t want this message, so we are going to modify the not-found.component.html file:
      <section fxLayout="column wrap" fxLayoutGap="60px" fxLayoutAlign="center center">
        <div fxFlex>
          404 We are searching for your page...
        </div>
        <div fxFlex>
          <mat-progress-bar mode="indeterminate"></mat-progress-bar>
        </div>
        <div fxFlex>
          ... But we can not find it.
        </div>
      </section>
      
      As we can see, we are using the mat-progress-bar material component, and for that reason, we need to import the required module into the material.module.ts file:
      import {..., MatProgressBarModule } from '@angular/material';
      imports: [
          MatProgressBarModule,
      
      exports: [
          MatProgressBarModule,
      
      Finally, let’s add some styles to the not-found.component.css file:
      section div:nth-child(1), section div:nth-child(3){
          color: blue;
          font-size: 50px;
      }
      
      section div:nth-child(1){
          margin-top: 20px;
      }
      
      section div:nth-child(2){
          width: 50%;
      }
      
      That is it. We can inspect our result by typing a none-existing URL (localhost:4200/something): not-found - angular material progress bar   This looks good. Let’s continue with the Server-Error component.

      Server-Error Component With Spinner and CheckBox Material Components

      We are going to start with Server-Error component creation:
      ng g component error-pages/server-error --spec false
      Having that done, let’s modify the routing file:
      import { ServerErrorComponent } from '../error-pages/server-error/server-error.component';
      const ownerRoutes: Routes = [
        { path: 'home', component: HomeComponent },
        { path: 'owner', loadChildren: "./../owner/owner.module#OwnerModule" },
        { path: '404', component: NotFoundComponent },
        { path: '500', component: ServerErrorComponent },
        { path: '', redirectTo: '/home', pathMatch: 'full' },
        { path: '**', redirectTo: '/404', pathMatch: 'full' }
      ];
      
      For the visual experience, we need to modify the server-error.component.html file:
      <section fxLayout="column wrap" fxLayoutAlign="center center" fxLayoutGap="30px">
        <div fxFlex>
          <p>500 Server Error</p>
          <p>We are sorry for the inconvinience, plese report this error.</p>
        </div>
        <div fxFlex>
          <mat-checkbox (change)="checkChanged($event)" color="primary">I want to report this error.</mat-checkbox>
        </div>
        <div fxFlex *ngIf="reportedError">
            <mat-progress-spinner mode="determinate" [value]="errorPercentage"></mat-progress-spinner>
            <h1>{{errorPercentage}}%</h1>
        </div>
      </section>
      
      Because we are using the checkbox and progress-spinner components, we need to import their modules into the material.module.ts file:
      import {..., MatCheckboxModule, MatProgressSpinnerModule } from '@angular/material';
      imports: [
          MatProgressSpinnerModule,
          MatCheckboxModule,
      exports: [
          MatProgressSpinnerModule,
          MatCheckboxModule,
      Ok, we have imported all the necessary modules and now we are going to  modify the server-error.component.ts file:
      import { Component, OnInit } from '@angular/core';
      
      @Component({
        selector: 'app-server-error',
        templateUrl: './server-error.component.html',
        styleUrls: ['./server-error.component.css']
      })
      export class ServerErrorComponent implements OnInit {
        public reportedError: boolean;
        public errorPercentage: number = 0;
        public timer;
      
        constructor() { }
      
        ngOnInit() {
        }
      
        public checkChanged = (event) => {
          this.reportedError = event.checked;
      
          this.reportedError ? this.startTimer() : this.stopTimer();
        }
      
        private startTimer = () => {
          this.timer = setInterval(() => {
            this.errorPercentage += 1;
      
            if (this.errorPercentage === 100) {
              clearInterval(this.timer);
            }
          }, 30);
        }
      
        private stopTimer = () => {
          clearInterval(this.timer);
          this.errorPercentage = 0;
        }
      
      }
      
      And finally, let’s modify the server-error.component.css file:
      section div p:nth-child(1){
          font-size: 50px;
          text-align: center;
          color: #f44336;
      }
      
      section div p:nth-child(2){
          font-size: 20px;
          text-align: center;
          color: #3f51b5;
      }
      
      mat-checkbox {
          color: #3f51b5;
      }
      
      section div h1{
          text-align: center;
          color: #3f51b5;
          position: relative;
          top: -85px;
      }
      
      Our result should look like this: server-error finished - angular material progress bar

      Error Handling Service

      It is not enough just to have the error pages, we need to handle errors and to redirect the user to the required page. For that, we are going to create an error-handler service. We are just going to write our code, without any explanation because this is all angular specific. We have a great article .NET Core 2.0, Angular and Mysql. Error Handling in which we have explained everything related to the topic. So, let’s create a service and modify it:
      ng g service shared/error-handler --spec false
      import { Injectable } from '@angular/core';
      import { HttpErrorResponse } from '@angular/common/http';
      import { Router } from '@angular/router';
      
      @Injectable({
        providedIn: 'root'
      })
      export class ErrorHandlerService {
        public errorMessage: string = '';
      
        constructor(private router: Router) { }
      
        public handleError = (error: HttpErrorResponse) => {
          if(error.status === 500){
            this.handle500Error(error);
          }
          else if(error.status === 404){
            this.handle404Error(error)
          }
          else{
            this.handleOtherError(error);
          }
        }
       
        private handle500Error = (error: HttpErrorResponse) => {
          this.createErrorMessage(error);
          this.router.navigate(['/500']);
        }
       
        private handle404Error = (error: HttpErrorResponse) => {
          this.createErrorMessage(error);
          this.router.navigate(['/404']);
        }
       
        private handleOtherError = (error: HttpErrorResponse) => {
          this.createErrorMessage(error);
          //TODO: this will be fixed later;
        }
       
        private createErrorMessage(error: HttpErrorResponse){
          this.errorMessage = error.error ? error.error : error.statusText;
        }
      }
      
      For now, this service can be implemented only in the owner-list component, so let’s do that:
      import { ErrorHandlerService } from '../../shared/error-handler.service';
      ...
      constructor(private repoService: RepositoryService, private errorService: ErrorHandlerService) { }
      ...
      public getAllOwners = () => {
          this.repoService.getData('api/owner')
          .subscribe(res => {
            this.dataSource.data = res as Owner[];
          },
          (error) => {
            this.errorService.handleError(error);
          })
        }
      
      That is it. Now if our server returns the not found response we will redirect a user to the not found page. Same will happen for the internal server error, just another page.

      Details Page – Card, Select and Expansion Panel Components

      In this section, we are going to create details functionality for our application. To do that, let’s first create the owner-details component:
      ng g component owner/owner-details --spec false
      owner-details-creation Then, let’s configure the routes for this new component the owner-routing.module.ts file:
      import { OwnerDetailsComponent } from '../owner-details/owner-details.component';
      
      const routes: Routes = [
        { path: 'owners', component: OwnerListComponent },
        { path: 'details/:id', component: OwnerDetailsComponent}
      ];
      
      After that, let’s modify the owner-list.component.ts file:
      import { Router } from '@angular/router';
      constructor(private repoService: RepositoryService, private errorService: ErrorHandlerService, private router: Router) { }
      public redirectToDetails = (id: string) => {
          let url: string = `/owner/details/${id}`;
          this.router.navigate([url]);
        }
      
      Right now, we can navigate to our details page by clicking on the details button on the owner-list component. The next thing we are going to do is to add an additional interface and change our existing one:
      export interface Account{
          id: string;
          dateCreated: Date;
          accountType: string;
          ownerId?: string;
      }
      
      import { Account } from './account.model';
      export interface Owner{
          id: string;
          name: string;
          dateOfBirth: Date;
          address: string;
      
          accounts?: Account
      }
      
      After all of these changes, we need to modify the owner-details component, to show our details data on the page. So, let’s start with the owner-details.component.ts file:
      import { Component, OnInit } from '@angular/core';
      import { Owner } from './../../_interface/owner.model';
      import { Router, ActivatedRoute } from '@angular/router';
      import { RepositoryService } from './../../shared/repository.service';
      import { ErrorHandlerService } from './../../shared/error-handler.service';
      
      @Component({
        selector: 'app-owner-details',
        templateUrl: './owner-details.component.html',
        styleUrls: ['./owner-details.component.css']
      })
      export class OwnerDetailsComponent implements OnInit {
        public owner: Owner;
        public showAccounts;
      
        constructor(private repository: RepositoryService, private router: Router, 
          private activeRoute: ActivatedRoute, private errorHandler: ErrorHandlerService) { }
      
        ngOnInit() {
          this.getOwnerDetails();
        }
      
        private getOwnerDetails = () =>{
          let id: string = this.activeRoute.snapshot.params['id'];
          let apiUrl: string = `api/owner/${id}/account`;
       
          this.repository.getData(apiUrl)
          .subscribe(res => {
            this.owner = res as Owner;
          },
          (error) =>{
            this.errorHandler.handleError(error);
          })
        }
      }
      
      Excellent. We have prepared the logic to fetch the data from the server, so the obvious continuation is to show that data on the HTML page. Because we are going to have a lot of code for this component, we are going to create two additional components to spread our HTML code between them. That being said, let’s create those components:
      ng g component owner/owner-details/owner-data --spec false
      ng g component owner/owner-details/account-data --spec false
      

      Owner-Data Component

      We are going to modify the owner-data component first. The HTML part:
      <section fxLayout="row wrap" fxLayoutAlign="center center">
          <mat-card fxFlex="500px" fxFlex.xs="100%">
              <mat-card-title>Details for the clicked owner</mat-card-title>
              <mat-card-content>
                <div fxLayout="column wrap" fxLayoutGap="40px">
                  <div fxLayout="row wrap" fxFlex>
                    <div fxFlex><strong>Owner's name:</strong></div>
                    <div fxFlex>{{owner?.name}}</div>
                  </div>
            
                  <div fxLayout="row wrap" fxFlex>
                    <div fxFlex><strong>Date of birth:</strong></div>
                    <div fxFlex>{{owner?.dateOfBirth | date}}</div>
                  </div>
            
                  <div fxLayout="row wrap" fxFlex *ngIf='owner?.accounts.length <= 2; else advancedUser'>
                    <div fxFlex><strong>Type of user:</strong></div>
                    <div fxFlex class="beginner-color">Beginner user.</div>
                  </div>
                  <ng-template #advancedUser>
                    <div fxLayout="row wrap" fxFlex>
                      <div fxFlex><strong>Type of user:</strong></div>
                      <div fxFlex class="advanced-color">Advanced user</div>
                    </div>
                  </ng-template>
                </div>
              </mat-card-content>
              <mat-card-actions>
                <mat-form-field>
                  <mat-select placeholder="Show accounts" (selectionChange)="onChange($event)">
                    <mat-option *ngFor="let opt of selectOptions" [value]="opt.value">
                      {{opt.name}}
                    </mat-option>
                  </mat-select>
                </mat-form-field>
              </mat-card-actions>
            </mat-card>
      </section>
      Because we are using the mat-card component and the mat-select component, we need to import modules inside the material.module.ts file:
      import { …, MatCardModule, MatSelectModule } from '@angular/material';
      imports: [
          MatSelectModule,
          MatCardModule,
      
      exports: [
          MatSelectModule,
          MatCardModule,
      
      Next thing we need to do is to modify the owner-data.component.ts file:
      import { Owner } from './../../../_interface/owner.model';
      import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
      
      @Component({
        selector: 'app-owner-data',
        templateUrl: './owner-data.component.html',
        styleUrls: ['./owner-data.component.css']
      })
      export class OwnerDataComponent implements OnInit {
        @Input() public owner: Owner;
        public selectOptions = [{name:'Show', value: 'show'}, {name: `Don't Show`, value: ''}];
        @Output() selectEmitt = new EventEmitter();
      
        constructor() { }
      
        ngOnInit() {
        }
      
        public onChange = (event) => {
          this.selectEmitt.emit(event.value);
        }
      
      }
      And finally, let’s modify the CSS file:
      mat-card-content, mat-card-title, mat-card-actions {
          text-align: center;
      }
      
      mat-card-content{
          padding-top: 20px;
          padding-bottom: 20px;
      }
      
      .advanced-color{
          color: #3f51b5;
      }
      
      .beginner-color{
          color: #f44336;
      }
      
      In order to show our data, we need to include our components inside the owner-details component and check the current progress:
      <app-owner-data [owner]='owner' (selectEmitt)='showAccounts = $event'></app-owner-data>
      <app-account-data *ngIf='showAccounts'></app-account-data>
      
      owner details part 1

      Account-Data Component

      Let’s modify the HTML part of this component first:
      <section fxLayout="row wrap" fxLayoutAlign="center center">
          <mat-accordion fxFlex="500px" fxFlex.xs="100%">
              <mat-expansion-panel *ngFor="let account of accounts; let i = index">
                <mat-expansion-panel-header>
                  <mat-panel-title>
                    {{i+1}}. Account
                  </mat-panel-title>
                  <mat-panel-description>
                    Account Information
                  </mat-panel-description>
                </mat-expansion-panel-header>
            
                <div fxLayout="row wrap" fxLayoutAlign="center center">
                  <div fxFlex="35%" class="text-color"><strong>type:</strong> &nbsp; {{account?.accountType}}</div>
                  <div fxFlex class="text-color"><strong>created:</strong> &nbsp; {{account?.dateCreated | date}}</div>
                </div>
      
              </mat-expansion-panel>
            </mat-accordion>
      </section>
      
      Then, let’s import the module for the accordion:
      import { …, MatExpansionModule } from '@angular/material';
      imports: [
          MatExpansionModule,
      
      exports: [
          MatExpansionModule,
      
      After that, we need to modify the account-data.component.ts file:
      import { Component, OnInit, Input } from '@angular/core';
      
      @Component({
        selector: 'app-account-data',
        templateUrl: './account-data.component.html',
        styleUrls: ['./account-data.component.css']
      })
      export class AccountDataComponent implements OnInit {
        @Input() public accounts: Account[];
      
        constructor() { }
      
        ngOnInit() {
        }
      
      }
      
      And, to modify the CSS file:
      .text-color {
          color: #3f51b5;
      }
      
      mat-accordion{
          margin-top: 20px;
      }
      
      Excellent. All we have left to do is to modify our owner-details.component.html file:
      <app-owner-data [owner]='owner' (selectEmitt)='showAccounts = $event'></app-owner-data>
      <app-account-data *ngIf='showAccounts' [accounts]='owner?.accounts'></app-account-data>
      
      Our completed page should look like this: owner details completed

      Conclusion

      And there you go. We have created additional pages in our project and made them functional and prettier by using Angular Material Components. So, to summarize, we have learned:
      • How to use Material Progress Bar and Spinner components
      • The way to use CheckBox, Select and Card components
      • To show additional data by using the Expansion-Panel component
      In the next article, we are going to show you how to use Material Input components and to validate forms as well. [sc name="subscribe" formNumber="47515" contentType="Angular Material"]]]>
      4928 0 0 0 829 0 0 830 829 0 831 830 0 832 831 0 1350 0 0 1354 1350 0 1901 0 0 1900 0 0 1903 1900 0
      Angular Material Form Validation, Input, Datepicker and Modal https://code-maze.com/angular-material-form-validation/ Mon, 19 Nov 2018 07:13:04 +0000 https://code-maze.com/?p=4941 Angular Form Validation. [sc name="part_of_series" headline="This article is part of the series"] For the complete navigation and all the basic instructions of the Angular Material series, check out: Introduction of the Angular Material series. The source code is available at GitHub Angular Material Form Validation - Source Code We strongly recommend reading our Angular Series prior to reading this article, if you want to restore your knowledge about that topic or to learn Angular development overall. Let’s start step by step. We are going to divide this article into the following sections:

      Owner Component and  the Routing Configuration

      To create our new component, we have to type a familiar command:
      ng g component owner/owner-create --spec false
      owner creation - Angular Material Form Validation Once we have our component created, let’s configure the routing part in the owner-routing.module.ts file:
      import { OwnerCreateComponent } from '../owner-create/owner-create.component';
      const routes: Routes = [
        { path: 'owners', component: OwnerListComponent },
        { path: 'details/:id', component: OwnerDetailsComponent},
        { path: 'create', component: OwnerCreateComponent}
      ];
      
      Finally, let’s modify the owner-list.component.html file, to add the link that will point to the owner-create component:
      <div fxLayout fxLayout.lt-md="column wrap" fxLayoutAlign="center center" fxLayoutGap.gt-sm="250px" fxLayoutGap.lt.md="20px">
        <mat-form-field>
          <input matInput type="text" (keyup)="doFilter($event.target.value)" placeholder="Filter">
        </mat-form-field>
        <div>
          <a [routerLink]="['/owner/create']" mat-button color="primary">Create Owner</a>
        </div>
      </div>
      
      Now we can inspect our result: create added to owner component - Angular Material Form Validation And if we click on the Create Owner button, we are going to be directed to the owner-create component for sure.

      Adding Angular Material Form Validation and Input Elements

      Before we start adding input fields, we need to import one more module into the owner.module.ts file:
      import { ReactiveFormsModule } from '@angular/forms';
      imports: [
        …,
        ReactiveFormsModule
      ],
      
      We need this module for the reactive form validation. In addition to input components, we are going to use the datepicker material component and for that, we need the MatDatepickerModule and MatNativeDataModule inside the material.module.ts file:
      Import{ …, MatDatepickerModule, MatNativeDateModule } from '@angular/material';
      imports: [
          MatDatepickerModule, 
          MatNativeDateModule,
      
      ...
      
      exports: [
          MatDatepickerModule, 
          MatNativeDateModule,
      
      After all these imports and registrations, we can start with the owner-create.component.html file modification:
      <section fxLayout="row wrap" fxLayoutAlign="center center">
        <mat-card fxFlex="500px" fxFlex.xs="100%">
            <mat-card-title>Create a new owner</mat-card-title>
            <mat-card-content>
            </mat-card-content>
            <mat-card-actions>
              
            </mat-card-actions>
          </mat-card>
      </section>
      
      The mat-card-content and mat-card-actions elements need to be wrapped with the form tag:
      <form [formGroup]="ownerForm" autocomplete="off" novalidate (ngSubmit)="createOwner(ownerForm.value)" fxLayout="column wrap"
            fxLayoutAlign="center center" fxLayoutGap="10px">
        <mat-card-content>
        </mat-card-content>
        <mat-card-actions>
        </mat-card-actions>
      </form>
      
      Then inside the mat-card-content tag, we are going to add the following code:
      <mat-form-field>
        <input matInput type="text" placeholder="Owner's name" formControlName="name" id="name">
        <mat-hint align="end">Not more then 60 characters long.</mat-hint>
        <mat-error *ngIf="hasError('name', 'required')">Name is required</mat-error>
        <mat-error *ngIf="hasError('name', 'maxlength')">You have more than 60 characters</mat-error>
      </mat-form-field>
      
      <mat-form-field>
        <input matInput [matDatepicker]="picker" placeholder="Choose a date of birth" formControlName="dateOfBirth" id="dateOfBirth" 
            readonly (click)="picker.open()">
        <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
        <mat-datepicker #picker></mat-datepicker>
      </mat-form-field>
      
      <mat-form-field>
        <input matInput type="text" placeholder="Owner's address" formControlName="address">
        <mat-hint align="end">Not more then 100 characters long.</mat-hint>
        <mat-error *ngIf="hasError('address', 'required')">Address is required</mat-error>
        <mat-error *ngIf="hasError('address', 'maxlength')">You have more than 100 characters</mat-error>
      </mat-form-field>
      
      Finally, let’s modify the mat-card-actions element:
      <mat-card-actions align="center">
        <button mat-raised-button color="primary" [disabled]="!ownerForm.valid">Create</button>
        <button type="button"> mat-raised-button color="warn" (click)="onCancel()">Cancel</button>
      </mat-card-actions>
      
      We have completed the HTML part, and we are ready to modify the owner-create.component.ts file. But before we do that, we are going to create a new interface OwnerForCreation:
      export interface OwnerForCreation {
          name: string;
          dateOfBirth: Date;
          address: string;
      }
      
      Right after that, we are going to modify our .ts file:
      import { RepositoryService } from './../../shared/repository.service';
      import { Component, OnInit } from '@angular/core';
      import { FormControl, FormGroup, Validators } from '@angular/forms';
      import { Location } from '@angular/common';
      import { OwnerForCreation } from '../../_interface/ownerForCreation.model';
      
      @Component({
        selector: 'app-owner-create',
        templateUrl: './owner-create.component.html',
        styleUrls: ['./owner-create.component.css']
      })
      export class OwnerCreateComponent implements OnInit {
        public ownerForm: FormGroup;
      
        constructor(private location: Location, private repository: RepositoryService) { }
      
        ngOnInit() {
          this.ownerForm = new FormGroup({
            name: new FormControl('', [Validators.required, Validators.maxLength(60)]),
            dateOfBirth: new FormControl(new Date()),
            address: new FormControl('', [Validators.required, Validators.maxLength(100)])
          });
        }
      
        public hasError = (controlName: string, errorName: string) =>{
          return this.ownerForm.controls[controlName].hasError(errorName);
        }
      
        public onCancel = () => {
          this.location.back();
        }
      
        public createOwner = (ownerFormValue) => {
          if (this.ownerForm.valid) {
            this.executeOwnerCreation(ownerFormValue);
          }
        }
      
        private executeOwnerCreation = (ownerFormValue) => {
          let owner: OwnerForCreation = {
            name: ownerFormValue.name,
            dateOfBirth: ownerFormValue.dateOfBirth,
            address: ownerFormValue.address
          }
       
          let apiUrl = 'api/owner';
          this.repository.create(apiUrl, owner)
            .subscribe(res => {
              //this is temporary, until we create our dialogs
              this.location.back();
            },
            (error => {
              //temporary as well
              this.location.back();
            })
          )
        }
      
      }
      
      The last thing we need to do is to modify the .css file:
      mat-form-field{
          width: 400px;
      }
      mat-card-title{
          text-align: center;
      }
      
      Excellent. We have implemented Angular Material Form Validation in couple of steps and now we can check the result: create component finished - Angular Material Form Validation

      Adding Dialogs and Shared Module

      We have finished the owner component creation but we need to inform a user about the creation result, whether it was successful or not. For that purpose, we are going to create two dialog components. One for the success message and another one for the error message.

      Shared Module

      Before we do that, we are going to create a shared module to register our dialog components and to register modules that are already registered inside the app module and owner module as well. So, let’s create a shared module first:
      ng g module shared --spec false
      Now, let’s modify the shared.module.ts file:
      import { NgModule } from '@angular/core';
      import { CommonModule } from '@angular/common';
      import { MaterialModule } from '../material/material.module';
      import { FlexLayoutModule } from '@angular/flex-layout';
      
      @NgModule({
        imports: [
          CommonModule,
          MaterialModule,
          FlexLayoutModule,
        ],
        exports: [
          MaterialModule,
          FlexLayoutModule
        ],
        declarations: []
      })
      export class SharedModule { }
      
      Important: Because we now have the FlexLayoutModule and MaterialModule inside of the shared module file, we don’t need them anymore in the app and owner module files. Therefore, we can remove the FlexLayoutModule and MaterialModule imports from the app and owner module files and just import the SharedModule in both mentioned module files (app and owner).

      Dialogs Creation

      To continue, we are going to create our dialog components:
      ng g component shared/dialogs/success-dialog --spec false
      ng g component shared/dialogs/error-dialog --spec false
      
      dialog components creation These modules are imported automatically in the shared.module.ts file, but we need to export them as well. Moreover, we need to place the dialog components inside the entryComponents array because we are not going to use routing nor app selector to call these components. We are going to use them as a template reference for the dialog’s open() function and thus the need for the entryComponents array:
      exports: [
          MaterialModule,
          FlexLayoutModule,
          SuccessDialogComponent,
          ErrorDialogComponent
        ],
        entryComponents: [
          SuccessDialogComponent,
          ErrorDialogComponent
        ]
      

      Success Dialog Modification

      Let’s open the success-dialog.component.html file and modify it:
      <section fxLayout="column" fxLayoutAlign="center center">
        <h1 mat-dialog-title>Success message</h1>
        <mat-dialog-content>
          <p>Action completed successfully</p>
        </mat-dialog-content>
        <mat-dialog-actions>
          <button mat-raised-button color="primary" [mat-dialog-close]="true">OK</button>
        </mat-dialog-actions>
      </section>
      
      One important thing to notice here is a usage of the mat-dialog-close attribute which instructs this button to close the dialog and submits a result (true in this case). To fetch this result, we need to subscribe to the afterClosed() function. We are going to do that a bit later. We haven’t used the mat-dialog elements in our project, therefore we need to register it in the material module:
      import { ..., MatDialogModule} from '@angular/material';
      imports: [
          MatDialogModule,
      
      exports: [
          MatDialogModule,
      
      Now, to use this success dialog, we are going to modify the owner-create.component.ts file. Our dialog needs to have a configuration, and we are going to provide that:
      import { MatDialog } from '@angular/material';
      ...
      
      private dialogConfig;
      
        constructor(private location: Location, private repository: RepositoryService, private dialog: MatDialog) { }
      
        ngOnInit() {
          this.ownerForm = new FormGroup({
            name: new FormControl('', [Validators.required, Validators.maxLength(60)]),
            dateOfBirth: new FormControl(new Date()),
            address: new FormControl('', [Validators.required, Validators.maxLength(100)])
          });
      
          this.dialogConfig = {
            height: '200px',
            width: '400px',
            disableClose: true,
            data: { }
          }
        }
      
      To start our success dialog, we need to import MatDialog and to create a private variable in a constructor of the same type. The dialogConfig object consists of several properties which describe the height, width, disable close dialog when clicking outside of the dialog and passing data to the dialog. Because we don’t want to pass anything to the success dialog, we have left an empty data object. Now, let’s modify the subscribe part of the component, to call this dialog:
      let apiUrl = 'api/owner';
      this.repository.create(apiUrl, owner)
        .subscribe(res => {
          let dialogRef = this.dialog.open(SuccessDialogComponent, this.dialogConfig);
      
          //we are subscribing on the [mat-dialog-close] attribute as soon as we click on the dialog button
          dialogRef.afterClosed()
            .subscribe(result => {
              this.location.back();
            });
        },
          (error => {
            //temporary as well
            this.location.back();
          })
        )
      
      This is the result: success dialog

      Error Dialog Modifications

      We are going to send an error message to the error dialog and we don’t want to emit any event when we click the dialog button, therefore the Error dialog implementation will be a little different. Let’s start by modifying the error-dialog.component.ts file:
      import { Component, OnInit, Inject } from '@angular/core';
      import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
      
      @Component({
        selector: 'app-error-dialog',
        templateUrl: './error-dialog.component.html',
        styleUrls: ['./error-dialog.component.css']
      })
      export class ErrorDialogComponent implements OnInit {
      
        constructor(public dialogRef: MatDialogRef<ErrorDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { }
      
        ngOnInit() {
        }
      
        public closeDialog = () => {
          this.dialogRef.close();
        }
      }
      
      The dialogRef variable is here to help us manipulate our opened dialog and the data variable is here to accept any information passed to this component. Of course, we must use the MAT_DIALOG_DATA injection token to enable data acceptance from other components. Now, we have to modify the error-dialog.component.html file:
      <section fxLayout="column" fxLayoutAlign="center center">
        <h1 mat-dialog-title>Error message</h1>
        <mat-dialog-content>
          <p>{{data.errorMessage}}</p>
        </mat-dialog-content>
        <mat-dialog-actions>
          <button mat-raised-button color="warn" (click)="closeDialog()">OK</button>
        </mat-dialog-actions>
      </section>
      
      Having that done, let’s modify the error-handler.service file:
      ... Other imports
      import { ErrorDialogComponent } from './dialogs/error-dialog/error-dialog.component';
      
      @Injectable({
        providedIn: 'root'
      })
      export class ErrorHandlerService {
        public errorMessage: string = '';
        public dialogConfig;
      
        constructor(private router: Router, private dialog: MatDialog) { }
      
      ... Other code
      
      private handleOtherError(error: HttpErrorResponse){
          this.createErrorMessage(error);
          this.dialogConfig.data = { 'errorMessage': this.errorMessage };
          this.dialog.open(ErrorDialogComponent, this.dialogConfig);
        }
      
      
      And finally, let’s modify the owner-create.component.ts file:
      import { ErrorHandlerService } from '../../shared/error-handler.service';
      constructor(private location: Location, private repository: RepositoryService, private dialog: MatDialog, private errorService: ErrorHandlerService) { }
      this.repository.create(apiUrl, owner)
            .subscribe(res => {
              let dialogRef = this.dialog.open(SuccessDialogComponent, this.dialogConfig);
      
              //we are subscribing on the [mat-dialog-close] attribute as soon as we click on the dialog button
              dialogRef.afterClosed()
                .subscribe(result => {
                  this.location.back();
                });
            },
              (error => {
                this.errorService.dialogConfig = { ...this.dialogConfig };
                this.errorService.handleError(error);
              })
            )
      
      And that is all. Now, we can check the result: error dialog completed

      Conclusion

      Excellent. We have created a lot of features and covered a lot of material components. We have left out the update and delete functionalities, but this is something that you could do on your own now as an exercise. In all the previous articles, we have covered all the material related logic we need for the Update and Delete actions (Forms, Validations, Input Fields, Dialogs, etc) and in Angular PUT Actions and Angular Delete Actions, we implemented all the required Angular related logic. Of course, if you have any problem creating new functionalities, we have provided the source code for that as well. So, feel free to read them, if you haven’t already, and make your own Update and Delete parts of this project. [sc name="subscribe" formNumber="47515" contentType="Angular Material"]]]>
      4941 0 0 0 1077 0 0 1078 1077 0 1351 0 0 1352 1351 0 1355 1352 0
      ASP.NET Core Web API with EF Core DB-First Approach https://code-maze.com/asp-net-core-web-api-with-ef-core-db-first-approach/ Mon, 26 Nov 2018 09:00:30 +0000 https://code-maze.com/?p=5050 One-to-One – A row in Table A can have only one matching row in Table B, and vice versa. One-to-Many (or Many-to-One) – A row in Table A can have many matching rows in table B, but a row in table B can have only one matching row in Table A. Many-to-Many– A row in Table A can have many matching rows in Table B, and vice versa. You can download the source code for this article, on the EF DB First Approach Source Code repo. We have divided this article into the following sections:

      Creating a Database and Tables

      As the first step, we are going to create the database and tables. So for example, let’s create a database to manage books. We are going to create tables for storing information about Books, Authors, Publishers etc. and establish relationships between them.  This is the complete SQL script for creating database tables and relationships.
      CREATE DATABASE BookStore 
      
      GO 
      
      USE BookStore 
      
      GO 
      
      CREATE TABLE Author 
        ( 
           Id   BIGINT IDENTITY(1, 1) NOT NULL, 
           NAME NVARCHAR(50) NOT NULL, 
           PRIMARY KEY (Id) 
        ) 
      
      GO 
      
      CREATE TABLE AuthorContact 
        ( 
           AuthorId      BIGINT NOT NULL, 
           ContactNumber NVARCHAR(15) NULL, 
           Address       NVARCHAR(100) NULL, 
           PRIMARY KEY (AuthorId), 
           FOREIGN KEY (AuthorId) REFERENCES Author(Id) 
        ) 
      
      GO 
      
      CREATE TABLE BookCategory 
        ( 
           Id   BIGINT IDENTITY(1, 1) NOT NULL, 
           NAME NVARCHAR(50) NOT NULL, 
           PRIMARY KEY (Id) 
        ) 
      
      GO 
      
      CREATE TABLE Publisher 
        ( 
           Id   BIGINT IDENTITY(1, 1) NOT NULL, 
           NAME NVARCHAR(100) NOT NULL, 
           PRIMARY KEY (Id) 
        ) 
      
      GO 
      
      CREATE TABLE Book 
        ( 
           Id          BIGINT IDENTITY(1, 1) NOT NULL, 
           Title       NVARCHAR(100) NOT NULL, 
           CategoryId  BIGINT NOT NULL, 
           PublisherId BIGINT NOT NULL, 
           PRIMARY KEY (Id), 
           FOREIGN KEY (CategoryId) REFERENCES BookCategory(Id), 
           FOREIGN KEY (PublisherId) REFERENCES Publisher(Id) 
        ) 
      
      GO 
      
      CREATE TABLE BookAuthors 
        ( 
           BookId   BIGINT NOT NULL, 
           AuthorId BIGINT NOT NULL 
           PRIMARY KEY (BookId, AuthorId), 
           FOREIGN KEY (BookId) REFERENCES Book(Id), 
           FOREIGN KEY (AuthorId) REFERENCES Author(Id) 
        )
      After running the script, we can see the tables and relationships created as below: DB Diagram

      Database design explained

      Tables:

      Author- Stores the information about the authors. AuthorContact- Contains the contact information about the authors. Book- Stores the information about the books. Publisher- Keeps the information about the publishers. BookCategory- Keeps the master list of all the categories. BookAuthors- Represents the mapping between the books and the authors.

      Relationships:

      Let’s take a look at how we implement the different types of relationships in our database design.
      One-to-One(1:1)
      In the above design, AuthorandAuthorContact have a 1:1 relationship between them. Each entry in theAuthor table has a corresponding entry in theAuthorContact table. They are related by theAuthorId foreign key. This type of relationship is not very common. We could also keep the author contact information in theAuthor table. But in certain scenarios, there could be some valid reasons to split out information into different tables like security, performance etc.
      One-to-Many(1:N)
      In the above design, PublisherandBook have a 1:N relationship between them. A publisher can publish many books, but a book can have only one publisher. They are related by thePublisherId foreign key. This is the most common type of relationship in any database.
      Many-to-Many(M:N)
      In the above design, BookandAuthor have an M:N relationships between them. A book can have many authors and at the same time, an author can write many books. They are related by an intermediate tableBookAuthors. This is also called an associative or junction table. We can translate an M:N relationship to two 1:N relationships, but linked by an intermediary table.

      Inserting Test Data

      Now that we have created our tables and established relationships between them, let’s insert some test data into them. Let’s use the below DB script to insert data:
      INSERT INTO BookCategory
      VALUES
      ('Fantasy Fiction'),
      ('Spirituality'),
      ('Fiction'),
      ('Science Fiction')
      
      INSERT INTO Publisher
      VALUES
      ('HarperCollins'),
      ('New World Library'),
      ('Oneworld Publications')
      
      INSERT INTO Author
      VALUES
      ('Paulo Coelho'),
      ('Eckhart Tolle'),
      ('Amie Kaufman'),
      ('Jay Kristoff')
      
      INSERT INTO AuthorContact
      VALUES
      (1, '111-222-3333', '133 salas 601 / 602, Rio de Janeiro 22070-010. BRAZIL'),
      (2, '444-555-6666', '933 Seymour St, Vancouver, BC V6B 6L6, Canada'),
      (3, '777-888-9999', 'Mentone 3194. Victoria. AUSTRALIA'),
      (4, '222-333-4444', '234 Collins Street, Melbourne, VIC, AUSTRALIA')
      
      INSERT INTO Book
      VALUES
      ('The Alchemist', 1, 1),
      ('The Power of Now', 2, 2),
      ('Eleven Minutes', 3, 1),
      ('Illuminae', 4, 3)
      
      INSERT INTO BookAuthors
      VALUES
      (1,1),
      (2,2),
      (3,1),
      (4,3),
      (4,4)
      
      After running the above insert script, our database tables will look like this Tables With Data

      Data Modelling - Creating Models and a Context

      So, now we have our database tables with data. Let’s model our entities based on those. As a first step, let’s set up an ASP.NET Core Web API Project. We have explained this in detail in one of our other articles: Creating and configuring a new ASP.NET Core Web API project. The article linked above covers a lot of additional topics. You may go through the entire article if you want to, but the section linked above is quite enough to follow along with this article. Following the article linked above, let’s create a new project calledEFCoreDatabaseFirstSample.

      Creating Models

      Now it's time to create the EF model based on our existing database. Go to Tools –> NuGet Package Manager –> Package Manager Console Run the following command to create the models from the existing database:
      Scaffold-DbContext "Server=.;Database=BookStore;Trusted_Connection=True;" 
      Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
      The above command will generate the following classes: Generated Classes BookStoreContext is the DB context class and other classes are the models. Now, let's look at how EF Core represents the relationships.

      One-to-One(1:1)

      Let’s take a look at the Author class: 
      public partial class Author
      {
          public long Id { get; set; }
          public string Name { get; set; }
      
          public virtual AuthorContact AuthorContact { get; set; }
          public virtual ICollection<BookAuthors> BookAuthors { get; set; }
      }
      Remember that theAuthor has a 1:1 relationship with theAuthorContact. To represent this we have anAuthorContactproperty in theAuthor class. This is called the Navigation Property.

      One-to-Many(1:N)

      Let’s take a look at the Publisher & Book classes:
      public partial class Publisher
      {
          public Publisher()
          {
              Books = new HashSet<Book>();
          }
      
          public long Id { get; set; }
          public string Name { get; set; }
      
          public virtual ICollection<Book> Books { get; set; }
      }
          
      public partial class Book
      {
          public Book()
          {
              BookAuthors = new HashSet<BookAuthors>();
          }
      
          public long Id { get; set; }
          public string Title { get; set; }
          public long CategoryId { get; set; }
          public long PublisherId { get; set; }
      
          public virtual BookCategory Category { get; set; }
          public virtual Publisher Publisher { get; set; }
          public virtual ICollection<BookAuthors> BookAuthors { get; set; }
      }
      Remember that thePublisherhas a 1:N relationship with theBook. Here, the Publisher is called the Principal Entity and the Book is called Dependent Entity. Publisher.PublisherId is the Principal Key and Book.PublisherId is the Foreign Key. Publisher.Books is the Collection Navigation property. Book.Publisher is the Reference Navigation property.

      Many-to-Many(M:N)

      Note: As of now, EF Core does not support many-to-many relationships without using an entity class for representing the join table. However, we can represent it by using an entity class for the join table. We could then map two separate one-to-many relationships. Let’s take a look at the Book, Author & BookAuthors classes. (Book and Author classes are already shown above):
      public partial class BookAuthors
      {
          public long BookId { get; set; }
          public long AuthorId { get; set; }
      
          public virtual Author Author { get; set; }
          public virtual Book Book { get; set; }
      }
      We can see that both the Book and the Author has a collection navigation propertyBookAuthorsWe have established the M:N relationship between theBook and the Author by these two 1:N relationships. Recommendation: For an even better understanding of Entity Framework Core, we strongly suggest reading our Entity Framework Core Series. There, you can find a lot of information related to different EF Core features.

      Creating a Repository

      Now that we have successfully created the models and context, let’s implement a simple data repository using the repository pattern. We have explained this pattern in detail in one of our other articles: Implementing the repository pattern. If you get stuck with the code, you can always refer to the mentioned article and to our source code for this article, as well. Let’s add a new folder under Models and name it Repository. We’ll then create a new interface called IDataRepository:
      public interface IDataRepository<TEntity, TDto>
      {
          IEnumerable<TEntity> GetAll();
          TEntity Get(long id);
          TDto GetDto(long id);
          void Add(TEntity entity);
          void Update(TEntity entityToUpdate, TEntity entity);
          void Delete(TEntity entity);
      }
      
      We will later inject this interface into our controller. Then the API will communicate with the data context using this interface. Of course, we are going to register all the repo services in the Startup class, as you can find out by your self in our source code. Next, let’s create concrete classes that implement theIDataRepository interface. We'll add a new folder under Models called DataManager. Let's keep things simple and focus on implementing only the required functions.

      Querying & Loading Related Data

      EF Core uses navigation properties in our model to load related entities. We use three common ORM patterns for loading related data. When we use eager loading, we load the related data from the database as part of the initial query. Explicit loading means that we load the related data explicitly from the database at a later time. Lazy loading is a way of loading the related data from the database when we access the navigation property.

      Eager loading

      We can use theIncludemethod to specify related data that need to be included in the query results. In the following example, the Authors that are returned in the results will have their  AuthorContacts property auto-populated. Let’s add a new class AuthorDataManager which implements theIDataRepository in the DataManager folder, and register it in the Startup class. We'll then implement the GetAll():
      public IEnumerable<Author> GetAll()
      {
          return _bookStoreContext.Author
              .Include(author => author.AuthorContact)
              .ToList();
      }
      The above code loads all the authors with their contact details at once since we are using eager loading. We shall verify this later when we test it.

      Explicit loading

      We can explicitly load a navigation property using the DbContext.Entry(). Let’s add a new class BookDataManager which implements the IDataRepository interface and register it in the Startup class as well. We'll then implement the Get()method:
      public Book Get(long id)
      {
          _bookStoreContext.ChangeTracker.LazyLoadingEnabled = false;
      
          var book = _bookStoreContext.Book
                .SingleOrDefault(b => b.Id == id);
      
          if (book == null)
          {
             return null;
          }
      
          _bookStoreContext.Entry(book)
              .Collection(b => b.BookAuthors)
              .Load();
      
          _bookStoreContext.Entry(book)
              .Reference(b => b.Publisher)
              .Load();
                  
          return book;
      }
      The above code is used to get the details of a Book. See how we are explicitly loading the list of BookAuthors and Publisher later. We’ll verify the explicit loading behavior later when we test this functionality.

      Lazy loading

      The simplest way to use lazy-loading is by installing the Microsoft.EntityFrameworkCore.Proxies package and enabling it with a call to UseLazyLoadingProxies. This is shown in the below code
      protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
      {
          if (!optionsBuilder.IsConfigured)
          {
              optionsBuilder
                  .UseLazyLoadingProxies()
                  .UseSqlServer("Server=.;Database=BookStore;Trusted_Connection=True;");
          }
      }
      EF Core will then enable lazy loading for any navigation property that can be overridden. Only thing is that it must be virtual and on a class that can be inherited from. For example, in the below Authorclass, the BookAuthors navigation property will be lazy-loaded:
      public partial class Author
      {
          public long Id { get; set; }
          public string Name { get; set; }
      
          public virtual AuthorContact AuthorContact { get; set; }
          public virtual ICollection<BookAuthors> BookAuthors { get; set; }
      }
      Let’s then disable lazy-loading at a context level. This helps to avoid circular referencing issues:
      public BookStoreContext(DbContextOptions<BooksStoreContext> options)
          : base(options)
      {
          ChangeTracker.LazyLoadingEnabled = false;
      }
      We’ll enable lazy-loading explicitly when we need to utilize it. Let’s implement the GetDto() method in the AuthorDataManagerclass:
      public AuthorDto GetDto(long id)
      {
          _bookStoreContext.ChangeTracker.LazyLoadingEnabled = true;
      
          using (var context = new BookStoreContext())
          {
      	var author = context.Author
                 .SingleOrDefault(b => b.Id == id);
      	return AuthorDtoMapper.MapToDto(author);
          }
      }
      
      public class AuthorDto
      {
          public AuthorDto()
          {
          }
      
          public long Id { get; set; }
      
          public string Name { get; set; }
      
          public AuthorContactDto AuthorContact { get; set; }
      }
      
      public static class AuthorDtoMapper
      {
          public static AuthorDto MapToDto(Author author)
          {
              return new AuthorDto()
              {
                  Id = author.Id,
                  Name = author.Name,
      
                  AuthorContact = new AuthorContactDto()
                  {
                      AuthorId = author.Id,
                      Address = author.AuthorContact.Address,
                      ContactNumber = author.AuthorContact.ContactNumber
                  }
              };
          }
      }
      In the code above, since we are using lazy loading, only the Author entity will be loaded initially. Later the AuthorContact property will be loaded only when we reference it inside the DTO mapper. We’ll verify this behavior later when we test this. Note: The referenced property can be lazy-loaded only inside the scope of the data context class. Once the context is out of scope, we will no longer have access to those.

      Saving Related Data

      In this section, we’ll explain how we can Add, Update and Delete related entities.

      Add

      If we create several new related entities, adding one of them to the context will cause the others to be added too. For example, in the below code, let’s implement the Add() method in AuthorDataManager. This will cause both Author and AuthorContact entities to be created:
      public void Add(Author entity)
      {
          _bookStoreContext.Author.Add(entity);
          _bookStoreContext.SaveChanges();
      }

      Update

      Now let’s implement the update. The below code implements the Update() method in AuthorDataManager class:
      public void Update(Author entityToUpdate, Author entity)
      {
          entityToUpdate = _bookStoreContext.Author
              .Include(a => a.BookAuthors)
              .Include(a => a.AuthorContact)
              .Single(b => b.Id == entityToUpdate.Id);
      
          entityToUpdate.Name = entity.Name;
      
          entityToUpdate.AuthorContact.Address = entity.AuthorContact.Address;
          entityToUpdate.AuthorContact.ContactNumber = entity.AuthorContact.ContactNumber;
      
          var deletedBooks = entityToUpdate.BookAuthors.Except(entity.BookAuthors).ToList();
          var addedBooks = entity.BookAuthors.Except(entityToUpdate.BookAuthors).ToList();
      
          deletedBooks.ForEach(bookToDelete =>
              entityToUpdate.BookAuthors.Remove(
                  entityToUpdate.BookAuthors
                      .First(b => b.BookId == bookToDelete.BookId)));
      
          foreach (var addedBook in addedBooks)
          {
              _bookStoreContext.Entry(addedBook).State = EntityState.Added;
          }
      
          _bookStoreContext.SaveChanges();
      }
      The above code will cause the Author,AuthorContactand BookAuthors entities to be updated. We’ll verify this later when we test this.

      Delete

      Delete operation can be tricky with related entities. There are three actions EF can take when a parent entity is deleted.
      • The child can be deleted
      • The child's foreign key values can be set to null
      • The child remains unchanged
      We should configure the DeleteBehavior appropriately based on our application logic. In the below example, let’s say when a publisher is deleted, we need the publisher’s book also to be deleted. First, let’s configure this in the OnModelCreating method in our context:
      modelBuilder.Entity<Book>(entity =>
      {
          entity.Property(e => e.Title)
              .IsRequired()
              .HasMaxLength(100);
      
          entity.HasOne(d => d.Publisher)
              .WithMany(p => p.Books)
              .HasForeignKey(d => d.PublisherId)
              .OnDelete(DeleteBehavior.Cascade)
              .HasConstraintName("FK_Books_Publishers");
      
          entity.HasOne(d => d.Category)
              .WithMany(p => p.Book)
              .HasForeignKey(d => d.CategoryId)
              .OnDelete(DeleteBehavior.ClientSetNull)
              .HasConstraintName("FK_Books_BookCategory");
      });
      Now let’s implement the Delete() method in PublisherDataManager class:
      public void Delete(Publisher entity)
      {
          _booksStoreContext.Remove(entity);
          _booksStoreContext.SaveChanges();
      }
      The above code will delete the Publisherand any related Book entities. We’ll verify this later when we test this functionality.  Now, all we have to do is to register our DataManager classes inside the IOC:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext<BookStoreContext>(opts => opts.UseSqlServer(Configuration["ConnectionString:BooksDB"]));
          services.AddScoped<IDataRepository<Author, AuthorDto>, AuthorDataManager>();
          services.AddScoped<IDataRepository<Book, BookDto>, BookDataManager>();
          services.AddScoped<IDataRepository<Publisher, PublisherDto>, PublisherDataManager>();
      
          services.AddMvc()
              .AddJsonOptions(
                  options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
              ).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
      }
      This is the appsettings.json file:
      {
        "Logging": {
          "LogLevel": {
            "Default": "Warning"
          }
        },
        "ConnectionString": {
          "BooksDB": "Server=.;Database=BookStore;Trusted_Connection=True"
        },
        "AllowedHosts": "*"
      }
      Excellent. Now we can move on.

      Creating the API Controller

      Now that our DataManager is all set, let's create the API Controller and create the endpoints for handling CRUD operations. This is described in detail in one of our other articles: Creating a .NET Core Web API Controller Following the above article, let’s create the AuthorsController,BooksController and PublishersController class in the Controllers folder as shown below. For keeping things simple and focused, we'll implement only those endpoints required to understand the concepts we discuss in this article. Let’s implement the GetAll,Get,Post and Put method in the AuthorsControllerclass:
      [Route("api/authors")]
      [ApiController]
      public class AuthorsController : ControllerBase
      {
          private readonly IDataRepository<Author, AuthorDto> _dataRepository;
      
          public AuthorsController(IDataRepository<Author, AuthorDto> dataRepository)
          {
              _dataRepository = dataRepository;
          }
      
          // GET: api/Authors
          [HttpGet]
          public IActionResult Get()
          {
              var authors = _dataRepository.GetAll();
              return Ok(authors);
          }
      
          // GET: api/Authors/5
          [HttpGet("{id}", Name = "GetAuthor")]
          public IActionResult Get(int id)
          {
              var author = _dataRepository.GetDto(id);
              if (author == null)
              {
                  return NotFound("Author not found.");
              }
      
              return Ok(author);
          }
      
          // POST: api/Authors
          [HttpPost]
          public IActionResult Post([FromBody] Author author)
          {
              if (author is null)
              {
                  return BadRequest("Author is null.");
              }
      
              if (!ModelState.IsValid)
              {
                  return BadRequest();
              }
      
              _dataRepository.Add(author);
              return CreatedAtRoute("GetAuthor", new { Id = author.Id }, null);
          }
      
          // PUT: api/Authors/5
          [HttpPut("{id}")]
          public IActionResult Put(int id, [FromBody] Author author)
          {
              if (author == null)
              {
                  return BadRequest("Author is null.");
              }
      
              var authorToUpdate = _dataRepository.Get(id);
              if (authorToUpdate == null)
              {
                  return NotFound("The Employee record couldn't be found.");
              }
      
              if (!ModelState.IsValid)
              {
                  return BadRequest();
              }
      
              _dataRepository.Update(authorToUpdate, author);
              return NoContent();
          }
      }
      
      Then let’s implement the Get() method in the BooksController:
      [Route("api/books")]
      [ApiController]
      public class BooksController : ControllerBase
      {
          private readonly IDataRepository<Book, BookDto> _dataRepository;
      
          public BooksController(IDataRepository<Book, BookDto> dataRepository)
          {
              _dataRepository = dataRepository;
          }
      
          // GET: api/Books/5
          [HttpGet("{id}")]
          public IActionResult Get(int id)
          {
              var book = _dataRepository.Get(id);
              if (book == null)
              {
                  return NotFound("Book not found.");
              }
      
              return Ok(book);
          }
      }
      
      Finally, let’s implement the Delete() method in the PublisherController:
      [Route("api/publishers")]
      [ApiController]
      public class PublishersController : ControllerBase
      {
          private readonly IDataRepository<Publisher, PublisherDto> _dataRepository;
      
          public PublishersController(IDataRepository<Publisher, PublisherDto> dataRepository)
          {
              _dataRepository = dataRepository;
          }
      
          // DELETE: api/ApiWithActions/5
          [HttpDelete("{id}")]
          public IActionResult Delete(int id)
          {
              var publisher = _dataRepository.Get(id);
              if (publisher == null)
              {
                  return NotFound("The Publisher record couldn't be found.");
              }
      
              _dataRepository.Delete(publisher);
              return NoContent();
          }
      }
      

      Recommendation

      If you want to learn in great detail about Entity Framework Core and many of its features, we recommend going through our Entity Framework Core series. Through the entire series, we talk about different EF Core features, from the Context classes and DbSet properties, relationships and none-relational configurations, additional migration information and querying the database. If you want, you have a place to learn a lot more on this topic.

      Testing the API

      Now we’ll test the controller methods using Postman. We'll also verify the results in the database. Later, we'll inspect the actual SQL queries executed in the database using the SQL Server Profiler.

      Loading the Data

      First, let’s test the GetAll endpoint of Authors: GET All Authors Remember that we used eager loading for implementing this functionality. If we look at the Profiler, we can see that the query fetches data by joining Author and AuthorContact tables: GetAll Profiler Next, let’s test the Get endpoint of the Book: GET Book Remember that we used explicit loading to implement this functionality. Here note that only those properties that we chose to load explicitly have data. Other related properties are empty. In the Profiler, we can see that initially, an SQL query fetches data from the Book table. Later, queries are generated to fetch data from other tables when we explicitly load data from other entities. GET Book Profiler Now, let’s test the Get endpoint of Author: Get Author Remember that we used lazy loading to implement this functionality. In the Profiler, we can see that initially only data from the Author table is loaded. Later, when we refer the AuthorContact entity inside the DTO Mapper class, another query loads data from the AuthorContact table: Get_Author_Profiler

      Updating Data

      Now, let’s test the Add endpoint of Author: POST_Author We can see that two INSERT queries are generated to insert data into tables Author and AuthorContact: INSERT_Profiler We can verify that our Add endpoint inserts data in both tables: DB_Result_After_POST Now let’s test the Update endpoint of Authors. We’ll insert some data into Publisher,BookCategory and Book table:
      INSERT INTO Publisher
      VALUES
      ('Simon & Schuster'),
      ('Oxford University Press')
      
      
      INSERT INTO BookCategory
      VALUES
      ('Tragedy'),
      ('Romance')
      
      
      INSERT INTO Book
      VALUES
      ('Hamlet', 5, 4),
      ('Romeo and Juliet', 6, 5)
      
      Let’s modify the Author we just inserted. Let’s edit the ContactNumber and map the newly added Books to this author: POST_Author In the Profiler, we can see an UPDATE query for the AuthorContact table and two INSERT queries for the BookAuthors table: UPDATE_Profiler Let’s verify the results in the database: DB_Result_After_PUT Finally, let’s test the Delete endpoint of Publisher. We'll insert a test publisher and two related books:
      INSERT INTO Publisher
      VALUES
      ('My Publisher')
      
      INSERT INTO Book
      VALUES
      ('My Publisher Book 1', 5, 6),
      ('My Publisher Book 2', 4, 6)
      
      Now let’s test the Delete endpoint. DELETE_Publisher In the Profiler, we can see that the related data is first removed from theBook table. Then the publisher record is deleted from thePublisher table. DELETE_Profiler Let’s verify the changes in the database. DB_Result_After_DELETE

      Conclusion

      In this article, we have covered the following topics.
      • EF Core Database-First approach and when to use it.
      • Different types of relationships in a database.
      • Creating a database and tables with relationships.
      • Modeling the entities with relationships.
      • Loading and saving related data using the repository pattern.
      • Different patterns for loading related data.
      • Creating API endpoints for operating on related data.
      • Testing the endpoints and inspecting the generated database queries.
      Hope you enjoyed the article. Happy programming! [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5050 0 0 0 544 0 0 628 0 0 629 544 0 703 628 0 1044 1043 0 1043 0 0 1226 0 0 1230 1226 0 1472 0 0 1473 1472 0 1474 1473 0 1623 Scaffold-DbContext "Server=.;Database=BookStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models me aparece el error Scaffold-DbContext : No se puede enlazar el argumento con el parámetro 'Provider' porque es una cadena vacía. En línea: 1 Carácter: 1 + Scaffold-DbContext "Server=.(localdb)ProjectsV13;Database=DataBaseF ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidData: (:) [Scaffold-DbContext], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Scaffold-DbContext Lo solucione instalando Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 3.0.1 y adicionalmente en el xml del proyecto(doble clic en en el proyecto, NO en la solucion) se adiciona: all volver a ejecutar el comando Scaffold-DbContext "Server=.;Database=BookStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models y listo espero les sirva]]> 0 0 1624 1623 0
      Uploading Files With .NET Core Web API and Angular https://code-maze.com/upload-files-dot-net-core-angular/ Mon, 17 Dec 2018 07:00:08 +0000 https://code-maze.com/?p=5090
    • ASP.NET Core Authentication with JWT and Angular 
    • .NET Core Tutorial
    • ASP.NET Core Web API with EF Core Code-First Approach
    • Enabling CORS in ASP.NET Core Web API
    • Angular Material Tutorial
    • [sc name="part_of_series" headline="Recommended Articles"] We have created the starter project to work with through this blog post and it can be downloaded from Upload Files .NET Core Angular Starter Project. We strongly recommend downloading this project because it would be much easier for you to follow along. In this project, we'll create a new user and display all the created users as an additional feature. We are going to modify the create-logic part by adding an upload functionality having a new user created together with an image path related to it. If you want to download our finished project, you can do that from Upload Files .NET Core Angular Finished Project. We are going to divide this article into the following sections:

      Controller and Action Logic - .NET Core Part

      After you've downloaded our starter project, you can start by opening the UploadFilesServer project. This project is created on top of the SQL database, so to create that database, we need to run the update-database command in a Package Manager Console window. By doing this, our migrations will be executed and the database with the required table will be created. The next step is to create a new folder Resources and inside it a new folder Images: static files - how to upload files with .Net core To continue, let’s create a simple API Controller file in the Controllers folder and name it UploadController. Let’s modify that file by adding a new action that will be responsible for the upload logic:
      [HttpPost, DisableRequestSizeLimit]
      public IActionResult Upload()
      {
          try
          {
              var file = Request.Form.Files[0];
              var folderName = Path.Combine("Resources", "Images");
              var pathToSave = Path.Combine(Directory.GetCurrentDirectory(), folderName);
      
              if (file.Length > 0)
              {
                  var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
                  var fullPath = Path.Combine(pathToSave, fileName);
                  var dbPath = Path.Combine(folderName, fileName);
      
                  using (var stream = new FileStream(fullPath, FileMode.Create))
                  {
                      file.CopyTo(stream);
                  }
      
                  return Ok(new { dbPath });
              }
              else
              {
                  return BadRequest();
              }
          }
          catch (Exception ex)
          {
              return StatusCode(500, "Internal server error");
          }
      }
      
      [learn_more caption="MultiPartBodyLength error ASP.NET Core 3.0" state="open"] If you are using your own application (not our starting app) with version 3.0 and you have the MultiPartBodyLength error, try adding this configuration in the Startup.cs class:
      services.Configure<FormOptions>(o => {
          o.ValueLengthLimit = int.MaxValue;
          o.MultipartBodyLengthLimit = int.MaxValue;
          o.MemoryBufferThreshold = int.MaxValue;
      });
      [/learn_more] We are using a POST action for the upload-related logic and disabling the request size limit as well. The logic inside this action is pretty straightforward. We extract the file from the request and provide the path where the file will be stored. Moreover, if the file has a length greater then zero, we just take its name and provide a full path on the server to store our file and a path to the database. This database path is going to be returned as a result of this action after we place our stream into the defined folder. We could also check if a file with the same name already exists, but didn't want to make the code more complicated at this moment.

      Serving Static Files

      Usually, all the files in the wwwroot folder are servable for the client applications. We provide that by adding app.UseStaticFiles() in the Startup class in the Configure method. Of course, our uploaded images will be stored in the Resources folder, and due to that, we need to make it servable as well. To do that, let’s modify the Configure method in the Startup.cs class:
      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
          else
          {
              app.UseHsts();
          }
      
          app.UseHttpsRedirection();
          app.UseCors("CorsPolicy");
          app.UseStaticFiles();
          app.UseStaticFiles(new StaticFileOptions()
          {
              FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), @"Resources")),
              RequestPath = new PathString("/Resources")
          });
          app.UseMvc();
       }
      
      And that's all it takes. We have prepared our server-side app and it is time to jump right to the client side code. If you want to learn in great detail about .NET Core project development, you can visit the .NET Core Tutorial.

      Upload Files – Angular Part

      Let’s open the UploadFilesClient project and take a look at the app component files. For the sake of simplicity, we have implemented all of our logic inside the app component. To learn in great detail about Angular project development, you can read the Angular Tutorial. So, the first thing we are going to do is to create a new Upload component in which we will handle all the upload-related logic:
      ng g component upload --spec false
      This will create three files in the upload folder, and we are going to modify the upload.component.ts file first:
      import { Component, OnInit, Output, EventEmitter } from '@angular/core';
      import { HttpEventType, HttpClient } from '@angular/common/http';
      
      @Component({
        selector: 'app-upload',
        templateUrl: './upload.component.html',
        styleUrls: ['./upload.component.css']
      })
      export class UploadComponent implements OnInit {
        public progress: number;
        public message: string;
        @Output() public onUploadFinished = new EventEmitter();
      
        constructor(private http: HttpClient) { }
      
        ngOnInit() {
        }
      
        public uploadFile = (files) => {
          if (files.length === 0) {
            return;
          }
      
          let fileToUpload = <File>files[0];
          const formData = new FormData();
          formData.append('file', fileToUpload, fileToUpload.name);
      
          this.http.post('https://localhost:5001/api/upload', formData, {reportProgress: true, observe: 'events'})
            .subscribe(event => {
              if (event.type === HttpEventType.UploadProgress)
                this.progress = Math.round(100 * event.loaded / event.total);
              else if (event.type === HttpEventType.Response) {
                this.message = 'Upload success.';
                this.onUploadFinished.emit(event.body);
              }
            });
        }
      }
      
      So, what's going on here? We create two public variables. First one to hold the message when upload action is finished and the second one to show the upload progress. In the uploadFile function, we create a formData object and append our file that we want to upload. The next action is to send a post request and pay attention to it. Besides the URL and body properties, we have another JSON object which states that we want to track changes of our HTTP request progress. As long as the upload is in progress, we will update the progress variable and show that percentage on the screen, but as soon as the upload is finished, we are going to write a message on the screen and emit a new event. This event contains the body of our response, which is simply the database path of our uploaded file. We need that path to display uploaded image with other user details. The files with the small size will be instantly uploaded so, we will see 100% progress as soon as we select our file. But for the larger files, the progress bar will update its values for sure.

      Displaying Functionalities

      To display all of the mentioned functionalities on the screen, we need to modify the upload.component.html file now:
      <div class="row" style="margin-bottom:15px;">
        <div class="col-md-3">
          <input type="file" #file placeholder="Choose file" (change)="uploadFile(file.files)" style="display:none;">
          <button type="button" class="btn btn-success" (click)="file.click()">Upload File</button>
        </div>
        <div class="col-md-4">
          <span class="upload" *ngIf="progress > 0">
            {{progress}}%
          </span>
          <span class="upload" *ngIf="message">
            {{message}}
          </span>
        </div>
      </div>
      
      This logic is pretty straightforward except to the part where we hide the actual upload control and use its reference (#file) to invoke its click event with the button, which looks much better. We could have styled the upload control as well, but this is the better way, at least from our point of view. Finally, let’s modify the upload.component.css file:
      .upload{
          font-weight:bold;
          color:#28a745;
          margin-left: 15px;
          line-height: 36px;
      }
      
      And add a selector from the upload component to the app.component.html file:
      <app-upload></app-upload>
      <div class="row">
        <div class="offset-md-5 col-md-2">
          <button type="button" class="btn btn-primary" (click)="onCreate()">Create </button>
        </div>
      </div>
      
      Excellent. We can now inspect our result: create user - How to upload files with .Net Core We can check our Resources/Images folder as well, to be sure that the files are really uploaded: file uploaded - How to upload files in .NET Core  

      Using Uploaded File in Our Application

      As soon as we press the Create button on our form, we are going to see our newly created user. But its profile picture won’t be rendered. So, let’s fix that. First, we need to react on the onUploadFinished event from the update component, and to do that let’s modify the app.component.html file:
      <app-upload (onUploadFinished)="uploadFinished($event)"></app-upload>
      This change forces us to modify the app.component.ts file as well. First, let’s add an additional property in that file:
      public response: {dbPath: ''};
      Then let’s add the uploadFinished function to populate this property:
      public uploadFinished = (event) => {
          this.response = event;
        }
      
      With this modification, we have the response object in which we can find a path to be saved in the database. Lastly, we have to modify the user object in the onCreate function in the same file:
      this.user = {
        name: this.name,
        address: this.address,
        imgPath: this.response.dbPath
      }
      
      Great job. Now we know the image file path related to the created user, so let’s use that knowledge to render that picture next to other user details. To do that, let’s change a table inside the app.component.html file:
      <table class="table table-striped">
        <thead>
          <tr>
            <th scope="col">Image</th>
            <th scope="col">Name</th>
            <th scope="col">Address</th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let user of users">
            <td><img [src]="createImgPath(user.imgPath)" alt="profile picture" style="width:60px; height:60px;"></td>
            <td>{{user.name}}</td>
            <td>{{user.address}}</td>
          </tr>
        </tbody>
      </table>
      
      And let’s modify the app.component.ts file by adding the createImgPath function:
      public createImgPath = (serverPath: string) => {
        return `https://localhost:5001/${serverPath}`;
      }
      
      Our result should be as follows: used downloaded picture

      Uploading Multiple Files

      If we want to upload multiple files in any of our projects, we need to modify both the server and client side code. So let's start with the server side:
      public IActionResult Upload()
      {
          try
          {
              var files = Request.Form.Files;
              var folderName = Path.Combine("StaticFiles", "Images");
              var pathToSave = Path.Combine(Directory.GetCurrentDirectory(), folderName);
      
              if (files.Any(f => f.Length == 0))
              {
                  return BadRequest();
              }
      
              foreach (var file in files)
              {
                  var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
                  var fullPath = Path.Combine(pathToSave, fileName);
                  var dbPath = Path.Combine(folderName, fileName); //you can add this path to a list and then return all dbPaths to the client if require
      
                  using (var stream = new FileStream(fullPath, FileMode.Create))
                  {
                      file.CopyTo(stream);
                  }
              }
      
              return Ok("All the files are successfully uploaded.");
          }
          catch (Exception ex)
          {
              return StatusCode(500, "Internal server error");
          }
      }
      After this modification, let's change the client side. First, we need to modify the input type file control by adding the multiple attribute:
      <input type="file" #file placeholder="Choose file" (change)="uploadFile(file.files)" style="display:none;" multiple>
      After that, we are going to modify the uploadFile function:
      public uploadFile = (files) => {
        if (files.length === 0) {
          return;
        }
      
        let filesToUpload : File[] = files;
        const formData = new FormData();
          
        Array.from(filesToUpload).map((file, index) => {
          return formData.append('file'+index, file, file.name);
        });
      
        this.http.post('https://localhost:5001/api/upload', formData, {reportProgress: true, observe: 'events'})
          .subscribe(event => {
            if (event.type === HttpEventType.UploadProgress)
              this.progress = Math.round(100 * event.loaded / event.total);
            else if (event.type === HttpEventType.Response) {
              this.message = 'Upload success.';
              this.onUploadFinished.emit(event.body);
            }
          });
      }
      One interesting thing to pay attention is the use of the Array.from() function. Even though the filesvariable contains all the selected files, it is not an array. So, in order to use the mapfunction, we are using the Array.from()syntax, which will convert the array like object into the new array copy.  The rest of the logic is pretty straight forward. And that is all that takes. Now you can test your code and check that your files are uploaded.

      Conclusion

      In this article, we have learned:
      • How to code our server-side action to handle file uploading
      • The way to create an upload component in our Angular application
      • How to use uploaded files in the Angular application
      [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5090 0 0 0 1028 0 0 1029 1028 0 1034 1029 0 1037 1034 0 1040 1037 0 1093 0 0 1094 1093 0 1095 1094 0 1096 1095 0 1123 0 0 1124 1123 0 1125 1124 0 1224 0 0 1225 var fullPath = Path.Combine(pathToSave, fileName, DateTime.Now.ToString()); ]]> 1224 0 1257 dbPaths as you proposed, and then the client side onCreate function we can assign imgPath: this.response.dbPaths.toString() (comma-delimited) and save it to DB. To complete this article in the simplest way, I load the first image where the image column has more than one file by modyfing the createImgPath function as follows: // Upload multi files. Display the first image public createImgPath = (serverPath: string) => { if(serverPath.includes(",")) { var serverPathArr = serverPath.split(','); serverPath = serverPathArr[0]; } return `https://localhost:5001/${serverPath}`; } Regards, Tam]]> 0 0 1258 1257 0 1437 1257 0 1438 1437 0 1475 0 0 1477 1475 0 1533 0 0 1535 1533 0 1569 0 0 1570 0 0 1571 1570 0 1584 0 0 1585 1584 0 1598 1585 0 1610 { if (serverss.includes(",")) { for(let i = 0;serverss.length;i++ ){ console.log(i) var serpatharr=serverss.split(','); serverss=serpatharr[0]; } return serverss } } public onCreate = () => { this.books = { name: this.name, ImageName1 : this.spliturl(this.response.dbpathsasstring) , imageName2 : this.spliturl(this.response.dbpathsasstring) }]]> 0 0 1611 1610 0
      .NET Core with SignalR and Angular – Real-Time Charts https://code-maze.com/netcore-signalr-angular/ Mon, 03 Dec 2018 07:15:06 +0000 https://code-maze.com/?p=5194 Timer class in .NET Core and use that data to change states of our Angular charts in real-time as well. For this example, we are going to use only one-way communication (from the server to the client), but we will add an additional feature to the example, to show the two-way communication as well (client-server-client). So, without further ado, let’s get started. [sc name="part_of_series" headline="Recommended Articles"] If you want to download a finished project, you can clone the repo from Real-Time Charts SignalR source code. This article is divided into the following sections:

      Creating Projects and Basic Configuration

      First thing first. Let’s create both the .NET Core and Angular projects. We are going to name them RealTimeCharts_Server and RealTimeCharts_Client respectively. For the .NET Core project, we are going to choose 3.1 version Web API empty project and for the Angular side, we are creating Angular version 7 with no routings created and CSS for the styles. To learn more about .NET Core, you can read the .NET Core Web API Tutorial. For the detailed Angular development guide, you can read Angular Tutorial. As soon as projects are created, we are going to switch to the server-side project and set up some basic configuration. To do that, let’s open the launchSettings.json file and modify it accordingly:
      {
        "$schema": "http://json.schemastore.org/launchsettings.json",
        "iisSettings": {
          "windowsAuthentication": false, 
          "anonymousAuthentication": true, 
          "iisExpress": {
            "applicationUrl": "http://localhost:60967",
            "sslPort": 44342
          }
        },
        "profiles": {
          "IIS Express": {
            "commandName": "IISExpress",
            "launchBrowser": false,
            "environmentVariables": {
              "ASPNETCORE_ENVIRONMENT": "Development"
            }
          },
          "RealTimeCharts_Server": {
            "commandName": "Project",
            "launchBrowser": false,
            "applicationUrl": "https://localhost:5001;http://localhost:5000",
            "environmentVariables": {
              "ASPNETCORE_ENVIRONMENT": "Development"
            }
          }
        }
      }
      
      Our server-side project will run on localhost:5001 and the client side will run on localhost:4200, so in order to establish communication between those two, we need to enable CORS. Let’s open the Startup.cs class and modify it:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddCors(options => 
          { 
              options.AddPolicy("CorsPolicy", builder => builder
              .WithOrigins("http://localhost:4200")
              .AllowAnyMethod()
              .AllowAnyHeader()
              .AllowCredentials()); 
          });
      
          services.AddControllers();
      }
      
      // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
      
          app.UseHttpsRedirection();
      
          app.UseRouting();
      
          app.UseCors("CorsPolicy");
      
          app.UseAuthorization();
      
          app.UseEndpoints(endpoints =>
          {
              endpoints.MapControllers();
          });
      }
      Please note that we are not using the AllowAnyOrigin() method to enable cors from any origin, but we explicitly say which origin to allow WithOrigins("http://localhost:4200"). We are doing this because in .NET Core 3.0 the combination of AllowAnyOrigin and AllowCredentials is considered as an insecure CORS configuration. For a more detailed guide about the CORS in .NET Core, you can read Enabling CORS in ASP.NET Core. One additional thing. It is important to call the UseCors method before the UseAuthorization or UseEndpoints methods. That is it regarding the configuration. Let’s move on to the next part.

      SignalR Installation, Hub and Configuration

      We need to install the SignalR library for the client side. To do that, we are going to open the Angular project in the Visual Studio Code and type the following command in the terminal window:
      npm install @aspnet/signalr –-save
      InstallingSignalR - SignalR with .NET Core and Angular That is it for now regarding the client side. Let’s switch back to the server-side project and create a new folder Models. In that folder, we are going to create a new class ChartModel and modify it:
      public class ChartModel
      {
          public List<int> Data { get; set; }
          public string Label { get; set; }
      
          public ChartModel()
          {
              Data = new List<int>();
          }
      }
      
      This form of data is expected by the Angular Charts library (which is yet to be installed), thus the model properties Data and Label. Having the model prepared, we are going to continue by creating a new folder HubConfig and inside a new class ChartHub:
      public class ChartHub: Hub
      {
              
      }
      
      As we can notice, our ChartHub class must derive from the Hub class, which is a base class for the SignalR hub. But why do we need this ChartHub? Well, a Hub is a high-level pipeline that allows communication between client and server to call each others methods directly. So basically, a Hub is a communication foundation between client and server while using SignalR. Right now our ChartHub class is empty because we don’t need any methods inside it, yet. To complete the SignalR configuration, let’s modify the Startup.cs class again:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddCors(options =>
          {
              options.AddPolicy("CorsPolicy",
                  builder => builder.WithOrigins("http://localhost:4200")
                  .AllowAnyMethod()
                  .AllowAnyHeader()
                  .AllowCredentials());
          });
      
          services.AddSignalR();
          services.AddControllers();
      }
      
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
      
          app.UseHttpsRedirection();
         
          app.UseRouting();
      
          app.UseCors("CorsPolicy");
      
          app.UseAuthorization();
      
          app.UseEndpoints(endpoints =>
          {
              endpoints.MapControllers();
              endpoints.MapHub<ChartHub>("/chart");
          });
      }
      
      In the ConfigureServices method, we are adding SignalR to the IService collection. And in a Configure method, we are adding SignalR to the request pipeline by pointing to our ChartHub with the provided /chart path.

      Timer Implementation with DataManager and ChartController

      To simulate a real-time data flow from the server, we are going to implement a Timer class from the System.Threading namespace. Let’s create a new folder TimerFeatures and inside it a new class TimerManager:
      public class TimerManager
      {
          private Timer _timer;
          private AutoResetEvent _autoResetEvent;
          private Action _action;
      
          public DateTime TimerStarted { get; }
      
          public TimerManager(Action action)
          {
              _action = action;
              _autoResetEvent = new AutoResetEvent(false);
              _timer = new Timer(Execute, _autoResetEvent, 1000, 2000);
              TimerStarted = DateTime.Now;
          }
      
          public void Execute(object stateInfo)
          {
              _action();
      
              if((DateTime.Now - TimerStarted).Seconds > 60)
              {
                  _timer.Dispose();
              }
          }
      }
      
      We are using an Action delegate to execute the passed callback function every two seconds. The timer will make a one-second pause before the first execution. Finally, we just create a sixty seconds time slot for execution, to avoid limitless timer loop. If you want to learn more about delegates and how to use them to write better C# code, you can visit Delegates in C# article. It is important to have a method that has one object parameter and returns a void result. The Timer class expects that kind of method in its constructor. After the TimeManager implementation, let's create a new folder DataStorage and inside it a new class DataManager. We are going to use this class to fake our data:
      public static class DataManager
      {
          public static List<ChartModel> GetData()
          {
              var r = new Random();
              return new List<ChartModel>()
              {
                 new ChartModel { Data = new List<int> { r.Next(1, 40) }, Label = "Data1" },          
                 new ChartModel { Data = new List<int> { r.Next(1, 40) }, Label = "Data2" },
                 new ChartModel { Data = new List<int> { r.Next(1, 40) }, Label = "Data3" }, 
                 new ChartModel { Data = new List<int> { r.Next(1, 40) }, Label = "Data4" }
              };
          }
      }
      
      Finally, to complete this section, we are going to create a new controller file ChartController inside the Controllers folder:
      [Route("api/[controller]")]
      [ApiController]
      public class ChartController : ControllerBase
      {
          private IHubContext<ChartHub> _hub;
      
          public ChartController(IHubContext<ChartHub> hub)
          {
              _hub = hub;
          }
      
          public IActionResult Get()
          {
              var timerManager = new TimerManager(() => _hub.Clients.All.SendAsync("transferchartdata", DataManager.GetData()));
      
              return Ok(new { Message = "Request Completed" });
          }
      }
      
      In this controller class, we are using the IHubContext interface to create its instance via dependency injection. By using that instance object, we are able to access and call the hub methods. This is the reason why we don’t have any method in our ChartHub class. We don’t need any yet, because we are providing just one-way communication (server is sending data to the client only), and we can access all the hub methods with IHubContext interface. Furthermore, in the Get action, we are instantiating the TimerManager class and providing a callback function as a parameter. This callback function will be executed every two seconds. Now, we have to pay attention to the _hub.Clients.All.SendAsync("transferchartdata", DataManager.GetData()) expression. With it, we are sending generated data to all subscribed clients on the transferchartdata event. This means that every client if it has a listener on the transferchartdata event, will receive a data generated by the DataManager class. And that is exactly what we are going to do in the next section.

      Angular Chart and SignalR Listener

      We have currently finished our work on the server-side, so let’s switch to the client side. To use charts in Angular, we are going to install two required libraries:
      npm install ng2-charts --save
      Installing ng2Charts - SignalR with .NET Core and Angular
      npm install chart.js --save
      Installing Chartjs As soon as we install required libraries, we are going to modify the angular.json file:
      "scripts": [
          "./node_modules/chart.js/dist/Chart.js"
      ]
      
      And finally, let’s modify the app.module.ts file:
      import { ChartsModule } from 'ng2-charts';
      import { HttpClientModule } from '@angular/common/http';
      
      imports: [
          BrowserModule,
          ChartsModule,
          HttpClientModule
        ],
      
      Of course, the HttpClientModule is not required for the charts to work, but we are going to send the HTTP request towards our server, so we need it. To continue, we are going to create a service file for the sole purpose to wrap the SignalR logic:
      ng g service services/signal-r --spec false
      Additionally, we are going to create an interface ChartModel:
      export interface ChartModel {
          data: [],
          label: string
      }
      
      Having done that, let’s modify our service file:
      import { Injectable } from '@angular/core';
      import * as signalR from "@aspnet/signalr";
      import { ChartModel } from '../_interfaces/chartmodel.model';
      
      @Injectable({
        providedIn: 'root'
      })
      export class SignalRService {
        public data: ChartModel[];
      
      private hubConnection: signalR.HubConnection
      
        public startConnection = () => {
          this.hubConnection = new signalR.HubConnectionBuilder()
                                  .withUrl('https://localhost:5001/chart')
                                  .build();
      
          this.hubConnection
            .start()
            .then(() => console.log('Connection started'))
            .catch(err => console.log('Error while starting connection: ' + err))
        }
      
        public addTransferChartDataListener = () => {
          this.hubConnection.on('transferchartdata', (data) => {
            this.data = data;
            console.log(data);
          });
        }
      }
      
      First of all, we create the data array which will hold the data fetched from the server and will provide a data source for the chart. In the startConnection function, we build and start our connection as well as logging the message in the console. Finally, we have the addTransferChartDataListener function in which we subscribe to the event transferchardata and accept the data from the server with the data parameter. If we take a look at the Get action in the ChartController file, we are going to see that we broadcast the data on the same transferchartdata event: (_hub.Clients.All.SendAsync("transferchartdata", DataManager.GetData())). And yes, those must match. All we have left to do, for now, is to modify the app.component.ts file:
      import { Component, OnInit } from '@angular/core';
      import { SignalRService } from './services/signal-r.service';
      import { HttpClient } from '@angular/common/http';
      
      @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
      })
      export class AppComponent implements OnInit {
      
        constructor(public signalRService: SignalRService, private http: HttpClient) { }
      
        ngOnInit() {
          this.signalRService.startConnection();
          this.signalRService.addTransferChartDataListener();   
          this.startHttpRequest();
        }
      
        private startHttpRequest = () => {
          this.http.get('https://localhost:5001/api/chart')
            .subscribe(res => {
              console.log(res);
            })
        }
      }
      
      This logic is straightforward. We just start the connection, add our listener, and send request towards the Get action on our server. This should be our current result:   SingalR connected - SignalR with .NET Core and Angular Excellent. We can see that our data is received in real-time and logged in the console window. Of course, this is just part of our goal, so let’s get to the finish line. To do that, let’s modify the app.component.html file:
      <div style="display: block" *ngIf='signalRService.data'>
        <canvas baseChart
                [datasets]="signalRService.data"
                [labels]="chartLabels"
                [options]="chartOptions"
                [legend]="chartLegend"
                [chartType]="chartType"
                [colors]="colors"
                (chartClick)="chartClicked($event)"></canvas>
      </div>
      
      And the app.component.ts file:
      import { Component, OnInit } from '@angular/core';
      import { SignalRService } from './services/signal-r.service';
      import { HttpClient } from '@angular/common/http';
      
      @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
      })
      export class AppComponent implements OnInit {
        public chartOptions: any = {
          scaleShowVerticalLines: true,
          responsive: true,
          scales: {
            yAxes: [{
              ticks: {
                beginAtZero: true
              }
            }]
          }
        };
        public chartLabels: string[] = ['Real time data for the chart'];
        public chartType: string = 'bar';
        public chartLegend: boolean = true;
        public colors: any[] = [{ backgroundColor: '#5491DA' }, { backgroundColor: '#E74C3C' }, { backgroundColor: '#82E0AA' }, { backgroundColor: '#E5E7E9' }]
      
        constructor(public signalRService: SignalRService, private http: HttpClient) { }
      
        ngOnInit() {
          this.signalRService.startConnection();
          this.signalRService.addTransferChartDataListener();
          this.startHttpRequest();
        }
      
        private startHttpRequest = () => {
          this.http.get('https://localhost:5001/api/chart')
            .subscribe(res => {
              console.log(res);
            })
        }
      }
      
      Now, our result should look like this: SignalR chart completed - SignalR This looks great. Our application is working as intended.

      Sending Data via SignalR from the Client to the Server and Back

      Until now, we've broadcasted data only from the server to the client (one-way communication). But what if we want to send some data from the client to the server and then to broadcast it to all the subscribed clients (all of that via SignalR)? Well, we can do that as well. So let’s imagine that we want to send the current data to some API as soon as we click on our chart, and then to display them on any other client. To cover that example, we could create another Angular app, but for the sake of simplicity, we are going to implement all of that in our current app. So, the first thing we want to do is to modify the ChartHub class in .NET Core:
      public async Task BroadcastChartData(List<ChartModel> data) => await Clients.All.SendAsync("broadcastchartdata", data);
      Because we are starting the SignalR communication from the client, we need a hub endpoint to Invoke our data to. This BroadcastChartData method will receive the message from the client and then broadcast that same message to all the clients that listen on the bradcastchratdata event. The second step is to modify the service file in Angular:
      import { Injectable } from '@angular/core';
      import * as signalR from "@aspnet/signalr";
      import { ChartModel } from '../_interfaces/chartmodel.model';
      
      @Injectable({
        providedIn: 'root'
      })
      export class SignalRService {
        public data: ChartModel[];
        public bradcastedData: ChartModel[];
      
      private hubConnection: signalR.HubConnection
      
        public startConnection = () => {
          this.hubConnection = new signalR.HubConnectionBuilder()
                                  .withUrl('https://localhost:5001/chart')
                                  .build();
      
          this.hubConnection
            .start()
            .then(() => console.log('Connection started'))
            .catch(err => console.log('Error while starting connection: ' + err))
        }
      
        public addTransferChartDataListener = () => {
          this.hubConnection.on('transferchartdata', (data) => {
            this.data = data;
            console.log(data);
          });
        }
      
        public broadcastChartData = () => {
          this.hubConnection.invoke('broadcastchartdata', this.data)
          .catch(err => console.error(err));
        }
      
        public addBroadcastChartDataListener = () => {
          this.hubConnection.on('broadcastchartdata', (data) => {
            this.bradcastedData = data;
          })
        }
      }
      
      The first function will send data to our Hub endpoint and the second function will listen on the braodcastchartdata event. The third step is to modify the app.component.ts file:
      ngOnInit() {
          this.signalRService.startConnection();
          this.signalRService.addTransferChartDataListener();
          this.signalRService.addBroadcastChartDataListener();
          this.startHttpRequest();
        }
      
        private startHttpRequest = () => {
          this.http.get('https://localhost:5001/api/chart')
            .subscribe(res => {
              console.log(res);
            })
        }
      
        public chartClicked = (event) => {
          console.log(event);
          this.signalRService.broadcastChartData();
        }
      
      And finally, let’s provide the chartClicked event for our chart:
      <div style="display: block" *ngIf='signalRService.data'>
        <canvas baseChart
                [datasets]="signalRService.data"
                [labels]="chartLabels"
                [options]="chartOptions"
                [legend]="chartLegend"
                [chartType]="chartType"
                [colors]="colors"
                (chartClick)="chartClicked($event)"></canvas>
      </div>
      <br><br>
      <ul>
        <li *ngFor='let data of signalRService.bradcastedData'>
          <strong>Label: </strong> {{data.label}} - <strong>Value: </strong> {{data.data[0]}}
        </li>
      </ul>
      
      After all of the changes, we can inspect result: SignalR - chart - two way communication Excellent work. Everything works like a charm. Of course, we can accomplish a lot more with SignalR and cover a whole load of features, but this is a good starting point for sure.

      Conclusion

      By reading this article, we could have learned:
      • How to install SignalR and prepare a basic configuration
      • The way to use Timer in .NET Core
      • How to provide a SignalR implementation on the client and server-side
      • The way to use charts to consume real-time data sent via SignalR
      [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5194 0 0 0 1160 starting at object with constructor 'Array' | index 0 -> object with constructor 'Object' | property '_meta' -> object with constructor 'Object' | ... | property 'data' -> object with constructor 'Object' --- property 'datasets' closes the circle at JSON.stringify () at JsonHubProtocol.push../node_modules/@aspnet/signalr/dist/esm/JsonHubProtocol.js.JsonHubProtocol.writeMessage (JsonHubProtocol.js:75) at HubConnection.js:259 at new ZoneAwarePromise (zone.js:931) at HubConnection.push../node_modules/@aspnet/signalr/dist/esm/HubConnection.js.HubConnection.invoke (HubConnection.js:237) at ChartService.broadcastChartData (chart.service.ts:38) at ChartComponent.chartClicked (chart.component.ts:54) at Object.eval [as handleEvent] (ChartComponent.html:4) at handleEvent (core.js:27329) at callWithDebugContext (core.js:28399)]]> 995 0 1164 0 0 833 804 0 516 http://grahams.io 0 0 517 516 0 531 0 0 532 531 0 541 532 0 610 0 0 789 http://appealforcourage.org/ 0 0 790 789 0 696 610 0 697 610 0 741 0 0 742 741 0 803 0 0 804 At Server side I am getting http 404 error At client side in developers tool I am getting this error - Utils.js:204 [2019-03-15T09:41:44.733Z] Error: Failed to complete negotiation with the server: SyntaxError: Failed to execute 'open' on 'XMLHttpRequest': Invalid URL]]> 0 0 806 803 0 826 0 0 827 826 0 834 0 0 835 834 0 845 0 0 846 845 0 863 0 0 864 863 0 868 864 0 869 833 0 870 610 0 871 868 0 927 0 0 928 0 0 929 927 0 930 928 0 931 0 0 932 931 0 933 932 0 941 0 0 943 941 0 968 0 0 969 968 0 993 ) at JsonHubProtocol.push../node_modules/@aspnet/signalr/dist/esm/JsonHubProtocol.js.JsonHubProtocol.writeMessage (JsonHubProtocol.js:75) at HubConnection.js:259 at new ZoneAwarePromise (zone.js:910) Thanks in advance for your inputs on this. Great work.]]> 0 0 994 993 0 995 994 0 1022 0 0 1023 1022 0 1041 0 0 1042 1041 0 1055 Configue method and ConfigureServices method perform vice-versa functionalities. Configure does what is been mentioned in ConfigureServices.
      In the Configure method, we are adding SignalR to the IService collection. And in a ConfigureServices method, we are adding SignalR to the request pipeline by pointing to our ChartHub with the provided /chart path.
      ]]>
      0 0
      1056 1055 0 1074 Error: Failed to complete the negotiation with the server Any suggestions?]]> 0 0 1075 1074 0 1102 0 0 1103 1102 0 1104 1102 0 1106 1103 0 1107 1106 0 1165 1164 0 1166 1165 0 1167 1166 0 1168 1167 0 1169 1168 0 1368 true). Then the framework will allow putting also AllowCredentials() and will not throw any exceptions during our app's lifetime. I got this solution from one of the comments on https://github.com/aspnet/AspNetCore/issues/4483 by @dylanvdmerwe.]]> 0 0 1370 1368 0 1371 1368 0 1425 I am facing one problem with TimeManager, my api is paging where i am only fetching 10 records per call. the problem is When I call first GET, I create a TimerManager. That TimeManager only knows about the first query params. It then executes on a timer with those parameters. When I call second GET, a second TimeManager will get created. That second manager will also start executing on a timer with the 2nd set of parameters, but it does not affect the first one at all. I am not sure whey the old values even getting executed. How can i resolve this.]]> 0 0 1426 1425 0 1427 1426 0 1428 1427 0 1549 1167 0 1550 1549 0 1572 995 0 1910 1907 0 1907 { let label1: string = "Data1" let label2: string = "Data2" let label3: string = "Data3" let label4: string = "Data4" let data1: number[] = [this.transferData[0].data[0]] let data2: number[] = [this.transferData[1].data[0]] let data3: number[] = [this.transferData[2].data[0]] let data4: number[] = [this.transferData[3].data[0]] let tmpData: ChartModel[] = [ {label: label1, data: data1}, {label: label2, data: data2}, {label: label3, data: data3}, {label: label4, data: data4}, ] this.hubConnection.invoke('broadcastchartdata', tmpData) .catch(err => console.error(err)); }]]> 1160 0
      SOLID Principles in C# - Single Responsibility Principle https://code-maze.com/single-responsibility-principle/ Mon, 24 Dec 2018 07:17:25 +0000 https://code-maze.com/?p=5414
    • Single Responsibility Principle (Current article)
    • Open/Closed Principle
    • Liskov Substitution Principle
    • Interface Segregation Principle
    • Dependency Inversion Principle
    • [sc name="part_of_series" headline="This article is part of the series"] In this article, we are going to show you, through an example, how to create a code that abides by SRP rules. We will start with the code which isn’t SRP compliant and then refactor it to be in accordance with SRP. To finish our example, we will add a bit of reusability to our code, because we don’t want to repeat ourselves while coding. To download the source code for this project, check out the Single Responsibility Principle Project Source Code. To read about other SOLID principles, check out our SOLID Principles page. This article is divided into the following sections: So, let’s start.

      Creating the Initial Project

      We are going to start with a simple console application. Imagine if we have a task to create a WorkReport feature that, once created, can be saved to a file and perhaps uploaded to the cloud or used for some other purpose. So we are going to start with a simple model class:
      public class WorkReportEntry
      {
          public string ProjectCode { get; set; }
          public string ProjectName { get; set; }
          public int SpentHours { get; set; }
      }
      
      Next step is creating a WorkReport class which will handle all the required features for our project:
      public class WorkReport
      {
          private readonly List<WorkReportEntry> _entries;
      
          public WorkReport()
          {
              _entries = new List<WorkReportEntry>();
          }
      
          public void AddEntry(WorkReportEntry entry) => _entries.Add(entry);
      
          public void RemoveEntryAt(int index) => _entries.RemoveAt(index);
      
          public override string ToString() =>
              string.Join(Environment.NewLine, _entries.Select(x => $"Code: {x.ProjectCode}, Name: {x.ProjectName}, Hours: {x.SpentHours}"));
      }
      
      In this class, we are keeping track of our work report entries by adding and removing them to/from a list. Furthermore, we are just overriding ToString() method to adjust it to our requirements. Because we have our WorkReport class, it is quite fine to add our additional features to it, like saving to a file:
      public class WorkReport
      {
          private readonly List<WorkReportEntry> _entries;
      
          public WorkReport()
          {
              _entries = new List<WorkReportEntry>();
          }
      
          public void AddEntry(WorkReportEntry entry) => _entries.Add(entry);
      
          public void RemoveEntryAt(int index) => _entries.RemoveAt(index);
      
          public void SaveToFile(string directoryPath, string fileName)
          {
              if(!Directory.Exists(directoryPath))
              {
                  Directory.CreateDirectory(directoryPath);
              }
              
              File.WriteAllText(Path.Combine(directoryPath, fileName), ToString());
          }
      
          public override string ToString() =>
              string.Join(Environment.NewLine, _entries.Select(x => $"Code: {x.ProjectCode}, Name: {x.ProjectName}, Hours: {x.SpentHours}"));
      }
      

      Problems With This Code

      We can add even more features in this class, like the Load or UploadToCloud methods because they are all related to our WorkReport, but, just because we can doesn’t mean we have to do it. Right now, there is one issue with the WorkReport class. It has more than one responsibility. Its job is not only to keep track of our work report entries but to save the entire work report to a file. This means that we are violating the SRP and our class has more than one reason to change in the future. The first reason to change this class is if we want to modify the way we keep track of our entries. But if we want to save a file in a different way, that is entirely a new reason to change our class. And imagine what this class would look like if we added additional functionalities to it. We would have so many unrelated code parts in a single class. So, in order to avoid that, let’s refactor the code.

      Refactoring Towards SRP

      The first thing we need to do is to separate the part of our code that is unlike others. In our case, that  is obviously the SaveToFile method, so we are going to move it to another class which is more appropriate:
      public class FileSaver
      {
          public void SaveToFile(string directoryPath, string fileName, WorkReport report)
          {
              if (!Directory.Exists(directoryPath))
              {
                  Directory.CreateDirectory(directoryPath);
              }
      
                  File.WriteAllText(Path.Combine(directoryPath, fileName), report.ToString());
              }
          }
      }
      
      public class WorkReport
      {
          private readonly List<WorkReportEntry> _entries;
      
          public WorkReport()
          {
              _entries = new List<WorkReportEntry>();
          }
      
          public void AddEntry(WorkReportEntry entry) => _entries.Add(entry);
      
          public void RemoveEntryAt(int index) => _entries.RemoveAt(index);
      
          public override string ToString() =>
              string.Join(Environment.NewLine, _entries.Select(x => $"Code: {x.ProjectCode}, Name: {x.ProjectName}, Hours: {x.SpentHours}"));
      }
      
      In this case, we have separated our responsibilities in two classes. The WorkReport class is now responsible for keeping track of work report entries and the FileSaver class is responsible for saving a file. Having done this, we have separated the concerns of each class thus making them more readable and maintainable as well. As a result, if we want to change how we save a file, we only have one reason to do that and one place to do it, which is the FileSaver class. We can check that everything is working as it supposed to do:
      class Program
      {
          static void Main(string[] args)
          {
              var report = new WorkReport();
              report.AddEntry(new WorkReportEntry { ProjectCode = "123Ds", ProjectName = "Project1", SpentHours = 5 });
              report.AddEntry(new WorkReportEntry { ProjectCode = "987Fc", ProjectName = "Project2", SpentHours = 3 });
      
              Console.WriteLine(report.ToString());
      
              var saver = new FileSaver();
              saver.SaveToFile(@"Reports", "WorkReport.txt", report);
          }
      }
      
      SRP finished example - Single Responsibility Principle

      Making the Code Even Better

      If we look at our SaveToFile method, we see that it does its job which is saving a work report to a file, but can it do it even better? This method is tightly coupled with the WorkReport class, but what if we want to create a Scheduler class that keeps track of its scheduled tasks? We would still like to save it to a file. Well, in that case, we are going to create some changes to our code:
      public interface IEntryManager<T>
      {
          void AddEntry(T entry);
          void RemoveEntryAt(int index);
      }
      
      The only change to the WorkReport class is to implement this interface:
      public class WorkReport: IEntryManager<WorkReportEntry>
      Finally, we have to change the SaveToFile method signature:
      public void SaveToFile<T>(string directoryPath, string fileName, IEntryManager<T> workReport)
      After these modifications, we are going to have the same result, but now if we have a task to implement Scheduler, it is going to be quite simple to implement that:
      public class ScheduleTask
      {
          public int TaskId { get; set; }
          public string Content { get; set; }
          public DateTime ExecuteOn { get; set; }
      }
      
      public class Scheduler : IEntryManager<ScheduleTask>
      {
          private readonly List<ScheduleTask> _scheduleTasks;
      
          public Scheduler()
          {
              _scheduleTasks = new List<ScheduleTask>();
          }
      
          public void AddEntry(ScheduleTask entry) => _scheduleTasks.Add(entry);
      
          public void RemoveEntryAt(int index) => _scheduleTasks.RemoveAt(index);
      
          public override string ToString() => 
              string.Join(Environment.NewLine, _scheduleTasks.Select(x => $"Task with id: {x.TaskId} with content: {x.Content} is going to be executed on: {x.ExecuteOn}"));
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              var report = new WorkReport();
              report.AddEntry(new WorkReportEntry { ProjectCode = "123Ds", ProjectName = "Project1", SpentHours = 5 });
              report.AddEntry(new WorkReportEntry { ProjectCode = "987Fc", ProjectName = "Project2", SpentHours = 3 });
      
              var scheduler = new Scheduler();
              scheduler.AddEntry(new ScheduleTask { TaskId = 1, Content = "Do something now.", ExecuteOn = DateTime.Now.AddDays(5) });
              scheduler.AddEntry(new ScheduleTask { TaskId = 2, Content = "Don't forget to...", ExecuteOn = DateTime.Now.AddDays(2) });
      
              Console.WriteLine(report.ToString());
              Console.WriteLine(scheduler.ToString());
      
              var saver = new FileSaver();
              saver.SaveToFile(@"Reports", "WorkReport.txt", report);
              saver.SaveToFile(@"Schedulers", "Schedule.txt", scheduler);
          }
      }
      
      After we execute this code, we will have our file saved in a required location on a defined schedule. We are going to leave it at that. Now every class we have is responsible for one thing and one thing only.

      Benefits of Single Responsibility Principle

      Our code has improved in several ways by implementing SRP. The first one being that it has become less complex. Because we are trying to accomplish only one task in our class, they have become free of clutter and simple to read. As we reduce code complexity, our code becomes readable and therefore maintainable. As we could see from our example, if our class does its job well, we can reuse its logic in a project. Furthermore, with such a code, testing becomes easier as well. When we implement SRP in our code, our methods become highly related (coherent). It means that different methods are joined to do one thing and to do it well. Finally, our classes are less dependent on each other (decoupled) which is one of the most important things to achieve while working on a project.

      Potential Downsides of SRP

      There is no strict rule which states what is that „one reason to change“ in our class. Everyone interprets this subjectively or rather how he/she feels it should be implemented. The rules are not clear to where we should draw the line, so we can potentially find different „right ways“ to implement the same feature. But still, the bottom line is that no matter what someone thinks about what reason to change is, we should all strive to write readable and maintainable code thus implementing Single Responsibility Principle in our own way. One of the potential downsides is that in projects that are already written, is difficult to implement SRP. We don’t say that it is not possible, just that it will take longer and take more resources as well. Implementing SRP leads to writing compact classes with tiny methods as well. And on a first look, this looks great. But having one big class decomposed into a lot of small classes creates an organizational risk. If those classes are not organized and grouped well, it could actually increase the amount of work needed to change a system and to understand it which is opposite of what we wanted to achieve in the first place.

      Conclusion

      Implementing the Single Responsibility Principle should be always in our mind while writing code. It can be tough to write the code according to SRP right from scratch, but you can write your code iteratively and return to the parts that need attention later. Refactoring is a common practice and nobody writes code perfectly right away. So refactor towards the SRP later if you are not sure which class does what at that moment. It will help not only you but the other developers that need to maintain your code later as well. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5414 0 0 0 545 0 0 546 545 0 750 Benefits of Single Responsibility Principle topic , 3rd paragraph there is a typo for SRP. Its mispelled SPR instead of SRP.]]> 0 0 751 750 0 794 546 0 795 0 0 796 795 0 1422 ? I'd keep interfaces out of SRP, if we want to respect the definition. Interface Segregation Principle (ISP) is about interfaces, not SRP, in my opinion.]]> 0 0
      SOLID Principles in C# - Open Closed Principle https://code-maze.com/open-closed-principle/ Mon, 31 Dec 2018 07:00:25 +0000 https://code-maze.com/?p=5436 OCP) is the SOLID principle which states that the software entities (classes or methods) should be open for extension but closed for modification. But what does this really mean? Basically, we should strive to write a code that doesn’t require modification every time a customer changes its request. Providing such a solution where we can extend the behavior of a class (with that additional customer's request) and not modify that class, should be our goal most of the time. [sc name="part_of_series" headline="This article is part of the series"] In this article, we will show you how to write the code by following the Open Closed Principle with two different examples. Initially, none of the examples will obey the OCP rules, but right after the initial development, we are going to refactor the code using the OCP. To download the source code for this project, check out the Open Closed Principle Project Source Code. To read about other SOLID principles, check out our SOLID Principles page. This article is divided into the following sections: So, let’s jump right into it.

      Salary Calculator Example

      Let’s imagine that we have a task where we need to calculate the total cost of all the developer salaries in a single company. Of course, we are going to make this example simple and focus on the required topic. To get started, we are going to create the model class first:
      public class DeveloperReport
      {
          public int Id { get; set; }
          public string Name { get; set; }
          public string Level { get; set; }
          public int WorkingHours { get; set; }
          public double HourlyRate { get; set; }
      }
      
      Once we’ve created our model, we can transition to the salary calculation feature:
      public class SalaryCalculator
      {
          private readonly IEnumerable<DeveloperReport> _developerReports;
      
          public SalaryCalculator(List <DeveloperReport> developerReports)
          {
              _developerReports = developerReports;
          }
      
          public double CalculateTotalSalaries()
          {
              double totalSalaries = 0D ;
      
              foreach (var devReport in _developerReports)
              {
                  totalSalaries += devReport.HourlyRate * devReport.WorkingHours;
              }
      
              return totalSalaries;
          }
      }
      
      Now, all we have to do is to provide some data for this class and we are going to have our total costs calculated:
      static void Main(string[] args)
      {
          var devReports = new List<DeveloperReport>
          {
              new DeveloperReport {Id = 1, Name = "Dev1", Level = "Senior developer", HourlyRate  = 30.5, WorkingHours = 160 },
              new DeveloperReport {Id = 2, Name = "Dev2", Level = "Junior developer", HourlyRate  = 20, WorkingHours = 150 },
              new DeveloperReport {Id = 3, Name = "Dev3", Level = "Senior developer", HourlyRate  = 30.5, WorkingHours = 180 }
          };
      
          var calculator = new SalaryCalculator(devReports);
          Console.WriteLine($"Sum of all the developer salaries is {calculator.CalculateTotalSalaries()} dollars");
      }
      
      Our result should be: Open Closed Principle first example So, all of this is working great, but now our boss comes to our office and says that we need a different calculation for the senior and junior developers. The senior developers should have a bonus of 20% on a salary. Of course, to satisfy this requirement, we are going to modify our CalculateTotalSalaries method like this:
      public double CalculateTotalSalaries()
      {
          double totalSalaries = 0D;
      
          foreach (var devReport in _developerReports)
          {
              if(devReport.Level == "Senior developer")
              {
                  totalSalaries += devReport.HourRate * devReport.WorkingHours * 1.2;
              }
              else
              {
                  totalSalaries += devReport.HourRate * devReport.WorkingHours;
              }
          }
      
          return totalSalaries;
      }
      
      Even though this solution is going to give us the correct result, this is not an optimal solution. Why is that? Mainly, because we had to modify our existing class behavior which worked perfectly. Another thing is that if our boss comes again and ask us to modify calculation for the junior dev’s as well, we would have to change our class again. This is totally against of what OCP stands for. It is obvious that we need to change something in our solution, so, let’s do it.

      Better Salary Calculator Example – OCP implemented

      To create a code that abides by the Open Closed Principle, we are going to create an abstract class first:
      public abstract class BaseSalaryCalculator
      {
          protected DeveloperReport DeveloperReport { get; private set; }
      
          public BaseSalaryCalculator(DeveloperReport developerReport)
          {
              DeveloperReport = developerReport;
          }
      
          public abstract double CalculateSalary();
      }
      
      As a continuation, we are going to create two classes which will inherit from the BaseSalaryCalculator class. Because it is obvious that our calculation depends on the developer’s level, we are going to create our new classes in that manner:
      public class SeniorDevSalaryCalculator : BaseSalaryCalculator
      {
          public SeniorDevSalaryCalculator(DeveloperReport report)
              :base(report)
          {
          }
      
          public override double CalculateSalary() => DeveloperReport.HourlyRate * DeveloperReport.WorkingHours * 1.2;
      }
      
      public class JuniorDevSalaryCalculator : BaseSalaryCalculator
      {
          public JuniorDevSalaryCalculator(DeveloperReport developerReport)
              :base(developerReport)
          {
          }
      
          public override double CalculateSalary() => DeveloperReport.HourlyRate * DeveloperReport.WorkingHours;
      } 
      
      Excellent. Now we can modify the SalaryCalculator class:
      public class SalaryCalculator
      {
          private readonly IEnumerable<BaseSalaryCalculator> _developerCalculation;
      
          public SalaryCalculator(IEnumerable<BaseSalaryCalculator> developerCalculation)
          {
              _developerCalculation = developerCalculation;
          }
      
          public double CalculateTotalSalaries()
          {
              double totalSalaries = 0D;
      
              foreach (var devCalc in _developerCalculation)
              {
                  totalSalaries += devCalc.CalculateSalary();
              }
      
              return totalSalaries;
          }
      }
      
      This looks so much better because we won’t have to change any of our current classes if our boss comes with another request about the intern payment calculation or any other as well. All we have to do now is to add another class with its own calculation logic. So basically, our SalaryCalculator class is now closed for modification and opened for an extension, which is exactly what OCP states. To finish this example, let’s modify the Program.cs class:
      class Program
      {
          static void Main(string[] args)
          {
              var devCalculations = new List<BaseSalaryCalculator>
              {
                  new SeniorDevSalaryCalculator(new DeveloperReport {Id = 1, Name = "Dev1", Level = "Senior developer", HourlyRate = 30.5, WorkingHours = 160 }),
                  new JuniorDevSalaryCalculator(new DeveloperReport {Id = 2, Name = "Dev2", Level = "Junior developer", HourlyRate = 20, WorkingHours = 150 }),
                  new SeniorDevSalaryCalculator(new DeveloperReport {Id = 3, Name = "Dev3", Level = "Senior developer", HourlyRate = 30.5, WorkingHours = 180 })
              };
      
              var calculator = new SalaryCalculator(devCalculations);
              Console.WriteLine($"Sum of all the developer salaries is {calculator.CalculateTotalSalaries()} dollars");
          }
      } 
      
      Awesome. We have finished our first example. Let’s start with another one.

      Filtering Computer Monitors Example

      Let’s imagine for a moment that we have a task to write an application which gives us all the required information about computer monitors in our shop, based on different criteria. We will introduce only two criteria here, the type of monitors and the screen size. So let’s start with that:
      public enum MonitorType
      {
          OLED,
          LCD,
          LED
      }
      
      public enum Screen
      {
          WideScreen,
          CurvedScreen
      }
      
      To continue, we are going to create a simple model class:
      public class ComputerMonitor
      {
          public string Name { get; set; }
          public MonitorType Type { get; set; }
          public Screen Screen { get; set; }
      }
      
      Now, we need to implement our filtering functionality. For example, we want to filter by the monitor types:
      public class MonitorFilter
      {
          public List<ComputerMonitor> FilterByType(IEnumerable<ComputerMonitor> monitors, MonitorType type) =>
                  monitors.Where(m => m.Type == type).ToList();
      }
      
      And finally the Program.cs class:
      class Program
      {
          static void Main(string[] args)
          {
              var monitors = new List<ComputerMonitor>
              {
                  new ComputerMonitor { Name = "Samsung S345", Screen = Screen.CurvedScreen, Type = MonitorType.OLED },
                  new ComputerMonitor { Name = "Philips P532", Screen = Screen.WideScreen, Type = MonitorType.LCD },
                  new ComputerMonitor { Name = "LG L888", Screen = Screen.WideScreen, Type = MonitorType.LED },
                  new ComputerMonitor { Name = "Samsung S999", Screen = Screen.WideScreen, Type = MonitorType.OLED },
                  new ComputerMonitor { Name = "Dell D2J47", Screen = Screen.CurvedScreen, Type = MonitorType.LCD }        
              };
      
              var filter = new MonitorFilter();
      
              var lcdMonitors = filter.FilterByType(monitors, MonitorType.LCD);
              Console.WriteLine("All LCD monitors");
              foreach (var monitor in lcdMonitors)
              {
                  Console.WriteLine($"Name: {monitor.Name}, Type: {monitor.Type}, Screen: {monitor.Screen}");
              }
          }
       }
      
      This is going to work just fine. But, after a couple of days, we receive a request that our customers want to have the filter by Screen functionality as well. So this should be quite simple, shouldn’t it? Let’s just change the MonitorFilter class:
      public class MonitorFilter
      {
          public List<ComputerMonitor> FilterByType(IEnumerable<ComputerMonitor> monitors, MonitorType type) =>
              monitors.Where(m => m.Type == type).ToList();
      
          public List<ComputerMonitor> FilterByScreen(IEnumerable<ComputerMonitor> monitors, Screen screen) =>
              monitors.Where(m => m.Screen == screen).ToList();
      }
      
      Even though this is going to give us the correct result, we have a problem because we have to modify our existing class. And what if we receive another request to filter all the monitors by type and screen together? We all see where this lead us, towards breaking the OCP. We are not extending our MonitorFilter class but modifying it. So, in order to avoid existing class modification, let’s try another approach. Creating a couple of interfaces is going to be our first step:
      public interface ISpecification<T>
      {
          bool isSatisfied(T item);
      }
      
      public interface IFilter<T>
      {
          List<T> Filter(IEnumerable<T> monitors, ISpecification<T> specification);
      }
      
      With the ISpecification interface, we can determine whether or not our criterion is satisfied and we can send it to the Filter method from the IFilter interface. To continue on, we are going to create a separate class for the monitor type specification:
      public class MonitorTypeSpecification: ISpecification<ComputerMonitor>
      {
          private readonly MonitorType _type;
      
          public MonitorTypeSpecification(MonitorType type)
          {
              _type = type;
          }
      
          public bool isSatisfied(ComputerMonitor item) => item.Type == _type;
      }
      
      After this modification, all we have to do is to write a class that implements IFilter interface. But because we already have the MonitorFilter class, we are just going to modify it:
      public class MonitorFilter : IFilter<ComputerMonitor>
      {
          public List<ComputerMonitor> Filter(IEnumerable<ComputerMonitor> monitors, ISpecification<ComputerMonitor> specification) =>
              monitors.Where(m => specification.isSatisfied(m)).ToList();
      }
      
      Finally, let’s modify the Program.cs class:
      var filter = new MonitorFilter();
      
      var lcdMonitors = filter.Filter(monitors, new MonitorTypeSpecification(MonitorType.LCD));
      Console.WriteLine("All LCD monitors");
      foreach (var monitor in lcdMonitors)
      {
          Console.WriteLine($"Name: {monitor.Name}, Type: {monitor.Type}, Screen: {monitor.Screen}");
      }
      
      The result should be the same: Open Closed Principle second example

      Additional Filter Requests

      Right now, we are perfectly able to extend our MonitorFilter class without any further modification. So, if now we have to implement the filter by screen feature, for example only widescreen monitors, we can do it with a new class:
      public class ScreenSpecification : ISpecification<ComputerMonitor>
      {
          private readonly Screen _screen;
      
          public ScreenSpecification(Screen screen)
          {
              _screen = screen;
          }
      
          public bool isSatisfied(ComputerMonitor item) => item.Screen == _screen;
      }
      
      And, we can make a call towards the MonitorFilter class:
      Console.WriteLine("All WideScreen Monitors");
      var wideScreenMonitors = filter.Filter(monitors, new ScreenSpecification(Screen.WideScreen));
      foreach (var monitor in wideScreenMonitors)
      {
           Console.WriteLine($"Name: {monitor.Name}, Type: {monitor.Type}, Screen: {monitor.Screen}");
      }
      
      Excellent. With this project structure, we can even extend our filtering criterion to, for example, only OLED and widescreen monitors. All we have to do is to create another specification class.

      Why Should We Implement the Open Closed Principle

      By implementing the OCP we are lowering the chance of producing bugs in our project. For example, if we have a fully working and already tested class in production, by extending it instead of changing it, we would definitely have a lesser impact on the rest of the system. Therefore, we introduce another class to extend the behavior of the main class thus avoid the existing functionality modification that other classes may rely upon. Another benefit is that we only have to test and deploy the new features, which wouldn’t be the case if we had to change existing functionality. Furthermore, if we decide that we don’t need this feature anymore (sometime in the future), all we have to do is to revert just newly implemented change and that’s it.

      Conclusion

      We’ve seen how the OCP can help us create better and more maintainable code. But, as with everything else, we should be cautious when implementing this principle. Sometimes it’s just impossible to extend our class and all we are left to do is to modify existing functionality. We shouldn’t be afraid to do it, it is quite normal, but at least we should try to make those changes as discrete as they can be. So, we should develop our applications with the OCP in mind and we should strive to write extendable code as much as we can because it leads to the maintainable, scalable and testable codebase. And that’s what we want, isn’t it? [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5436 0 0 0 556 0 0 552 https://dinobansigan.com 0 0 553 552 0 554 0 0 555 554 0 1108 0 0 1109 1108 0 1541 http://www.fcakiroglu.com 0 0 1542 1541 0 1898 0 0 1899 1898 0
      SOLID Principles in C# - Liskov Substitution Principle https://code-maze.com/liskov-substitution-principle/ Mon, 07 Jan 2019 07:00:52 +0000 https://code-maze.com/?p=5476
    • Single Responsibility Principle
    • Open/Closed Principle
    • Liskov Substitution Principle (Current article)
    • Interface Segregation Principle
    • Dependency Inversion Principle
    • [sc name="part_of_series" headline="This article is part of the series"] To download the source code for this project, check out the Liskov Substitution Principle Project Source Code. To read about other SOLID principles, check out our SOLID Principles page. This article is divided into the following sections:

      Initial Project

      In this example, we are going to have an array of numbers and a base functionality to sum all the numbers from that array. But let’s say we need to sum just even or just odd numbers. How would we implement that? Let’s see one way to do it:
      public class SumCalculator
      {
          protected readonly int[] _numbers;
      
          public SumCalculator(int[] numbers)
          {
              _numbers = numbers;
          }
      
          public int Calculate() => _numbers.Sum();
      }
      
      public class EvenNumbersSumCalculator: SumCalculator
      {
          public EvenNumbersSumCalculator(int[] numbers)
              :base(numbers)
          {
          }
      
          public new int Calculate() => _numbers.Where(x => x % 2 == 0).Sum();
      }
      
      Now if we test this solution, whether we calculate the sum of all the numbers or the sum of just even numbers, we are going to get the correct result for sure:
      class Program
      {
          static void Main(string[] args)
          {
              var numbers = new int[] { 5, 7, 9, 8, 1, 6, 4 };
      
              SumCalculator sum = new SumCalculator(numbers);
              Console.WriteLine($"The sum of all the numbers: {sum.Calculate()}");
      
              Console.WriteLine();
      
              EvenNumbersSumCalculator evenSum = new EvenNumbersSumCalculator(numbers);
              Console.WriteLine($"The sum of all the even numbers: {evenSum.Calculate()}");
          }
      }
      
      The result is: LSP not implemented - Liskov Substitution Principle

      Creating a Better Solution

      As we can see, this is working just fine. But what is wrong with this solution then? Why are we trying to fix it? Well, as we all know, if a child class inherits from a parent class, then the child class is a parent class. Having that in mind, we should be able to store a reference to an EvenNumbersSumCalculator as a SumCalculator variable and nothing should change. So, let’s check that out:
      SumCalculator evenSum = new EvenNumbersSumCalculator(numbers);
      Console.WriteLine($"The sum of all the even numbers: {evenSum.Calculate()}");
      
      Wrong result - Liskov Substitution Principle As we can see, we are not getting the expected result because our variable evenSum is of type SumCalculator which is a higher order class (a base class). This means that the Count method from the SumCalculator will be executed. So, this is not right, obviously, because our child class is not behaving as a substitute for the parent class. Luckily, the solution is quite simple. All we have to do is to implement small modifications to both of our classes:
      public class SumCalculator
      {
          protected readonly int[] _numbers;
      
          public SumCalculator(int[] numbers)
          {
              _numbers = numbers;
          }
      
          public virtual int Calculate() => _numbers.Sum();
      }
      
      public class EvenNumbersSumCalculator: SumCalculator
      {
          public EvenNumbersSumCalculator(int[] numbers)
              :base(numbers)
          {
          }
      
          public override int Calculate() => _numbers.Where(x => x % 2 == 0).Sum();
      }
      
      As a result, when we start our solution, everything works as expected and the sum of even numbers is 18 again. So, let’s explain this behavior.  If we have a child object reference stored in a parent object variable and call the Calculate method, the compiler will use the Calculate method of the parent class. But right now because the Calculate method is defined as „virtual“ and is overridden in the child class, that method in the child class will be used instead.

      Implementing the Liskov Substitution Principle

      Still, the behavior of our derived class has changed and it can't replace the base class. So we need to upgrade this solution by introducing the Calculator abstract class:
      public abstract class Calculator
      {
          protected readonly int[] _numbers;
      
          public Calculator(int[] numbers)
          {
              _numbers = numbers;
          }
      
          public abstract int Calculate();
      }
      Then we have to change our other classes:
      public class SumCalculator : Calculator
      {
          public SumCalculator(int[] numbers)
              :base(numbers)
          {
          }
      
          public override int Calculate() => _numbers.Sum();
      }
      public class EvenNumbersSumCalculator: Calculator
      {
          public EvenNumbersSumCalculator(int[] numbers)
             :base(numbers)
          {
          }
      
          public override int Calculate() => _numbers.Where(x => x % 2 == 0).Sum();
      }
      Excellent. Now we can start making calls towards these classes:
       class Program
      {
          static void Main(string[] args)
          {
              var numbers = new int[] { 5, 7, 9, 8, 1, 6, 4 };
      
              Calculator sum = new SumCalculator(numbers);
              Console.WriteLine($"The sum of all the numbers: {sum.Calculate()}");
      
              Console.WriteLine();
      
              Calculator evenSum = new EvenNumbersSumCalculator(numbers);
              Console.WriteLine($"The sum of all the even numbers: {evenSum.Calculate()}");
          }
      }
      We will again have the same result, 40 for all the numbers and 18 for the even numbers. But now, we can see that we can store any subclass reference into a base class variable and the behavior won't change which is the goal of LSP.

      What We Gain By Implementing the LSP

      By implementing the LSP, we are keeping our functionality intact and still having our subclasses act as a substitute to a base class. Also, we encourage the code reusability by implementing the LCP and having better project maintenance as well.

      Conclusion

      We can see that implementing the LSP is not that complicated but just the opposite. Most of us probably already implemented this principle many times in our code without knowing its name because in the object-oriented world Polymorphism is quite a big thing. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5476 0 0 0 841 0 0 660 http://www.ericbackhage.net public static void VerifySum(SumCalculator c) { if (c.Sum() != 40) throw new ApplicationException($"Invalid sum: {sum}"); } This will pass for a SumCalculator but fail for an EvenSumCalculator, hence the EvenSumCalculator still violates LSP. In order to fix this you would need to create a completely abstract base class without any default implementation of Sum. I would suggest reading Uncle Bob's book Agile Principles, Patterns, and Practices in C# for a good example on this topic.]]> 0 0 663 660 0
      Using C# and DalSoft.RestClient to Consume Any REST API https://code-maze.com/dalsoft-restclient-consume-any-rest-api/ Thu, 10 Jan 2019 18:26:43 +0000 https://code-maze.com/?p=5492 JSONPlaceholder. We are using JSONPlaceholder because it's a great tool for testing REST clients without requiring registration or standing up your own REST API.

      What is DalSoft.RestClient?

      From the DalSoft.RestClient website:
      A beautiful, dynamic and fluent C# RestClient. Create frictionless code whilst still using the HttpClient you already know.
      But what does that mean? Dynamic and fluent: DalSoft.RestClient is inspired by how easy it is to consume REST API's in other languages such as JavaScript and Python, but that doesn't mean you can't use it like any other library you just have more flexibility. DalSoft.RestClient uses a really easy to remember fluent looking convention to access REST API's, which allows you to interchange dynamic and strongly-typed objects easily. Frictionless code: DalSoft.RestClient reduces the amount of ceremony and boilerplate code required to consume a REST API, most REST API calls can be done in one line of code. The resulting code is easier to read as it doesn't overuse generics, just cast objects exactly how you would normally. Using the HttpClient you already know: One of the strengths of DalSoft.RestClient is it doesn't overly abstract away HttpClient you can still easily access the HttpResponseMessage. All extensibility is via DelegatingHandlers meaning everything you can do with HttpClient today you can do with DalSoft.RestClient, and better still any handlers you created for HttpClient work with DalSoft.RestClient with no code changes whatsoever. Lastly DalSoft.RestClient is developed against .NET Standard 2.0 and is fully Cross Platform. DalSoft.RestClient works and is tested on Windows, Linux, and MacOS. Xamarin is fully supported (iOS, Android and UWP). You can use DalSoft.RestClient whether you are using .NET Core or Full framework such as .NET 4.6.

      Setting up DalSoft.RestClient

      You can add DalSoft.RestClient to your project by typing Install-Package DalSoft.RestClient in your package manager console, or by using .NET CLI: dotnet add package DalSoft.RestClient.

      What does DalSoft.RestClient Offer?

      DalSoft.RestClient is biased for creating minimalist code to access any REST/HTTP API that returns JSON – if that's what you need you can get going with two lines of code! For example, perform a HTTP GET on https://jsonplaceholder.typicode.com/users/1:
      dynamic client = new RestClient("https://jsonplaceholder.typicode.com");  
      var response = await client.Users(1).Get();
      View Live Example But that's not all, because you extend DalSoft.RestClient using standard HttpClient DelegatingHandlers you can do anything with DalSoft.RestClient that you can with HttpClient. Here's an example of how you would POST application/x-www-form-urlencoded data by configuring DalSoft.RestClient to use a DelegatingHandler.
      var config = new Config().UseFormUrlEncodedHandler();  
        
      dynamic restClient = new RestClient("https://jsonplaceholder.typicode.com", config);  
      var user = new User { name="foo", email="foo@bar.com" };  
        
      //POST name=foo&email=foo@bar.com  https://jsonplaceholder.typicode.com/users/1  
      var result = await client  
            .Headers(new { ContentType = "application/x-www-form-urlencoded" })  
            .User(1)  
            .Post(user);
      All responses are awaitable and you can cast as a strongly-typed response or just leave the response as a dynamic object if you prefer it that way. DalSoft.RestClient with DelegatingHandlers works well with strings, bytes, streams or any other media type. Using DalSoft.RestClient you can work directly with the HttpResponseMessage or call ToString() to get the body as a string if you prefer.

      Understanding the DalSoft.RestClient convention

      DalSoft.RestClient is built using a Convention over Configuration approach, generally to get started with a REST API that returns JSON you new up the REST Client and provide a base URL for your request like so: dynamic restClient = new RestClient("https://jsonplaceholder.typicode.com"); This says that for every request using restClient start with the request with the URL https://jsonplaceholder.typicode.com Next, we use a dynamic object to express how we want to access a URL like so: restClient.Users(1); Let's break it down, this is saying use the base URL then add Users/1 to the URL, so you end up with https://jsonplaceholder.typicode.com/Users/1 Finally, we need to tell DalSoft.RestClient what HTTP verb to use like so (for example GET or POST): var response = restClient.Users(1).Get(); This will perform a GET on https://jsonplaceholder.typicode.com/Users/1 the accept and content type headers will be set for JSON by default. It returns a dynamic object that you can access right away – for example, response.name will return the value for the name (if it's in the JSON of the response returned). Optionally you can cast the dynamic response object to a strongly-typed object, more on this later. What about the convention for POST, PUT and PATCH? var user = new { name="foo", email="foo@bar.com" }; var result = client.User(1).Put(user); Breaking this down the URL is created the same way, but in the HTTP Verb, we pass an object that we want to submit to the REST API, in this case, a user. The object you pass to Post, Put and Patch can be an anonymous object or a strongly-typed object. That's it pretty simple huh! However… Using dynamic objects isn't to everyone's taste, and what if you can't express the URL in C# syntax, simple just express it as a string like so: var response = restClient.Resource("String-To-Add-To-The-BaseURL-Here").Resource("Can_Be_Chained_Too").Get();

      HttpResponseMessage and body ToString()

      It's useful to be able to access the HttpResponseMessage directly, this is simple using DalSoft.RestClient, you can either call .HttpResponseMessage or cast the response as HttpResponseMessage.
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com");  
        
      var result = await client.Users.Get(1);  
        
      var statusCode = result.HttpResponseMessage.StatusCode; //.HttpResponseMessage  
      HttpResponseMessage httpResponseMessage = result.HttpResponseMessage; // Cast as HttpResponseMessage
      View Live Example Want to get the response body as a string no problem DalSoft.RestClient has got you covered, just call .ToString() on the response.
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com");  
        
      var response = await client.Users.Get(1);  
      var responseAsString = response.ToString();
      View Live Example

      Query Strings

      DalSoft.RestClient has a neat way to add a Query String – just append the QueryString method to your URL chain passing an anonymous object representing your QueryString:
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com");  
        
      //http://jsonplaceholder.typicode.com/users?id=2  
      await client.Users.Query(new { id = 2 }).Get();

      Headers

      Now you know a bit about the conventions used by DalSoft.RestClient, how do you think you add headers? You guessed it by appending the Headers method to your URL chain and passing an anonymous object. You can also pass a strongly-type Header object or Dictionary. Example anonymous object:
      dynamic client = new RestClient("http://headers.jsontest.com/");  
        
      await client  
              .Headers(new { Accept = "text/html" })  
              .Get();
      Example Headers object:
      dynamic client = new RestClient("http://headers.jsontest.com/");  
        
      await client  
              .Headers(new Headers { { "Accept", "text/html" } })  
              .Get(1);
      But what if you wanted to add the same header to every request? Simple pass the Headers object to the DalSoft.RestClient constructor:
      dynamic client = new RestClient("http://headers.jsontest.com",  
                                        new Headers (new { Accept = "text/html" })
      View Live Example

      How to Write Get, Post, Put/Patch, and Delete Requests

      Let's see how easy it is to access a REST API using DalSoft.RestClient! This is where DalSoft.RestClient really shines. Fluent syntax, combined with the asynchronous execution and readability to match. Once you understand the convention writing requests is really easy, and it produces easy to read code! Example consume a REST API using read-only verbs GET, DELETE and HEAD:
      //Perform a HTTP actions on https://jsonplaceholder.typicode.com/users/1  
      dynamic client = new RestClient("https://jsonplaceholder.typicode.com");  
        
      var getUserResult = await client.Users(1).Get();  
      var deleteUserResult = await client.Users(1).Delete();  
      await client.Users(1).Head();
      View Live Example Example POST to a REST API:
      dynamic client = new RestClient("https://jsonplaceholder.typicode.com");  
        
      var user = new {  name="foo", email="foo@bar.com", userId=10 };   
        
      //POST { "name":"foo", "email":"foo@bar.com", "userId":10 } https://jsonplaceholder.typicode.com/users  
      var result = await client.Users.Post(user);
      View Live Example Example PUT to a REST API:
      dynamic client = new RestClient("https://jsonplaceholder.typicode.com");  
        
      var user = new User new { name="foo", email="foo@bar.com" };  
        
      //PUT { "name":"foo", "email":"foo@bar.com" }  https://jsonplaceholder.typicode.com/users/1  
      var result = await client.User(1).Put(user);
      View Live Example Example PATCH to a REST API:
      dynamic client = new RestClient("https://jsonplaceholder.typicode.com");  
        
      var user = new User new { name="foo", email="foo@bar.com" };  
        
      //PATCH{ "name":"foo", "email":"foo@bar.com" }  https://jsonplaceholder.typicode.com/users/1  
      var result = await client.User(1).Patch(user);
      View Live Example
      You can find more usage examples in the DalSoft.RestClient GitHub repo.

      Casting and Serialization

      DalSoft.RestClient tries to make it as easy as possible to deserialize and cast your responses into C# models.

      Casting

      DalSoft.RestClient uses it's duck typing feature to cast your response. This feature is unique to DalSoft.RestClient and not seen in other libraries. This means you don't have to spend time creating boilerplate code to map your JSON responses to your models, just simply cast your response to your model type. Casting to a strongly typed object:
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com");  
        
      User user = await client.Users.Get(1);
      View Live Example

      Collections

      So what about Collections? Again just cast the response and use as normal! Dynamically or as strongly-typed objects are supported: This is how you can iterate over the dynamic response returned if a collection is returned from the REST API:
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com");  
        
      var users = await client.Users.Get();  
        
      foreach (var user in users)  
      {  
         Console.WriteLine(user.name);  
      }
      View Live Example Using the dynamic response returned you can also access by index:
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com");  
        
      var users = await client.Users.Get();  
        
      Console.WriteLine(users[0].name);
      You can also cast the dynamic response returned to IList, IEnumerable, IList<T>, Array, IDictionary, IDictionary<TKey, TValue>
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com");  
      List<User> users = await client.Users.Get();  
        
      foreach (var user in users)  
      {  
         Console.WriteLine("User:" + user.name);  
         Console.WriteLine("Company:" + user.company.name);  
      }
      View Live Example A question came to my mind is how do I use LINQ on the dynamic response returned? Answer – cast it to List<dynamic> first:
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com");  
                
      List<dynamic> users = await client.Users.Get();  
                
      var user = users.Single(x => x.name == "Leanne Graham");  
                    
      Console.WriteLine(user.name);
      View Live Example

      Serialization

      DalSoft.RestClient uses Json.NET so you can control anything about the request and response serialization using JsonSerializerSettings or Json.NET attributes. The most common scenario is wanting to "map" the JSON returned from a REST API response to your model. And Here's how you can map the response to your model using Json.NET attributes:
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com");  
        
      User user = await client.Users.Get(1);   
                
      public class User  
      {  
         [JsonProperty("name")]  
         public string NameOfUser { get; set;}  
                
         [JsonProperty("company")]  
         public Company Company { get; set; }  
      }  
        
      public class Company  
      {  
         [JsonProperty("name")]  
         public string NameOfCompany { get; set; }  
      }
      View Live Example And to use JsonSerializerSettings pass your JsonSerializerSettings to DalSoft.RestClient like so:
      // For example if the JSON returned used snake_case... We can use JsonSerializerSettings to get it deserializing correctly to our PascalCase model  
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com", new Config().SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver =  new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy () } })));

      Extensibility

      DalSoft.RestClient can be extended using HttpClient DelegatingHandlers, this means anything that works with HttpClient today will work with DalSoft.RestClient!

      DelegatingHandlers

      It's really easy to use existing (or your own) DelegatingHandlers with DalSoft.RestClient. Below is an example of how to use HttpClientHandler with DalSoft.RestClient to support cookies. Notice it works exactly the same way as HttpClient:
      var cookieContainer = new CookieContainer();  
      var httpClientHandler = new HttpClientHandler { CookieContainer = cookieContainer };  
        
      dynamic restClient = new RestClient("https://httpbin.org/cookies/set?testcookie=restclient", new Config(httpClientHandler));  
        
      await restClient.Get();  
        
      Console.WriteLine(cookieContainer.GetCookies(new Uri("https://httpbin.org"))["testcookie"]?.Value);

      Extending with Func delegates

      DelegatingHandlers are cool but are a bit verbose to write. Wouldn't it be awesome if you could just use a Func delegate instead? If you have used ASP.NET Core Middleware extensions before you will be familiar with this technique. Here we configure DalSoft.RestClient to use a Func delegate to add a bearer token to every request. Notice again DalSoft.RestClient works with standard HttpRequestMessage and HttpResponseMessage objects.
      dynamic restClient = new RestClient("http://headers.jsontest.com/", new Config()  
            .UseHandler(async (request, token, next) =>  
            {     // You can do anything to the HttpRequestMessage or HttpResponseMessage here  
                  request.Headers.Add("Authorization", "Bearer your_bearer_token");   
                  return await next(request, token); //Call next handler in pipeline  
            });

      DelegatingHandlers that ship with DalSoft.RestClient

      These handlers come with the DalSoft.RestClient package ready for you to use: UnitTestHandler: Lets you Unit Test anything in HttpClient, no more having to spend time working out how to mock a request or response. FormUrlEncodedHandler: Allows you to post “application/x-www-form-urlencoded” data. MultipartFormDataHandler Allows you to post “multipart/form-data” data, which means you can post both form data and files using a .NET Stream or Byte Array. RetryHandler Allows you to set policies for retrying transient failures. TwitterHandler A full Twitter SDK! Shows off what can be done with DalSoft.RestClient with hardly any code!

      Exception Handling and Retrying Transient Failures

      Exception handling is easy in DalSoft.RestClient, you just use IsSuccessStatusCode or EnsureSuccessStatusCode in the HttpResponseMessage (like you would when using HttpClient). For example:
      dynamic client = new RestClient("https://httpstat.us/500");  
        
      var response = await client.Get();   
      var httpResponseMessage = response.HttpResponseMessage;   
        
      if (httpResponseMessage.IsSuccessStatusCode)  
         Console.WriteLine("Success");  
      else  
         Console.WriteLine("Failure by checking IsSuccessStatusCode");  
        
      try  
      {  
         httpResponseMessage.EnsureSuccessStatusCode();  
         Console.WriteLine("Success");     
      }  
      catch (HttpRequestException)  
      {  
         Console.WriteLine("Exception thrown by EnsureSuccessStatusCode()");  
      }
      View Live Example A common problem you will encounter when accessing a REST API is what to do about transient failures, DalSoft.RestClient supports retrying transient failures using the RetryHandler. The RetryHandler supports both exponential and Linear strategies. The code below shows how you set an exponential policy, in this example, a transient failure would back off exponentially and retry 4 times with 2, 4, 8, 32 seconds between tries. However, because we set maxWaitToRetryInSeconds to 10 seconds we restrict the maximum amount of seconds a backoff interval can be – this stops your back off interval growing too large for example larger than your request timeout.
      var config = new Config()  
         .UseRetryHandler  
         (  
           maxRetries:4,   
           waitToRetryInSeconds:2,   
           maxWaitToRetryInSeconds: 10,   
           backOffStrategy: RetryHandler.BackOffStrategy.Exponential  
         );  
        
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com", config);
      The next example shows how you set a Linear policy, the Linear policy is easier to understand, in this example, the RetryHandler will retry a request that had a transient failure 3 times waiting 2 seconds between retries.
      var config = new Config()  
         .UseRetryHandler  
         (  
           maxRetries:3,   
           waitToRetryInSeconds:2,   
           backOffStrategy: RetryHandler.BackOffStrategy.Linear   
         );  
        
      dynamic client = new RestClient("http://jsonplaceholder.typicode.com", config);

      Unit Testing with DalSoft.RestClient

      When Unit Testing code that consumes a REST API, Mocking HttpClient can be painful. DalSoft.RestClient has the UnitTestHandler, which lets you Mock (Fake is the correct TDD term) requests or responses easily. This simple extensibility point works great with Unit Testing your back-end services or front-end code such as Xamarin. DalSoft.RestClient uses the UnitTestHandler for its own Unit Tests too! All you need to do is pass an instance of DalSoft.RestClient with a UnitTestHandler to your Unit Test. No need to work out how to Mock HttpClient, and you don't even have to learn anything new you just work with a HttpRequestMessage and HttpResponseMessage. Let's look at a trivial example, here we ensure the JSON returned is {‘foo':'bar'}.
      var config = new Config()    
         .UseUnitTestHandler(request => new HttpResponseMessage    
         {    
            Content = new StringContent("{'foo':'bar'}")    
         });    
          
      dynamic client = new RestClient("http://test.com", config);  
        
      var result = await client.whatever.Get();  
        
      Assert.AreEqual("{'foo':'bar'}", result.ToString());
      We have just scratched the surface of what you can do with the UnitTestHandler, head over to the documentation if you want to learn more.

      DalSoft.RestClient Lifetime Best Practice and IoC

      It's very easy to get the lifetime of HttpClient wrong. If you use a HttpClient with a service that gets any kind of load you will eventually hit a socket exhaustion issue. Before ASP.NET Core 2.1 the general advice was to register the HttpClient as Singleton, however doing this you might get an issue with DNS updates if your service/app is long running. Knowing this limitation, and realizing that because of microservices HttpClient's are being used more from servers the .NET Core team has come up with HttpClientFactory. The way to think of HttpClientFactory is like connection pooling for HttpClient. DalSoft.RestClient deals with HTTP lifetime management for you, it implements IHttpClientFactory (again with the theme of not changing how you use HttpClient). So if you understand HttpClientFactories in ASP.NET Core you already know how to use them with DalSoft.RestClient. Lets whizz through a quick demo of how to use HttpClientFactory with DalSoft.RestClient… First in our ASP.NET Core project, let’s register DalSoft.RestClient as a service in Startup.cs. Here's the simplest way to configure HttpClientFactory if you only need one DalSoft.RestClient:
      public class Startup  
      {  
         public void ConfigureServices(IServiceCollection services)  
         {  
            services.AddRestClient("https://api.github.com");  
         }  
      }
      Now all you need to do is inject IRestClientFactory into your controller:
      public class GitHubController : Controller  
      {  
         private readonly IRestClientFactory _restClientFactory;  
                
         public GitHubController(IRestClientFactory restClientFactory)  
         {  
            _restClientFactory = restClientFactory;  
         }  
        
         [Route("github/users/dalsoft"), HttpGet]  
         public async Task<List<repository>> CreateClient()  
         {  
            dynamic restClient = _restClientFactory.CreateClient();  
                    
            var repositories = await restClient.users.dalsoft.repos.Get();  
                    
            return repositories;  
         }          
      }
      If you need to support multiple RestClient's we can use a named client. Again first register your named RestClient as a service in Startup.cs
      public class Startup  
      {  
         public void ConfigureServices(IServiceCollection services)  
         {  
            services.AddRestClient("MyNamedGitHubClient", "https://api.github.com/orgs/");  
         }  
      }
      Then inject IRestClientFactory into your controller, and call the CreateClient method passing the name of your client.
      public class GitHubController : Controller  
      {  
         private readonly IRestClientFactory _restClientFactory;  
                
         public GitHubController(IRestClientFactory restClientFactory)  
         {  
            _restClientFactory = restClientFactory;  
         }  
        
         [Route("github/orgs/dotnet/repos"), HttpGet]  
         public async Task<List<repository>> CreateClient()  
         {  
            dynamic restClient = _restClientFactory.CreateClient("MyNamedGitHubClient"); //Get Client by name  
              
            var repositories = await restClient.dotnet.repos.Get();  
                    
            return repositories;  
         }          
      }
      Is it worth using IRestClientFactory? If you get any sort of load YES, as it can improve your performance significantly.

      Source Code

      The source code for this article can be found on our GitHub repo.

      Conclusion

      This wraps it up for our DalSoft.RestClient library post. DalSoft.RestClient is a very neat library that with minimal fuss or friction allows us to work with REST in our C# applications. It is simple enough to easily learn and use, and it is extensible to cover some pretty complex scenarios. Most importantly we can make use of the HttpClient knowledge we already have. One thing we found was that DalSoft.RestClient is so easy to work with sometimes you find yourself overcomplicating it, a simple rule is it shouldn't take more than a couple of lines of code to do most things REST with DalSoft.RestClient. So we hope this little demonstration will help you understand how DalSoft.RestClient works, and maybe even persuade you to try it out. We will definitely use it in our newest projects that require REST communication. To learn about more useful ways to communicate with REST endpoints, you can check our article: A Few Great Ways to Consume RESTful API in C#. What we've learned in this post:
      • What is DalSoft.RestClient, and what it can do for you?
      • How to set it up, configure it, and extend it
      • How to use the DalSoft.RestClient convention to build URLs and write requests
      • Casting and Serialization using DalSoft.RestClient
      • How easy Unit Testing is with DalSoft.RestClient
      • Best practices for IoC and HttpClient Lifetime
      There is a lot more you can do with DalSoft.RestClient, check out the official documentation to find out more.]]>
      5492 0 0 0 605 https://www.dalsoft.co.uk/blog/index.php/2019/02/22/great-post-on-codemaze-using-c-and-dalsoft-restclient-to-consume-any-rest-api/ 0 0 668 0 0 726 https://code-maze.com/ 668 0 861 0 0 1131 861 0
      SOLID Principles in C# - Interface Segregation Principle https://code-maze.com/interface-segregation-principle/ Mon, 21 Jan 2019 07:20:41 +0000 https://code-maze.com/?p=5546
    • Single Responsibility Principle
    • Open/Closed Principle
    • Liskov Substitution Principle
    • Interface Segregation Principle (Current article)
    • Dependency Inversion Principle
    • [sc name="part_of_series" headline="This article is part of the series"] To download the source code for this project, check out the Interface Segregation Principle Project Source Code. To read about other SOLID principles, check out our SOLID Principles page. This article is divided into the following sections:

      Starting Example

      There are vehicles that we can drive, and there are those we can fly with. But there are cars we can drive and fly (yes those are on sale). So, we want to create a code structure which supports all the actions for a single vehicle, and we are going to start with an interface:
      public interface IVehicle
      {
          void Drive();
          void Fly();
      }
      
      Now if we want to develop a behavior for a multifunctional car, this interface is going to be perfect for us:
      public class MultiFunctionalCar : IVehicle
      {
          public void Drive()
          {
              //actions to start driving car
              Console.WriteLine("Drive a multifunctional car");
          }
      
          public void Fly()
          {
              //actions to start flying
              Console.WriteLine("Fly a multifunctional car");
          }
      }
      
      This is working great. Our interface covers all the required actions. But now, we want to implement the Car class and the Airplane class as well:
      public class Car : IVehicle
      {
          public void Drive()
          {
              //actions to drive a car
              Console.WriteLine("Driving a car");
          }
      
          public void Fly()
          {
              throw new NotImplementedException();
          }
      }
      
      public class Airplane : IVehicle
      {
          public void Drive()
          {
              throw new NotImplementedException();
          }
      
          public void Fly()
          {
              //actions to fly a plane
              Console.WriteLine("Flying a plane");
          }
      }
      Now we can see what the problem with the IVehicle interface is. It contains only one required declaration per each class. The other method, which is not required, is implemented to throw an exception. That is a bad idea because we should be writing our code to do something and not just to throw exceptions. Furthermore, we would have to put an additional effort to document our class so that users know why they shouldn’t be using the not implemented method. A really bad idea. So, in order to fix this problem, we are going to do some refactoring to our code and write it in accordance to ISP.

      Implementing the ISP In the Current Solution

      The first thing we are going to do is to divide our IVehicle interface:
      public interface ICar
      {
          void Drive();
      }
      
      public interface IAirplane
      {
          void Fly();
      }
      
      As a result, our classes can implement only the methods they need:
      public class Car : ICar
      {
          public void Drive()
          {
              //actions to drive a car
              Console.WriteLine("Driving a car");
          }
      }
      
      public class Airplane : IAirplane
      {
          public void Fly()
          {
              //actions to fly a plane
              Console.WriteLine("Flying a plane");
          }
      }
      
      public class MultiFunctionalCar : ICar, IAirplane
      {
          public void Drive()
          {
              //actions to start driving car
              Console.WriteLine("Drive a multifunctional car");
          }
      
          public void Fly()
          {
              //actions to start flying
              Console.WriteLine("Fly a multifunctional car");
          }
      }
      
      We can even use a higher level interface if we want in a situation where a single class implements more than one interface:
      public interface IMultiFunctionalVehicle : ICar, IAirplane
      {
      }
      
      Once we have our higher level interface, we can implement it in different ways. The first one is to implement the required methods:
      public class MultiFunctionalCar : IMultiFunctionalVehicle
      {
          public void Drive()
          {
              //actions to start driving car
              Console.WriteLine("Drive a multifunctional car");
          }
      
          public void Fly()
          {
              //actions to start flying
              Console.WriteLine("Fly a multifunctional car");
          }
      }
      
      Or if we already have implemented the Car class and the Airplane class, we can use them inside our class by using the decorator pattern:
      public class MultiFunctionalCar : IMultiFunctionalVehicle
      {
          private readonly ICar _car;
          private readonly IAirplane _airplane;
      
          public MultiFunctionalCar(ICar car, IAirplane airplane)
          {
              _car = car;
              _airplane = airplane;
          }
      
          public void Drive()
          {
              _car.Drive();
          }
      
          public void Fly()
          {
              _airplane.Fly();
          }
      }
      

      What are the Benefits of the Interface Segregation Principle

      We can see from the example above, that smaller interface is a lot easier to implement due to not having to implement methods that our class doesn’t need. Of course, due to the simplicity of our example, we can make a single interface with a single method inside it. But in real-world projects, we often come up with an interface with multiple methods, which is perfectly normal as long as those methods are highly related to each other. Therefore, we make sure that our class needs all these actions to complete its task. Another benefit is that the Interface Segregation Principle increases the readability and maintainability of our code. We are reducing our class implementation only to required actions without any additional or unnecessary code.

      Conclusion

      To sum this article up, we should put an effort into creating smaller interfaces while developing our project. Yes, we may end up with a lot of different interfaces in the end but from our point of view, this is much better than having a few large interfaces that can force us to implement non-required methods in our classes. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5546 0 0 0 784 0 0 785 784 0 1137 0 0 1139 1137 0 1421 0 0
      SOLID Principles in C# - Dependency Inversion Principle https://code-maze.com/dependency-inversion-principle/ Mon, 28 Jan 2019 07:00:09 +0000 https://code-maze.com/?p=5631
    • High-level modules should not depend on low-level modules, both should depend on abstractions.
    • Abstractions should not depend on details. Details should depend on abstractions.
    • We are going to make all of this easier to understand with an example and additional explanations. [sc name="part_of_series" headline="This article is part of the series"] To download the source code for this project, check out the Dependency Inversion Principle Project Source Code. To read about other SOLID principles, check out our SOLID Principles page. This article is divided into the following sections:

      What are the High-Level and Low-Level Modules

      The high-level modules describe those operations in our application that has more abstract nature and contain more complex logic. These modules orchestrate low-level modules in our application. The low-level modules contain more specific individual components focusing on details and smaller parts of the application. These modules are used inside the high-level modules in our app. What we need to understand when talking about DIP and these modules is that both, the high-level and low-level modules, depend on abstractions. We can find different opinions about if the DIP inverts dependency between high and low-level modules or not. Some agree with the first opinion and others prefer the second. But the common ground is that the DIP  creates a decoupled structure between high and low-level modules by introducing abstraction between them.

      Example Which Violates DIP

      Let’s start by creating two enumerations and one model class:
      public enum Gender
      {
          Male,
          Female
      }
      
      public enum Position
      {
          Administrator,
          Manager,
          Executive
      }
      
      public class Employee
      {
          public string Name { get; set; }
          public Gender Gender { get; set; }
          public Position Position { get; set; }
      }
      
      To continue, we are going to create one low-level class which keeps (in a simplified way) track of our employees:
      public class EmployeeManager
      {
          private readonly List<Employee> _employees;
      
          public EmployeeManager()
          {
              _employees = new List<Employee>();
          }
      
          public void AddEmployee(Employee employee)
          {
              _employees.Add(employee);
          }
      }
      
      Furthermore, we are going to create a higher-level class to perform some kind of statistical analysis on our employees:
      public class EmployeeStatistics
      {
          private readonly EmployeeManager _empManager;
      
          public EmployeeStatistics(EmployeeManager empManager)
          {
              _empManager = empManager;
          }
      
          public int CountFemaleManagers()
          {
              //logic goes here
          }
      }
      
      With this kind of structure in our EmployeeManager class, we can’t make use of the _employess list in the EmployeeStatistics class, so the obvious solution would be to expose that private list:
      public class EmployeeManager
      {
          private readonly List<Employee> _employees;
          
          public EmployeeManager()
          {
              _employees = new List<Employee>();
          }
      
          public void AddEmployee(Employee employee)
          {
              _employees.Add(employee);
          }
      
          public List<Employee> Employees => _employees;
      }
      
      Now, we can complete the Count method logic:
      public class EmployeeStatistics 
      { 
          private readonly EmployeeManager _empManager; 
          public EmployeeStatistics(EmployeeManager empManager) 
          {
             _empManager = empManager; 
          } 
      
          public int CountFemaleManagers () => 
            _empManager.Employees.Count(emp => emp.Gender == Gender.Female && emp.Position == Position.Manager);
       }
      Even though this will work just fine, this is not what we consider a good code and it violates the DIP. How is that? Well, first of all, our EmployeeStatistics class has a strong relation (coupled) to the EmployeeManager class and we can’t send any other object in the EmployeeStatistics constructor except the EmployeeManager object. The second problem is that we are using the public property from the low-level class inside the high-level class. By doing so, our low-level class can’t change its way of keeping track of employees. If we want to change its behavior to use a dictionary instead of a list, we need to change the EmployeeStatistics class behavior for sure. And that’s something we want to avoid if possible.

      Making Our Code Better by implementing the Dependency Inversion Principle

      What we want is to decouple our two classes so the both of them depend on abstraction. So, the first thing we need to do is to create the IEmployeeSearchable interface:
      public interface IEmployeeSearchable
      {
          IEnumerable<Employee> GetEmployeesByGenderAndPosition(Gender gender, Position position);
      }
      
      Then, let’s modify the EmployeeManager class:
      public class EmployeeManager: IEmployeeSearchable
      {
          private readonly List<Employee> _employees;
      
          public EmployeeManager()
          {
              _employees = new List<Employee>();
          }
          
          public void AddEmployee(Employee employee)
          {
              _employees.Add(employee);
          }
      
          public IEnumerable<Employee> GetEmployeesByGenderAndPosition(Gender gender, Position position)
              => _employees.Where(emp => emp.Gender == gender && emp.Position == position);
      }
      
      Finally, we can modify the EmployeeStatistics class:
      public class EmployeeStatistics
      {
          private readonly IEmployeeSearchable _emp;
      
          public EmployeeStatistics(IEmployeeSearchable emp)
          {
              _emp = emp;
          }
      
          public int CountFemaleManagers() => 
          _emp.GetEmployeesByGenderAndPosition(Gender.Female, Position.Manager).Count();
      }
      
      This looks much better now and it’s implemented by DIP rules. Now, our EmployeeStatistics class is not dependent on the lower-level class and the EmployeeManager class can change its behavior about storing employees as well. Finally, we can check the result by modifying Program.cs class:
      class Program
      {
          static void Main(string[] args)
          {
              var empManager = new EmployeeManager();
              empManager.AddEmployee(new Employee { Name = "Leen", Gender = Gender.Female, Position = Position.Manager });
              empManager.AddEmployee(new Employee { Name = "Mike", Gender = Gender.Male, Position = Position.Administrator });
      
              var stats = new EmployeeStatistics(empManager);
              Console.WriteLine($"Number of female managers in our company is: {stats.CountFemaleManagers()}");
          }
      }
      
      finished example - Dependency Inversion Principle

      Benefits of Implementing the Dependency Inversion Principle

      Reducing the number of dependencies among modules is an important part of the process of creating an application. This is something that we get if we implement DIP correctly. Our classes are not tightly coupled with the lower-tier objects and we can easily reuse the logic from the high-tier modules. So, the main reason why DIP is so important is the modularity and reusability of the application modules. It is also important to mention that changing already implemented modules is risky. By depending on abstraction and not on a concrete implementation, we can reduce that risk by not having to change high-level modules in our project. Finally, DIP when applied correctly gives us the flexibility and stability at the level of the entire architecture of our application. Our application will be able to evolve more securely and become stable and robust.

      Conclusion

      So to sum up, the Dependency Inversion Principle is the last part of the SOLID principles which introduce an abstraction between high and low-level components inside our project to remove dependencies between them. If someone asks: „Should I put an effort to implement the DIP into my code?“, our answer would be: „Yes you should“.  Loosely coupled code and reusable components should be our goal and responsibility when developing software applications. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5631 0 0 0 1423 https://twitter.com/jorovipe1 0 0
      Advanced C# - Dynamic Type https://code-maze.com/advanced-csharp-dynamic-type/ Mon, 04 Feb 2019 06:00:49 +0000 https://code-maze.com/?p=5488 Dynamic Type in C# GitHub repo. So here's exactly what we are going to learn: Let's start.

      Static vs Dynamic Languages

      As you might be aware, software development languages are divided into two major categories: static languages and dynamic languages. The main difference between a static and a dynamic language is how it handles its types (or doesn't). Static languages (statically typed languages) revolve around the concept of statically typed variables. In dynamic languages, the variable type doesn't need to be defined right off the bat. In other words, in the static language variables are resolved during "compile time" and in the dynamic in the "runtime". What does this really mean though? In a static language, a variable is assigned its type when the project is compiled and it stays that way during the whole execution of the application. In a dynamic language, a variable can change it's type several times while the application is already running. Some examples of static and dynamic languages: [table id=15 /] There are some great languages in both of these categories. And how should you choose which language category is better for you or the project you are working on? Well, let's see what are the pros of using each language category and then make a decision.

      Advantages of Static and Dynamic Languages

      Static languages are generally considered to be faster because they resolve their types during the compilation phase. This helps improve the application's performance and optimization. Excluding machine language and Assembly, the fastest high-level languages are probably C and C++. No other languages can still match the speed of these two because of their memory management capabilities. Besides being fast, static languages are also being fast to fail. You'll find a lot of bugs even before the application has started because of the compiler checks. This means fewer bugs once the application is up and running. On the other hand dynamic languages, while being slower, are much easier to write. And you can write them faster without having to think about which type to use or how to initialize it. This comes at the price of finding more bugs in runtime due to the interpreter missing to recognize a variable's type. At the same time, such code tends to be easier to read and is smaller overall, so the bugs generally have less space to hide in. But, let's not turn this into which language is better while we still can :D Any language is powerful in the right hands, and knowing any language to its core and utilizing all of its features is the key to making great applications.

      What is Dynamic Type in C#

      So we already mentioned that C# is a statically typed language. So what does a dynamic type has to do with C#? The dynamic type has been added to C# since version 4 because of the need to improve interoperability with COM (Component Object Model) and other dynamic languages. While that can be achieved with reflection, the dynamic type provides a natural and more intuitive way to implement the same code. Dynamic type in C# is instantiated exclusively by using the dynamic keyword and it can be assigned any other type. Here is an example of two dynamic variables, one that is instantiated as an int, and another that is a complex type (Book).
      dynamic intNumber = 12;
      dynamic book = new Book(isbn);
      Simple enough.

      How Does Dynamic Type Work?

      Essentially, when you declare a variable dynamic, you are telling the compiler to turn off the compile-time type checks. Dynamic is basically System.Object type under the hood, but it doesn't need to explicitly cast a value before using it. More on that a bit later in the article. For now, let's see how dynamic type works on some examples. We can start by creating a dynamic variable:
      dynamic testVariable = 5;
      Console.WriteLine(testVariable.GetType());
      Can you guess the output of the Console.Writeline()? It's not hard to guess - it is of course System.Int32. So what happens if we change the value of the testVariable to something entirely different like "Hello World"? Can we do that? Let's see.
      testVariable = "Hello World";
      Console.WriteLine(testVariable.GetType());
      Now we get the output System.String. But if we wish to do something like incrementing a value now, we'll get an exception:
      Console.WriteLine(testVariable++);
      The code will compile, but we are going to get an exception when we run the application: Runtime Binder Exception RuntimeBinderException is the exception you will probably see the most when you work with dynamic type. As the exception message states, we cannot use ++ operator on the variable of the type string. So that means that dynamic type behaves like the last type we've assigned to it. One more example to wrap it up. We have a class that gets us the instance of some logger implementation:
      public class Logger
      {
          public void LogInfo(string message)
          {
              Console.WriteLine($"INFO: {message}");
          }
      
          public void LogWarning(string message)
          {
              Console.WriteLine($"WARNING: {message}");
          }
      
          public void LogError(string message)
          {
              Console.WriteLine($"ERROR: {message}");
          }
      }
      
      public class LoggerFactory
      {
          public static Logger GetLogger()
          {
              return new Logger();
          }
      }
      
      dynamic logger = LoggerFactory.GetLogger();
      logger.LogInfo("Hi");
      logger.LogWarning("You are about to enter a time warp");
      logger.LogError("System is malfunctioning");
      logger.LogTrace("Communication lost");
      As far as the compiler is concerned, everything is fine. But once again we get the RuntimeBinderException because LogTrace method is not defined. Runtime Binder Exception LogTrace Ok, that's enough examples for now. Let's dive a bit deeper into dynamic type (geeky stuff ahead).

      A Short Overview of DLR

      Ok, so now that we've seen how the dynamic type works, let's discuss what's behind the curtains a bit. We won't go too deep since that would be overdoing it for this article. DLR or Dynamic Language Runtime is what makes dynamic type easy to use in a C# which is statically typed language. DLR adds a set of services to the CLR (Common Language Runtime) that make dynamic work. DLR is an open source project and can be found on this GitHub repo. Here's how DLR looks like: [caption id="attachment_5629" align="alignnone" width="586"]dlr-archoverview DLR architecture[/caption] As we can see, the services DLR added to the already existing functionalities of CLR include Expression Trees, Call Site Caching and Dynamic Object Interoperability. Let's see what these features can do for us.

      Expression Trees

      Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation. DLR has extended LINQ expression trees with control flow, assignment and other language-modeling nodes. If you are interested in what expression trees check out the documentation on Expression Trees in C# and How to Build Dynamic Queries With Expression Trees in C#.

      Call Ste Caching 

      To achieve the optimal efficiency of operation execution, DLR provides a mechanism to cache previously called operations. A dynamic call site is a place where you call a.b() or a+b on dynamic objects. DLR caches the types of objects that are used while executing these operations and stores them in a cache. If a similar operation is performed again, DLR can retrieve the information from that cache.

      Dynamic Object Interoperability

      To achieve operability with different languages and enable authors to implement dynamic libraries DLR provides a set of classes and interfaces that are used for this purpose: IDynamicMetaObjectProviderDynamicMetaObjectDynamicObject, and ExpandoObject. So having all this in mind, the advantages of DLR are:
      • Simplified portability of dynamic languages to .NET
      • Enables dynamic features in static languages
      • Improved sharing of libraries and communication between static and dynamic languages
      • Fast dispatch and invocation (using caching)
      In conclusion, DLR is what makes the dynamic type work as we know it and we wouldn't be able to use dynamic type without it and the mechanisms it provides. Enough of the geeky stuff, let's proceed.

      Why Should We Use the Dynamic Type

      By now, you probably have some ideas on where you can use a dynamic type. But let's go through some common scenarios in which dynamic could potentially improve our applications and make our lives as developers a bit easier. First of all, let's make it clear that dynamic is not a silver bullet. We shouldn't use it just because we can. While it has its benefits, dynamic objects are harder to work with while writing code, since we don't have an Intellisense for them due to the nature of dynamic type. On the other hand, if have a need to implement dynamic type everywhere, we are probably using the wrong type of language. Dynamic languages are better suited for those kinds of cases. So what are the common cases to apply a dynamic type to:
      • Communicating with other dynamic languages
      • Simplifying responses from API calls when we don't know what type of object to expect (or we don't care)
      • Creating libraries that can be used between languages
      • Making generic solutions when speed isn't the main concern
      • Replacing and simplifying reflection code
      • More?
      Why we shouldn't use dynamic all the time because:
      • It's slower than statically typed code
      • Increases a chance to get runtime exceptions
      • Decreases code readability in some cases, and working with it is a bit harder due to the lack of IntelliSense
      Ok, so let's see some concrete examples of dynamic in action.

      Dynamic vs Reflection

      Reflection is a mechanism to get a type of abstract System.Object object, and to invoke its members without knowing what the concrete type of that object is. For example, let's see what a typical reflection flow looks like:
      EmployeeFactory employeeFactory = GetEmployeeFactory();
      object firstEmployee = employeeFactory.GetFirstEmployee();
      Type firstEmployeeType = firstEmployee.GetType();
      object workStatusObject = firstEmployeeType.InvokeMember("GetWorkStatus", BindingFlags.InvokeMethod, null, firstEmployee, null);
      WorkStatus workStatus = Enum.Parse<WorkStatus>(workStatusObject.ToString());
      
      Console.WriteLine(workStatus);
      This piece of code although very complicated and large for its purpose retrieves an employee's work status. All that code just to call one method. That's because our EmployeeFactory class returns an employee as a generic System.Object type. System.Object is the base class of all other classes and we can always use it when we don't know what type of object to expect. To call the method of an object of the type System.Object we need to find out what type of object it is. We do that by calling the object.GetType() method. After that, we can use the InvokeMember() method to explicitly call GetWorkStatus method, on the firstEmployee object. After a bit of enum parsing, we can finally find out what the work status of the firstEmployee. A bit complicated but it works. Now let's see how the same example looks like by using dynamic:
      EmployeeFactory employeeFactory = GetEmployeeFactory();
      dynamic firstEmployee = employeeFactory.GetFirstEmployee();
      WorkStatus workStatus = firstEmployee.GetWorkStatus();
      
      Console.WriteLine(workStatus);
      Well, that looks much simpler and easier to read. Moreover, we didn't have to think about types or type method names as strings. So, in the battle of dynamic vs reflection we get:
      • Cleaner and more readable code
      • Better performance due to dynamic type caching
      • Not having hardcoded strings in our code
      • Easier implementation
      Once again, a small disclaimer. This example is for demonstration purposes only. Generally, both dynamic and reflection should be used sparingly and not just "because we can". Both dynamic and reflection can degrade the performance of an application drastically, and introduce bugs we could have avoided by implementing static mechanisms and abstractions correctly.

      Var vs Object vs Dynamic

      If you are new to the C# world, you might still have some problems discerning these three keywords. Let's break them down.

      Var Keyword

      Var is used in a method scope to implicitly type a variable. The variable is still strongly typed, but the compiler decides which type it will be in the runtime. We can even see the type by hovering over the var keyword.
      var imNumber = 10; //implicitly typed variable
      int exNumber = 10; //explicitly typed variable
      Our opinion is that var improves the readability of the code and it should be used because more often than not we don't need to explicitly type our variables. Especially if the types are complicated. For example Dictionary<string, IEnumerable<Employee>> or something even more complicated. The type of the variable cannot be changed at runtime. There are pros and cons to using var, but that's beyond the scope of this article. If you are interested in learning more about when to use it and when not, check out this blog post: When to Use and Not Use var in C#.

      Object Keyword

      The object is an alias for System.Object which is the ultimate base class in C#. Every class in C# is derived from System.Object, and we can utilize that fact to make a more generic codebase. Through the object type, we can get the concrete type of the object we are working with, in runtime. While we can achieve pretty much anything in terms of abstraction with System.Object, sometimes we are forced to use reflection to achieve the goal we want, which is not ideal. To be able to use the type that is assigned to the System.Object variable, we need to explicitly cast it first. For example:
      object testObj = 10;
      testObj = (int)testObj + 10;

      Dynamic Keyword

      Dynamic is actually an object type but it bypasses compiler type checks. We can assign anything to a dynamic variable and not worry about the compiler or the type. It utilizes the power of DLR which is an extension of CLR. Although bypassing compiler checks is nice, it could potentially lead to unexpected behavior if not used carefully. We don't need to explicitly cast dynamic before trying out different operations.
      dynamic testDynamic = 10;
      testDynamic = testDynamic + 25;

      RuntimeBinderException

      RuntimeBinderException is the exception we get at execution time if a dynamic type fails to resolve the operation we provided on a type that doesn't support it. To put it in simple terms, imagine you are trying to do something on a static type and that static type doesn't implement that behavior. Like trying to increment a string. Or calling .Split() on an integer. You get a picture. It's not that simple though, there are some gotchas that we should be aware of. For example, calling the .Count() on a dynamic object that has been assigned ICollection:
      public static void ExamplePrintCount(ICollection collection)
      {
          dynamic d = collection;
          Console.WriteLine("Static typing: {0}", collection.Count);
          Console.WriteLine("Dynamic typing: {0}", d.Count);
      }
      If we run this example by providing an int array:
      ExamplePrintCount(new int[20]);
      What do you think the result will be? If you guessed that first Console.WriteLine() will output 2, and that we'll get RuntimeBinderException in the second one, you guessed it right. int array count runtimebinderexception This example is taken from chapter 14 of C# in Depth book which you can find here. We recommend you read that chapter if you are serious about using dynamic in your applications. It will definitely save you some time, reduce headaches and prolong your lifespan. Sound like a miracle medicine :)

      Conclusion

      The dynamic type is a useful construct. It has a variety of usages and it can both help us or make our life tougher if we are not careful when and how we implement it. While dynamic has originated from the need of overcoming interoperability problems, there are a lot of nice examples you can find today where dynamic type is utilized in the best possible way. Take for example DalSoft.RestClient we've already written about. It is a full-blown dynamic library for consuming restful APIs from C#. Also, it's worth mentioning that this is not all that dynamic feature can do for us. There are more useful constructs like ExpandoObject and DynamicObject which we are going to cover in some of the future articles. So to sum it up, in this post we've learned:
      • What static and dynamic languages are and what the pros and cons of each are
      • About dynamic type in C# and how to implement it
      • Some situations where dynamic can come in handy
      • The difference between reflection and dynamic
      • A bit about DLR
      • Cleared out the differences between var, object and dynamic keywords
      • About RuntimeBinderException
      [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5488 0 0 0 When to Use and Not Use var in C#]]> 583 https://www.alvinashcraft.com/2019/02/04/dew-drop-february-4-2019-2891/ 0 0 584 https://aidyourpc.com/advanced-c-dynamic-type/ 0 0 586 https://hk.aidyourpc.com/1108389816.html 0 0 1629 0 0
      Working with Data in ASP.NET Core MVC https://code-maze.com/working-with-data-in-asp-net-core-mvc/ Mon, 03 Jun 2019 06:00:23 +0000 https://code-maze.com/?p=5500 In the previous part of this series, we built an ASP.NET Core MVC application using some mock data. In this part, we are going to look at the ways to connect the application with a database and work with data. We are going to use EF Core Code-First approach which is the preferred way to work with data when starting a new project. We strongly recommend visiting the complete navigation of this series: ASP.NET Core MVC Series. To download this article's source code visit: Working With Data in ASP.NET Core MVC Source Code. First, we need to create a model. Then we are going to scaffold the model to produce the controller action methods and views corresponding to the Create, Read, Update, and Delete (CRUD) operations for the model. After that, we are going to create the database using the EF Core Migrations. Finally, we are going to seed the database with some initial data. Once these steps are completed, we’ll have a working ASP.NET Core MVC app that connects to the database. The article is divided into the following sections:

      Creating a Model in ASP.NET Core MVC

      Let’s create the Book model, which is a similar object to the one we created in the previous part of this series, just without Authors property:
      public class Book
      {
         public int Id { get; set; }
      
         [Display(Name = "Book Title")]
         [Required]
         public string Title { get; set; }
      
         public string Genre { get; set; }
      
         [DataType(DataType.Currency)]
         [Range(1, 100)]
         public decimal Price { get; set; }
      
         [Display(Name = "Publish Date")]
         [DataType(DataType.Date)]
         public DateTime PublishDate { get; set; }
      }
      
      This model will serve as a base for building the project.

      Scaffolding the Model

      Next step is to scaffold the model to generate the controller action methods and views. Scaffolding will create a new fully functional controller. Right-click on the Controllers folder > Add > New Scaffolded Item: add scaffolded item In the Add Scaffold dialog, we are going to select MVC Controller with views, using Entity Framework > Add: In the Add Scaffold dialog, select MVC Controller with views, using Entity Framework > Add. Let's complete the Add Controller dialog:
      1. Model class: Book (BookStoreWithData.Models)
      2. Data context class: Select the + icon.
      3. Leave the default name (BookStoreWithData.Models.BookStoreWithDataContext)
      4. Click Add
      add data context
      1. Views: Keep the default of each option checked
      2. Controller name: Keep the default BooksController
      3. Select Add
      Add view and controller details - ASP.NET Core MVC Visual Studio creates:
      • An Entity Framework Core database context class (Data/BookStoreWithDataContext.cs)
      • A Controller (Controllers/BooksController.cs)
      • Razor view files for Create,Delete, Details, Edit, and Index pages (Views/Books/*.cshtml)
      This process of automatic creation of the database context, CRUD (create, read,update and delete) action methods and views are known as scaffolding. Scaffolding is optional, and you can do the whole process manually, too. In situations where scaffolding is not needed or appropriate, we can take control of the entire creation process.

      Migrations

      Migrations automate the creation of database based on our model. First, let’s run the following command in the Package Manager console: PM> Add-Migration BookStoreWithData.Models.EmployeeContext This will create the classes for supporting migrations. Now we need to apply those changes to the database. For that, let’s run the following command: PM> update-database This will update the database based on our models. We have covered the way to do this in detail in the ASP.NET Core Web API with EF Core Code-First Approach article. Once we complete all the steps, we are going to have the database and columns created as defined in the model. Our database table and columns will look like this: database and columns - ASP.NET Core MVC We have successfully created the database from our code using EF Core Code-First Migrations.

      Seeding the Data

      The next step is seeding the data. Data seeding allows us to provide the initial data during the creation of a database. As mentioned in the linked article above, let’s override the OnModelCreating method in the BookContext:
            protected override void OnModelCreating(ModelBuilder modelBuilder)
              {
                  modelBuilder.Entity<Book>().HasData(new Book
                  {
                      Id = 1,
                      Title = "Book1",
                      Genre = "Genre1",
                      Price = 20,
                      PublishDate = new DateTime(2012, 4, 23)
                  }, new Book
                  {
                      Id = 2,
                      Title = "Book2",
                      Genre = "Genre2",
                      Price = 30,
                      PublishDate = new DateTime(2008, 6, 13)
                  });
              }
      
      Once we apply the migration and update the database, we can see that the database tables are updated with the seed data that we provided: database table with data

      Running the ASP.NET Core MVC Application

      Now, let's try running the application.

      Listing Page

      Let's navigate to /Books This will invoke the Index() method in the controller: list page - ASP.NET Core MVC We can see that the Index page displays the list of all books in the database. There are also links for editing, viewing details and deleting a book on the grid. At the top, there is also a link for creating a new book.

      Create Page

      Let's click on the ‘Create New’ link. Clicking on the link will take us to /Books/Create. This will invoke the Create() method in the controller using a GET request: create page This page can be used to create a new book. After entering the book details and clicking on the Create button, the Create() method with the [HttpPost] attribute will be invoked. This will be a POST request and the form data will be submitted. There is a link at the bottom to navigate back to the list.

      Details Page

      In the listing page, if we click on the details link of any book, we’ll be taken to /Books/Details/{id}. This will invoke the Details() method in the controller: details page This page shows the details of a book. In the details page, we can see buttons for editing and navigate back to the list.

      Edit Page

      If we click on the edit link from here or the listing page, it will take us to /Book/Edit/{id} This will invoke the first Edit() method in the controller which supports only GET requests: edit page Clicking the Save button will invoke the Edit() method with the [HttpPost] attribute. This will update the record with the values we provide on the page. Depending on the request type, MVC decides which Edit method to call. For editing a record, PUT request is the more appropriate method. But here the auto-generated code used a POST method that can also be used. However, when we are creating the controller methods by ourselves, the recommended approach is to use the PUT method for updating a record.

      Delete Page

      Finally, if we click on the Delete link from the listing page, we’ll be navigated to /Book/Delete/{id} This will invoke the first Delete() method in the controller which supports only GET requests. This is the delete confirmation page: delete confirmation page Once we confirm the delete by clicking the Delete button, the DeleteConfirmed() method with the [HttpPost]  attribute will be invoked. This will delete the record from the database.

      Code Explained

      By following the above steps, we have successfully created a fully functional app with database integration. Now let’s take a look at the auto-generated code and try to understand how the app functions. Visual Studio generates the following files as part of scaffolding: scaffolding generated files - ASP.NET Core MVC

      DB Context

      A DB Context file is responsible for facilitating all communication with the database. To learn about DBContext in more detail, check out our article: Context Class and the Database Connection. Context file for this app is auto-generated at Data/BookStoreWithDataContext as part of the scaffolding.

      Controller

      A Controller file is generated at Controllers/BooksController.cs with action methods corresponding to CRUD operations. If we look at the BooksController file, we can see the following action methods: GET api/books - Lists all the books. GET api/books/details/{id} - Gets the details of a book. GET api/books/create - Shows the initial create book page. POST api/books/create - Creates a new book. GET api/books/edit/{id} - Shows the initial edit page. POST api/books/edit/{id} - Updates the details of a book. GET api/books/delete/{id} - Shows the delete confirmation page. POST api/books/delete/{id} - Deletes a book. The action methods in the controller access the context to perform data operations. For example, In the Details() method, we can see that the book record is fetched by accessing the context:
        var book = await _context.Book
                      .FirstOrDefaultAsync(m => m.Id == id);
      
      Similarly, in the Create() method with [HttpPost] attribute, we access the context to add a new record:
      _context.Add(book);
      Ideally, a controller should not directly access the context file. Instead, we should introduce a repository layer in between. We have explained the repository pattern in detail in one of our other article section: Implementing the repository pattern.

      Razor view files

      Inside the Views/Books folder, we can see that the view pages are created for CreateDelete, Details, Edit and Index methods. These view pages are based on the razor syntax. We’ll discuss creating the view pages using razor syntax in detail in a later article.

      Conclusion

      In this article, we have looked at the following:
      • Creating a model.
      • Scaffolding the model to generate context, controller and the view files.
      • Creating the database using migration.
      • Seeding the data.
      • The code auto-generated by scaffolding.
      In the next part of the series, we’ll look at creating the view pages using razor syntax. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5500 0 0 0 1177 0 0 1227 0 0 1228 1227 0 1367 0 0 1369 1367 0 1372 1369 0 1630 0 0 1631 Migrations and Seed data in EF Core. Read it carefully, everything is explained inside it. Best regards.]]> 1630 0
      Getting Started with ASP.NET Core MVC https://code-maze.com/getting-started-with-asp-net-core-mvc/ Mon, 27 May 2019 07:00:26 +0000 https://code-maze.com/?p=5513 In this article, we are going to look at the basics of building an ASP.NET Core MVC web app. We are going to start by creating a simple ASP.NET Core MVC app using the default template provided by Visual Studio. The default template itself will translate into a working app. To the default template, we’re going to add a controller and a few action methods. Afterward, we’re going to introduce the views using the razor syntax and return them from the controller methods. Finally, we’re going to define some models and see how those can be passed into the views. We are also going to look at how the model data can be rendered on the web page. We strongly recommend visiting the complete navigation of this series: ASP.NET Core MVC Series. To download this article's source code visit: Getting Started with ASP.NET Core MVC Source Code. We have divided this article into the following sections:

      Creating an ASP.NET Core MVC project

      First, let’s create a new ASP.NET Core MVC project.    From Visual Studio, select File > New > Project Complete the New Project dialog:
      1. In the left pane, select Web
      2. Then in the center pane, select ASP.NET Core Web Application
      3. Name the project BookStore
      4. select OK
      Create New Project Complete the New ASP.NET Core Web Application - BookStore dialog:
      1. In the version selector drop-down box select ASP.NET Core 2.2
      2. Select Web Application (Model-View-Controller)
      3. Then select OK
      New web application template Visual Studio creates an MVC project using the default template. The great thing is that we have a working app right now by entering a project name and selecting a few options. This is a basic starter project and a good place to start.

      The Project Structure

      Now, let's examine the project structure and look at the files generated as part of the default MVC template: Solution Explorer We can see that the project is well organized into separate folders for Models, Controllers, and Views. The Views are further organized into subfolders corresponding to each view. There are some default files generated in each of these folders as well. Then there are the usual configuration and startup files that come with the .NET Core project template. Now let’s run the app with Ctrl+F5. We can see a website based on the default layout provided by ASP.NET Core MVC: default mvc app Congrats! We have just created a website using ASP.NET Core MVC.

      Adding Controllers

      Since we have a working ASP.NET Core MVC app, let’s start experimenting with it. Let’s add an empty controller to the Controllers folder and name it BooksController. We can do it by right-clicking Controllers > Add > Controller add controller In the Add Scaffold dialog box, select MVC Controller - Empty add scaffold Then, in the Add Empty MVC Controller dialog box, give the controller name as BooksController and click Add: add empty mvc controller This will create BooksController with a default action method. Let’s change the code and create two action methods in it:
      public string Index()
      {
          return "This is the book index.";
      }
      
      public string Details()
      {
           return "This is the details of a book.";
      }
      Every public method in a controller is callable as an HTTP endpoint. In our controller, both methods return a string. Let’s run the application and navigate to the BooksController by changing the URL to https://localhost:44323/books *Please note that port number is randomly assigned by IIS Express and may vary in different systems. books index page We’ll cover routing in detail in an upcoming article, but for now, let’s just understand some basics. MVC invokes controller classes and the action methods within them depending on the incoming URL. The default URL routing logic used by MVC uses a format like this to determine what code to invoke: /[Controller]/[ActionName]/[Parameters] The ActionName defaults to Index when not supplied. Parameters are also optional. So in this case when we hit the above URL, the application executes the Index method of the BooksController. This method returns a string and what we see is an HTML page generated with the supplied string. Similarly, If we change the URL to https://localhost:44323/books/details, we can see the Details method of the BooksController executed: books details We have created our own controller with two methods and executed them which is awesome.

      Creating Views

      Even though returning plain strings from the controller works, that is not a good practice. The controller action methods should ideally return a view. Then the view should be responsible for displaying the page output. So let’s add a view file for the Index action method. Right-click on the Index action method and click Add View: add view Give the view name as Index and click Add: add mvc view This will create a new folder Books under Views and a view file Index.cshtml inside it: solution explorer view This is a razor view file. We’ll learn about creating views using the Razor syntax in detail in an upcoming article. For now, let’s just add some text inside the view file as below:
      @{
          ViewData["Title"] = "Index";
      }
      
      <h1>This is the book index generated by the view.</h1>
      
      Let ’s change the Index method of the BooksController as well, to return the view instead of a string:
      public IActionResult Index()
      {
         return View();
      }
      Now let’s run the application again. https://localhost:44323/books book details view page We can see that a new page is displayed based on the view file we just created. Also, we can see that a default layout template is applied, which we’ll revisit when we look at the layout files in a later article. So we have created a view file, returned it from the controller action method and verified that it is displayed when we ran the application.

      Defining Models

      So far we have seen the controllers and views in action. Now, let’s introduce the models into the equation. Let’s add a new class Book into the Models folder with some properties:
      public class Book
      {
          public int Id { get; set; }
      
          public string Title { get; set; }
      
          public string Genre { get; set; }
      
          public List<string> Authors { get; set; }
      
          public decimal Price { get; set; }
      
          public DateTime PublishDate { get; set; }
      }
      We’ll return this model in the Details action method of the BooksController. But before that, we need to create a view for displaying the book details. To do that, we are going to add a new view called Details as we did for the Index above. Let’s also modify the Details action method to return this view. We'll pass the model into the view and display the book details on the page. Ideally, we would fetch the model data from a database. We will learn how to do that in an upcoming article. For now, we’ll just generate some mock data to return:
      public IActionResult Details()
      {
           Book book = new Book()
           {
               Id = 1,
               Title = "Learning ASP.NET Core 2.0",
               Genre = "Programming & Software Development",
               Price = 45,
               PublishDate = new System.DateTime(2012, 04, 23),
               Authors = new List<string> { "Jason De Oliveira", "Michel Bruchet" }
           };
      
           return View(book);
      }
      Let’s also modify the view to display the model data:
      @model BookStore.Models.Book
      
      @{
          ViewData["Title"] = "Details";
      }
      
      <h1>Details</h1>
      
      <div>
          <h4>Book</h4>
          <hr />
          <dl class="row">
              <dt class="col-sm-2">
                  @Html.DisplayNameFor(model => model.Title)
              </dt>
              <dd class="col-sm-10">
                  @Html.DisplayFor(model => model.Title)
              </dd>
              <dt class="col-sm-2">
                  @Html.DisplayNameFor(model => model.Genre)
              </dt>
              <dd class="col-sm-10">
                  @Html.DisplayFor(model => model.Genre)
              </dd>
              <dt class="col-sm-2">
                  @Html.DisplayNameFor(model => model.Price)
              </dt>
              <dd class="col-sm-10">
                  @Html.DisplayFor(model => model.Price)
              </dd>
              <dt class="col-sm-2">
                  @Html.DisplayNameFor(model => model.PublishDate)
              </dt>
              <dd class="col-sm-10">
                  @Html.DisplayFor(model => model.PublishDate)
              </dd>
          </dl>
          <table>
              <thead>
                  <tr>
                      <th>
                          Authors
                      </th>
                  </tr>
              </thead>
              <tbody>
                  @foreach (var item in Model.Authors)
                  {
                      <tr>
                          <td>
                              @Html.DisplayFor(modelItem => item)
                          </td>
                      </tr>
                  }
              </tbody>
          </table>
      </div>
      <hr />
      <div>
          <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
          <a asp-action="Index">Back to List</a>
      </div>
      
      Here we use the approach of strongly typed models. By including a @model statement at the top of the view file, we specify the type of object that the view expects. So here our view expects a model of type Book. We can access any property of the class Book via IntelliSense available in the Visual Studio. Next, we define an HTML template for displaying the view data. DisplayNameFor() and DisplayFor() are HTML Helper methods which show the name and value of the properties in the model. Model.Authors is a collection and we use the @foreach syntax to iterate through it and display the values. Now let’s run the app and navigate to the details page once again: book details with view Voila! We have now created an MVC app with Models, Views and Controllers. Next, let’s look into validating the model using data annotations.

      Data Annotations

      Data annotations provide a built-in set of validation attributes that we apply declaratively to any class or property. It also contains attributes that help with formatting the data:
          public class Book
          {
              public int Id { get; set; }
      
              [Display(Name = "Book Title")]
              [Required]
              [StringLength(maximumLength: 20, ErrorMessage = "The Title length should be between 2 and 20.", MinimumLength = 2)]
              public string Title { get; set; }
      
              public string Genre { get; set; }
      
              public List<string> Authors { get; set; }
      
              [DataType(DataType.Currency)]
              [Range(1, 100)]
              public decimal Price { get; set; }
      
              [Display(Name = "Publish Date")]
              [DataType(DataType.Date)]
              public DateTime PublishDate { get; set; }
          }
      
      In the above code, we have applied some annotations to the Book model class.   The validation attributes specify behavior that you want to enforce on the model properties they're applied to: The Required  attribute indicates that a property must have a value.  Using a MinimumLength attribute indicates that the property should have a minimum length which also means it cannot be empty. The RegularExpression attribute is used to limit what characters can be input. By using the Range attribute, we can constrain the value of a property within a specified range. The StringLength attribute lets us set the maximum length of a string property, and optionally its minimum length. DataTypes are used to specify the data type of the fields and are inherently required and don't need the Required attribute. Now let’s run the app once again and navigate to the book details page: Notice that the Title has now changed to Book Title and PublishDate to Publish Date as we have applied the Display attribute. Also, note that the Price and Publish Date is formatted as currency and date for the specific locale. Now let’s create a page for adding a new book and see the validations in action. In the controller, we’ll add two Create methods:
       public IActionResult Create()
       {
           return View();
       }
      
       [HttpPost]
       [ValidateAntiForgeryToken]
       public IActionResult Create(Book book)
       {
           if (ModelState.IsValid)
           {
               // Logic to add the book to DB
               return RedirectToAction("Index");
           }
           return View(book);
       }

      Create View

      The first Create action method displays the initial create form. Let's create a view for this action. To do that we are going to right-click on the first Create action and choose the Add View option (as we did for the Index and Details actions). In the next window, we are going to give a name to the view, to select a template for the view and to select a model class to connect this view with: creating view with template   After the view is created, just remove the div part which generates the Id control, because we don't need that for the Create view. Second Create method has a [HttpPost] attribute which signals that only POST requests can be handled by it. Since this is a post request and we’re submitting a form, we can use ModelState.IsValid to check whether the Book has any validation errors. Calling this method evaluates any validation attributes that have been applied to the object. If the object does not satisfy our validation criteria, the Create method re-displays the form. If there are no errors, the method should ideally save the new book in the database. (Not implemented now) By clicking the Create button without entering valid data, we’ll see the validation messages: Book create page with validation errors So we have successfully implemented model validations and data formatting using data annotations.

      Conclusion

      In this article, we looked at the following topics:
      • How to create an ASP.NET MVC Core project
      • Examining the project structure
      • Adding controllers, views, and models
      • Validating and formatting data using annotations
      In the next part of this series, we’re going to learn how to handle data in an ASP.NET Core MVC. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      5513 0 0 0 1356 0 0 1358 1356 0 1576 0 0 1578 1576 0
      C# Design Patterns - Builder Design Pattern and Fluent Builder https://code-maze.com/builder-design-pattern/ Mon, 11 Feb 2019 07:10:54 +0000 https://code-maze.com/?p=47228
    • Builder Design Pattern and Fluent Builder (Current article)
    • Fluent Builder Interface With Recursive Generics
    • Faceted Builder
    • Factory Method
    • Singleton
    • Adapter
    • Composite
    • Decorator
    • Command
    • Strategy
    • [sc name="part_of_series" headline="This article is part of the series"] In this article, we are going to show you how to implement the Builder pattern to avoid such complex constructors. We will go even further and explain the Builder Recursive Generics design pattern and Faceted Builder design pattern as well in our next articles. You can download the source code from here: Builder Design Pattern - Source Code For the main page of this series check out C# Design Patterns. This article is divided into the following sections:

      Implementing the Builder Design Pattern

      We are going to write a simple example of creating a stock report for all the products in our store. So, let us start with a simplified Product class:
      public class Product
      {
          public string Name { get; set; }
          public double Price { get; set; }
      }
      
      We are going to use this class just for storing some basic data about a single product. Our stock report object is going to consist of the header, body and footer parts. So, it is quite logical to divide the object building process into those three actions. First, let’s start with the ProductStockReport class:
      public class ProductStockReport
      {
          public string HeaderPart { get; set; }
          public string BodyPart { get; set; }
          public string FooterPart { get; set; }
      
          public override string ToString() =>
              new StringBuilder()
              .AppendLine(HeaderPart)
              .AppendLine(BodyPart)
              .AppendLine(FooterPart)
              .ToString();
      }
      
      This is the object, we are going to build with the Builder design pattern. To continue on, we need a builder interface to organize the building process:
      public interface IProductStockReportBuilder
      {
          void BuildHeader();
          void BuildBody();
          void BuildFooter();
          ProductStockReport GetReport();
      }
      
      We can see that the concrete builder class which is going to implement this interface, needs to create all the parts for our stock report object and return that object as well. So, let’s implement our concrete builder class:
      public class ProductStockReportBuilder : IProductStockReportBuilder
      {
          private ProductStockReport _productStockReport;
          private IEnumerable<Product> _products;
      
          public ProductStockReportBuilder(IEnumerable<Product> products)
          {
              _products = products;
              _productStockReport = new ProductStockReport();
          }
      
          public void BuildHeader()
          {
              _productStockReport.HeaderPart = $"STOCK REPORT FOR ALL THE PRODUCTS ON DATE: {DateTime.Now}\n";
          }
      
          public void BuildBody()
          {
              _productStockReport.BodyPart = string.Join(Environment.NewLine, _products.Select(p => $"Product name: {p.Name}, product price: {p.Price}"));
          }
      
          public void BuildFooter()
          {
              _productStockReport.FooterPart = "\nReport provided by the IT_PRODUCTS company.";
          }
      
          public ProductStockReport GetReport()
          {
              var productStockReport = _productStockReport;
              Clear();
              return productStockReport;
          }
      
          private void Clear() => _productStockReport = new ProductStockReport();
      }
      
      This logic is quite straight forward. We receive all the products required for our report and instantiate the _productStockReport object. Then, we create all the parts of our object and finally return it. In the GetReport method, we reset our object and prepare a new instance to be ready to create another report. This is usual behavior but it is not mandatory. Once our building logic is over, we can start building our object in a client class or even encapsulate the building process from the client class inside a Director class. Well, this is exactly what we are going to do:
      public class ProductStockReportDirector
      {
          private readonly IProductStockReportBuilder _productStockReportBuilder;
      
          public ProductStockReportDirector(IProductStockReportBuilder productStockReportBuilder)
          {
              _productStockReportBuilder = productStockReportBuilder;
          }
      
          public void BuildStockReport()
          {
              _productStockReportBuilder.BuildHeader();
              _productStockReportBuilder.BuildBody();
              _productStockReportBuilder.BuildFooter();
          }
      }
      

      Creating  the StockReport Object

      After we have finished all this work, we can start building our object:
      class Program
      {
          static void Main(string[] args)
          {
              var products = new List<Product>
              {
                  new Product { Name = "Monitor", Price = 200.50 },
                  new Product { Name = "Mouse", Price = 20.41 },
                  new Product { Name = "Keyboard", Price = 30.15}
              };
      
              var builder = new ProductStockReportBuilder(products);
              var director = new ProductStockReportDirector(builder);
              director.BuildStockReport();
      
              var report = builder.GetReport();
              Console.WriteLine(report);
          }
      }
      
      The result : Builder Design Pattern - Result Excellent. We have created our object with the Builder design pattern.

      Fluent Builder

      The Fluent builder is a small variation of the Builder design pattern, which allows us to chain our builder calls towards different actions. To implement the Fluent builder, we are going to change the builder interface first:
      public interface IProductStockReportBuilder
      {
          IProductStockReportBuilder BuildHeader();
          IProductStockReportBuilder BuildBody();
          IProductStockReportBuilder BuildFooter();
          ProductStockReport GetReport();
      }
      
      We have to modify the implementation of the ProductStockReportBuilder class as well:
      public IProductStockReportBuilder BuildHeader()
      {
          _productStockReport.HeaderPart = $"STOCK REPORT FOR ALL THE PRODUCTS ON DATE: {DateTime.Now}\n";
          return this;
      }
      
      public IProductStockReportBuilder BuildBody()
      {
          _productStockReport.BodyPart = string.Join(Environment.NewLine, _products.Select(p => $"Product name: {p.Name}, product price: {p.Price}"));
          return this;
      }
      
      public IProductStockReportBuilder BuildFooter()
      {
          _productStockReport.FooterPart = "\nReport provided by the IT_PRODUCTS company.";
          return this;
      }
      
      As a result of these modifications, we can chain the calls in the Director class:
      public void BuildStockReport()
      {
          _productStockReportBuilder
              .BuildHeader()
              .BuildBody()
              .BuildFooter();
      }
      
      If we start our application now, the result is going to be the same, but this time we use the fluent interface.

      Conclusion

      In this article, we have learned about how to create Builder Design Pattern and how to implement it into our project to create complex objects. Furthermore, we have expanded our example to use the Fluent interface, which allows us to chain our Builder calls together. In the next article, we are going to learn how to implement Fluent Builder Interface With Recursive Generics. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47228 0 0 0 1151 0 0 1153 1151 0 1658 0 0 1659 1658 0
      C# Design Patterns - Fluent Builder Interface With Recursive Generics https://code-maze.com/fluent-builder-recursive-generics/ Tue, 12 Feb 2019 07:11:56 +0000 https://code-maze.com/?p=47234 Builder and the Fluent Builder design patterns. So, we strongly recommend reading that one before you continue with this post if you are not familiar with the Fluent Builder pattern. In this post, we are going to get a Fluent Builder to a higher level and show how we can use generics while inheriting from another Fluent Builder. When builders inherit from other builders, nothing particular is going to happen and everything should remain the same. But if one Fluent Builder inherits from another one, well, we are going to have a problem with chaining actions. Therefore, we are going to use a Recursive Generics approach to enable the default behavior of our fluent interfaces. [sc name="part_of_series" headline="This article is part of the series"] You can download the source code from here: Fluent Builder With Recursive Generics - Source Code For the complete list of articles from this series check out C# Design Patterns. This article is divided into the following sections:

      The Problem With the Fluent Builder Inheritance

      Let’s imagine that we want to build an Employee object. So obviously, the first thing to do is to create our model class:
      public class Employee
      {
          public string Name { get; set; }
          public string Position { get; set; }
          public double Salary { get; set; }
      
          public override string ToString()
          {
              return $"Name: {Name}, Position: {Position}, Salary: {Salary}";
          }
      }
      
      To continue on, we are going to create a builder class to build the Name part of our object:
      public class EmployeeInfoBuilder
      {
          protected Employee employee = new Employee();
      
          public EmployeeInfoBuilder SetName(string name)
          {
              employee.Name = name;
              return this;
          }
      }
      
      Now, we can create another builder class to build the Position part, and that class is going to inherit from the EmployeeInfoBuilder class because we want to reuse our employee object:
      public class EmployeePositionBuilder: EmployeeInfoBuilder
      {
          public EmployeePositionBuilder AtPosition(string position)
          {
              employee.Position = position;
              return this;
          }
      }
      
      Finally, we can start making calls towards this builder classes: Fluent Builder Recursive Generics - Chain problem But, as we can see, we are not able to create a required object. This is because the SetName method will return an instance of type EmployeeInfoBuilder which currently doesn’t implement or inherit the AtPosition method. This is quite okay since the EmployeeInfoBuilder class is a higher order class and the EmployeePositionBuilder class inherits from it, and not another way around. So, we need to find a solution to propagate information from the derived class to the base class. And the solution is in recursive generics approach.

      Implementing Recursive Generics with Fluent Builder

      So, let’s start with the EmployeeBuilder abstract class, which will be responsible for instantiating and providing our employee object:
      public abstract class EmployeeBuilder
      {
          protected Employee employee;
      
          public EmployeeBuilder()
          {
              employee = new Employee();
          }
      
          public Employee Build() => employee;
      }
      
      Once we are done, we can continue to the EmployeeInfoBuilder modification. We have seen that the SetName can’t return the EmployeeInfoBuilder type, it needs to return a generic type. Having this in mind, let’s modify our class:
      public class EmployeeInfoBuilder<T>: EmployeeBuilder where T: EmployeeInfoBuilder<T>
      {
          public T SetName(string name)
          {
              employee.Name = name;
              return (T)this;
          }
      }
      
      Ok, what??? Well, it is not that complicated as it may look like at a first glance. We’ve said that the SetName method needs to return a generic type, therefore our class is generic as well. It needs to inherit from the EmployeeBuilder class because we need that employee object. Finally, we must make sure to get the right type for the T type in our class. We can achieve this by restricting our T type to the EmployeeInfoBuilder type. Now we can continue to the EmployeePositionBuilder modification:
      public class EmployeePositionBuilder<T>: EmployeeInfoBuilder<EmployeePositionBuilder<T>> where T: EmployeePositionBuilder<T>
      {
          public T AtPosition(string position)
          {
              employee.Position = position;
              return (T)this;
          }
      }
      
      By doing this we enabled inheritance in both of these classes. They support the fluent builder interface approach and they can return the required type now. This is very useful in our case because our employee needs his salary. We can easily add the salary now by using the WithSalary method of the EmployeeSalaryBuilder class:
      public class EmployeeSalaryBuilder<T>: EmployeePositionBuilder<EmployeeSalaryBuilder<T>> where T: EmployeeSalaryBuilder<T>
      {
          public T WithSalary(double salary)
          {
              employee.Salary = salary;
              return (T)this;
          }
      }
      
      At this moment, we know how to create Builder classes with recursive generics. But we can’t start building our object yet. That’s because it is not entirely clear which type we should when instantiating the EmployeeInfoBuilder class. Therefore, we are going to create an API to allow the building of our object:
      public class EmployeeBuilderDirector : EmployeeSalaryBuilder<EmployeeBuilderDirector>
      {
           public static EmployeeBuilderDirector NewEmployee => new EmployeeBuilderDirector()
      }
      
      Now we can start building our object in a fluent way:
      class Program
      {
          static void Main(string[] args)
          {
              var emp = EmployeeBuilderDirector
                  .NewEmployee
                  .SetName("Maks")
                  .AtPosition("Software Developer")
                  .WithSalary(3500)
                  .Build();
      
              Console.WriteLine(emp);
          }
      }
      
      Awesome. Now we know how to enable fluent interface inheritance by using a recursive generics approach.

      Conclusion

      In the next article, which is going to be related to the Builder pattern again, we are going to talk about Faceted Builder and show you how to use a facade to create an object which requires more than one builder class. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47234 0 0 0 638 public interface IEmployeeBuilder { IEmployeeBuilder SetName(string name); IEmployeeBuilder AtPosition(string position); IEmployeeBuilder WithSalary(double salary); Employee Build(); } public class EmployeeSalaryBuilder : EmployeePositionBuilder, IEmployeeBuilder { IEmployeeBuilder IEmployeeBuilder.SetName(string name) { SetName(name); return this; } IEmployeeBuilder IEmployeeBuilder.AtPosition(string position) { AtPosition(position); return this; } public IEmployeeBuilder WithSalary(double salary) { employee.Salary = salary; return this; } } ]]> 0 0 700 638 0 763 0 0 764 763 0 996 0 0 1050 public abstract class Employee with T : Employee { public string Name { get; set; } public string Position { get; set; } public double Salary { get; set; } public abstract string ToString(); public abstract class Builder { public abstract Builder WithSalary(double salary); } } public abstract class Manager : Employee with T : Employee { public string Name { get; set; } public string Position { get; set; } public double Salary { get; set; } public abstract string ToString(); public abstract class Builder { public abstract Builder WithSalary(double salary); } } Can't quite get my head around it.]]> 0 0 1392 .AddSoftwareDeveloper(name) where extension method would use something like this behind the scenes: .SetName(name) .AtPosition("Software Developer") .WithSalary(3500) A lot of possibilities with this pattern! Can't wait to have fun with it! :D]]> 0 0 1393 1392 0
      C# Design Patterns - Faceted Builder https://code-maze.com/faceted-builder/ Wed, 13 Feb 2019 07:11:57 +0000 https://code-maze.com/?p=47241 Builder Design Pattern and Fluent Builder With Recursive Generics. We recommend reading at least the first one for a better understanding of the Builder Design Pattern. The second one is the upgrade to the first article and if you want to learn more about using recursive generics with the Builder Pattern, then we recommend reading that one as well. [sc name="part_of_series" headline="This article is part of the series"] Why do we need the Faceted Builder? Sometimes we may have a complex object, and the creational process requires more than one builder class. So, what we need to do is to introduce multiple builder classes in such a way, that we can jump from one builder to another while creating our object. Faceted Builder approach helps us a lot in that process because we create a facade over our builders and it allows us to use all the builders to create a single object. Let’s learn how to do that. The source code is available at Faceted Builder - Source Code. For the main page of this series check out C# Design Patterns. This article is divided into the following sections:

      Faceted Builder Implementation

      We are going to start with a “complex” object model:
      public class Car
      {
          public string Type { get; set; }
          public string Color { get; set; }
          public int NumberOfDoors { get; set; }
      
          public string City { get; set; }
          public string Address { get; set; }
      
          public override string ToString()
          {
              return $"CarType: {Type}, Color: {Color}, Number of doors: {NumberOfDoors}, Manufactured in {City}, at address: {Address}";
          }
      } 
      
      We have the info part and the address part of our object, so we are going to use two builders to create this whole object. As we said, we need a facade, so, let’s start with that:
      public class CarBuilderFacade
      {
          protected Car Car { get; set; }
      
          public CarBuilderFacade()
          {
              Car = new Car();
          }
      
          public Car Build() => Car;
      }
      
      We instantiate the Car object, which we want to build and expose it through the Build method. What we need now is to create concrete builders. So, let’s start with the CarInfoBuilder which needs to inherit from the facade class:
      public class CarInfoBuilder: CarBuilderFacade
      {
          public CarInfoBuilder(Car car)
          {
              Car = car;
          }
      
          public CarInfoBuilder WithType(string type)
          {
              Car.Type = type;
              return this;
          }
      
          public CarInfoBuilder WithColor(string color)
          {
              Car.Color = color;
              return this;
          }
      
          public CarInfoBuilder WithNumberOfDoors(int number)
          {
              Car.NumberOfDoors = number;
              return this;
          }
      }
      
      As we can see, we receive, through the constructor, an object we want to build and use the fluent interface for building purpose. Let’s do the same for the CarAddresBuilder class:
      public class CarAddressBuilder: CarBuilderFacade
      {
          public CarAddressBuilder(Car car)
          {
              Car = car;
          }
      
          public CarAddressBuilder InCity(string city)
          {
              Car.City = city;
              return this;
          }
      
          public CarAddressBuilder AtAddress(string address)
          {
              Car.Address = address;
              return this;
          }
      }
      
      At this moment we have both builder classes, but we can’t start building our object yet because we haven’t exposed our builders inside the facade class. Well, let’s do that:
      public class CarBuilderFacade
      {
          protected Car Car { get; set; }
      
          public CarBuilderFacade()
          {
              Car = new Car();
          }
      
          public Car Build() => Car;
      
          public CarInfoBuilder Info => new CarInfoBuilder(Car);
          public CarAddressBuilder Built => new CarAddressBuilder(Car);
      }
      
      As of this moment, we can start building our object:
      class Program
      {
          static void Main(string[] args)
          {
              var car = new CarBuilderFacade()
                  .Info
                    .WithType("BMW")
                    .WithColor("Black")
                    .WithNumberOfDoors(5)
                  .Built
                    .InCity("Leipzig ")
                    .AtAddress("Some address 254")
                  .Build();
      
              Console.WriteLine(car);
          }
      }
      
      And that is it. We have built our object with the Faceted Builder approach.

      Conclusion

      With this article, we are finished with the small Builder Design Pattern series, but we have more articles to come about Design Patterns in C#. So stay tuned. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47241 0 0 0 613 http://noveltheory.com 0 0 695 613 0 1604 0 0 1605 var car = new CarBuilderFacade(new Car()) So, I would say it is all based on your preference, how you want to implement your solution. Anyhow, thank you a lot for your suggestion and for reading this article. Best regards.]]> 1604 0
      C# Design Patterns - Factory Method https://code-maze.com/factory-method/ Mon, 18 Feb 2019 07:00:35 +0000 https://code-maze.com/?p=47324
    • Builder Design Pattern and Fluent Builder
    • Fluent Builder Interface With Recursive Generics 
    • Faceted Builder 
    • Factory Method (Current article)
    • Singleton
    • Adapter
    • Composite
    • Decorator
    • Command
    • Strategy
    • [sc name="part_of_series" headline="This article is part of the series"] We are going to start with the simple factory implementation, and we’re gradually going to improve to achieve a very usable, but readable factory for our objects. The source code is available at the Factory Method - Source Code. For the main page of this series check out C# Design Patterns. This article is divided into the following sections:

      Factory Method Implementation

      To implement a Factory method pattern, we are going to create a simple Air conditioner application. Our app will receive an input from a user and based on that input will trigger a required action (cooling or warming the room). So let’s start with an interface:
      public interface IAirConditioner
      {
          void Operate();
      }
      
      Now, we need concrete classes to implement this interface:
      public class Cooling : IAirConditioner
      {
          private readonly double _temperature;
      
          public Cooling(double temperature)
          {
              _temperature = temperature;
          }
      
          public void Operate()
          {
              Console.WriteLine($"Cooling the room to the required temperature of {_temperature} degrees");
          }
      }
      
      public class Warming : IAirConditioner
      {
          private readonly double _temperature;
      
          public Warming(double temperature)
          {
              _temperature = temperature;
          }
      
          public void Operate()
          {
              Console.WriteLine($"Warming the room to the required temperature of {_temperature} degrees.");
          }
      }
      
      Great. We have prepared our base functionality. Now let’s create a factory creator for these objects.

      Factory Classes

      We are going to start with the AirConditionerFactory abstract class:
      public abstract class AirConditionerFactory
      {
          public abstract IAirConditioner Create(double temperature);
      }
      
      This abstract class provides an interface for object creation in derived classes. That being said, let’s implement our concrete creator classes:
      public class CoolingFactory : AirConditionerFactory
      {
          public override IAirConditioner Create(double temperature) => new Cooling(temperature);
      }
      
      public class WarmingFactory : AirConditionerFactory
      {
          public override IAirConditioner Create(double temperature) => new Warming(temperature);
      }
      
      Excellent. Now we are ready to start using our Factory methods. In many examples, we can see the switch statement which switches through the user's input and selects the required factory class. That works just fine. But imagine if we have a lot of factory classes, which is quite common in large projects. That would lead to a quite big switch case statement which is quite unreadable. Therefore, we are going to use another approach.

      Factory Execution

      Let’s start with a simple enumeration to define air conditioner actions:
      public enum Actions
      {
          Cooling,
          Warming
      }
      
      To continue on, we are going to create the AirConditioner class where the user can specify the type of action and execute the appropriate factory. Our concrete factories inherit from the abstract class and we are going to use that structure in our further implementation:
      public class AirConditioner
      {
          private readonly Dictionary<Actions, AirConditionerFactory> _factories;
      
          public AirConditioner()
          {
              _factories = new Dictionary<Actions, AirConditionerFactory>
              {
                  { Actions.Cooling, new CoolingFactory() },
                  { Actions.Warming, new WarmingFactory() }
              };
          }
      }
      
      This is a better way of implementing our factory execution than using a switch-case statement. But we can do it in another more dynamic way, where we don’t have to manually add action and factory creator for each action. Let's introduce reflection into our project:
      public class AirConditioner
      {
          private readonly Dictionary<Actions, AirConditionerFactory> _factories;
      
          public AirConditioner()
          {
              _factories = new Dictionary<Actions, AirConditionerFactory>();
      
              foreach (Actions action in Enum.GetValues(typeof(Actions)))
              {
                  var factory = (AirConditionerFactory)Activator.CreateInstance(Type.GetType("FactoryMethod." + Enum.GetName(typeof(Actions), action) + "Factory"));
                  _factories.Add(action, factory);
              }
          }
      }
      
      Whether we choose the first or the second example, the result should be the same: Factory Method - factory class There is one more thing we need to add to this class. And that is the method which is going to execute appropriate creation:
      public class AirConditioner
      {
          //previous constructor code
      
          public IAirConditioner ExecuteCreation(Actions action, double temperature) =>_factories[action].Create(temperature);
      }
      
      Now, we just have to make a call from a client. In a real-world project, we would surely check first for the current temp and then just have a factory to decide whether we should lower it or make it higher. But for sake of simplicity, we are just going to make a simple call towards our AirCOnditioner class:
      class Program
      {
          static void Main(string[] args)
          {
              var factory = new AirConditioner().ExecuteCreation(Actions.Cooling, 22.5);
              factory.Operate();
          }
      }
      
      Our result should be as expected: Finished Factory Method

      Using the Factory Method Refactoring Technique

      We can use the Factory method to replace our constructor while creating an object. If our constructor consists of lots of code, we should replace it with the factory method. Furthermore, we can have multiple factory methods with meaningful names and parameter names as well which replace a single constructor. This improves code readability a lot. Finally, it helps us to implement a chaining syntax. So let’s modify the AirConditioner class with the factory method:
      public class AirConditioner
      {
          private readonly Dictionary<Actions, AirConditionerFactory> _factories;
      
          private AirConditioner()
          {
              _factories = new Dictionary<Actions, AirConditionerFactory>();
      
              foreach (Actions action in Enum.GetValues(typeof(Actions)))
              {
                  var factory = (AirConditionerFactory)Activator.CreateInstance(Type.GetType("FactoryMethod." + Enum.GetName(typeof(Actions), action) + "Factory"));
                      _factories.Add(action, factory);
              }
          }
      
          public static AirConditioner InitializeFactories() => new AirConditioner();
      
          public IAirConditioner ExecuteCreation(Actions action, double temperature) =>_factories[action].Create(temperature);
      }
      
      Our client call should be modified as well:
      class Program
      {
          static void Main(string[] args)
          {
              AirConditioner
                  .InitializeFactories()
                  .ExecuteCreation(Actions.Cooling, 22.5)
                  .Operate();
          }
      }
      
      Excellent. The result should be the same, but now we are using the factory method refactoring technique.

      Conclusion

      By reading this article, we have learned:
      • How to implement the Factory Method design pattern into the application
      • Several ways of replacing switch-case statements by using a dictionary or reflection
      • How to refactor your code by using the Factory Method Refactoring Technique
      [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47324 0 0 0 842 840 0 840 0 0 614 0 0 624 614 0 686 614 0 923 0 0 926 923 0 1090 614 0 1091 1090 0 1170 GetType().Namespace+"." instead of the string where the namespace should be (took me a while to figure out that you have to manually append the "." Dot operator. this way the code will work in any namespace it is pasted into!]]> 1090 0 1213 typeof(AirConditioner).Namespace + "."]]> 0 0 1214 1213 0
      C# Design Patterns - Singleton https://code-maze.com/singleton/ Mon, 25 Feb 2019 07:25:24 +0000 https://code-maze.com/?p=47488
    • Builder Design Pattern and Fluent Builder
    • Fluent Builder Interface With Recursive Generics 
    • Faceted Builder 
    • Factory Method 
    • Singleton (Current article)
    • Adapter
    • Composite
    • Decorator
    • Command
    • Strategy
    • [sc name="part_of_series" headline="This article is part of the series"] If you want to see a logger in action in ASP.NET Core Web API, you can read this article: ASP.NET Core – Logging with NLog. Or maybe sometimes we have a task to read some data from a file and use them through our project. If we know for sure that file won’t change while we read it, we can create a single instance of the object which will read that file and share it through the project to the consumer classes. In this article, we are going to show you how to properly implement a Singleton pattern in your project. When we say properly we mean that our singleton class is going to be a thread-safe which is a crucial requirement when implementing a Singleton pattern. So, let’s begin. The source code is available at the Singleton Design Pattern - Source Code. For the complete list of articles from this series check out C# Design Patterns. This article is divided into the following sections:

      Initial Project

      We are going to start with a simple console application in which we are going to read all the data from a file (which consists of cities with their population) and then use that data. So, to start off, let’s create a single interface:
      public interface ISingletonContainer
      {
          int GetPopulation(string name);
      }
      
      After that, we have to create a class to implement the ISingletonContainer interface. We are going to call it SingletonDataContainer:
      public class SingletonDataContainer: ISingletonContainer
      {
          private Dictionary<string, int> _capitals = new Dictionary<string, int>();
      
          public SingletonDataContainer()
          {
              Console.WriteLine("Initializing singleton object");
      
              var elements = File.ReadAllLines("capitals.txt");
              for (int i = 0; i < elements.Length; i+=2)
              {
                  _capitals.Add(elements[i], int.Parse(elements[i + 1]));
              }
          }
      
          public int GetPopulation(string name)
          {
              return _capitals[name];
          }
      }
      
      So, we have a dictionary in which we store the capital names and their population from our file. As we can see, we are reading from a file in our constructor. And that is all good. Now we are ready to use this class in any consumer by simply instantiating it. But is this really what we need to do, to instantiate the class which reads from a file which never changes (in this particular project. Population of the cities is changing daily). Of course not, so obviously using a Singleton pattern would be very useful here. So, let’s implement it.

      Singleton Implementation

      To implement the Singleton pattern, let’s change the SingletonDataContainer class:
      public class SingletonDataContainer: ISingletonContainer
      {
          private Dictionary<string, int> _capitals = new Dictionary<string, int>();
      
          private SingletonDataContainer()
          {
              Console.WriteLine("Initializing singleton object");
      
              var elements = File.ReadAllLines("capitals.txt");
              for (int i = 0; i < elements.Length; i+=2)
              {
                  _capitals.Add(elements[i], int.Parse(elements[i + 1]));
              }
          }
      
          public int GetPopulation(string name)
          {
              return _capitals[name];
          }
      
          private static SingletonDataContainer instance = new SingletonDataContainer();
      
          public static SingletonDataContainer Instance => instance;
      }
      
      So, what we’ve done here is that we hid our constructor from the consumer classes by making it private. Then, we’ve created a single instance of our class and exposed it through the Instance property. At this point, we can call the Instance property as many times as we want, but our object is going to be instantiated only once and shared for every other call. Let’s check that theory:
      class Program
      {
          static void Main(string[] args)
          {
              var db = SingletonDataContainer.Instance;
              var db2 = SingletonDataContainer.Instance;
              var db3 = SingletonDataContainer.Instance;
              var db4 = SingletonDataContainer.Instance;
          }
      }
      
      If we start our app, we are going to see this: Singleton result We can see that we are calling our instance four times but it is initialized only once, which is exactly what we want. But our implementation is not ideal. Let’s construct our object the lazy way.

      Implementing a Thread-Safe Singleton

      Let’s modify our class to implement a thread-safe Singleton by using the Lazy type:
      public class SingletonDataContainer : ISingletonContainer
      {
          private Dictionary<string, int> _capitals = new Dictionary<string, int>();
      
          private SingletonDataContainer()
          {
              Console.WriteLine("Initializing singleton object");
      
              var elements = File.ReadAllLines("capitals.txt");
              for (int i = 0; i < elements.Length; i+=2)
              {
                  _capitals.Add(elements[i], int.Parse(elements[i + 1]));
              }
          }
      
          public int GetPopulation(string name)
          {
              return _capitals[name];
          }
      
          private static Lazy<SingletonDataContainer> instance = new Lazy<SingletonDataContainer>(() => new SingletonDataContainer());
      
          public static SingletonDataContainer Instance => instance.Value;
      }
      
      Right now, our class is completely thread-safe. It is loaded in a lazy way which means that our instance is going to be created only when it is actually needed. We can even check if our object is created with the IsValueCreated property if we need to. Excellent, we have finished our Singleton implementation. Now we can fully consume it in our consumer class:
      class Program
      {
          static void Main(string[] args)
          {
              var db = SingletonDataContainer.Instance;
              Console.WriteLine(db.GetPopulation("Washington, D.C."));
              var db2 = SingletonDataContainer.Instance;
              Console.WriteLine(db2.GetPopulation("London"));
          }
      }
      
      Great job.

      Conclusion

      We have seen that even though the Singleton pattern isn’t that much appreciated, it can be helpful in some cases. So, it is always up to us to decide whether we are going to use it or not. Bottom line is that if we need to apply a Singleton pattern in our project, this is a good way to do it. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47488 0 0 0 1446 0 0 739 0 0 740 739 0 1447 1446 3
      C# Design Patterns - Adapter https://code-maze.com/adapter/ Mon, 04 Mar 2019 07:01:49 +0000 https://code-maze.com/?p=47505
    • Builder Design Pattern and Fluent Builder
    • Fluent Builder Interface With Recursive Generics 
    • Faceted Builder 
    • Factory Method 
    • Singleton 
    • Adapter (Current article)
    • Composite
    • Decorator
    • Command
    • Strategy
    • [sc name="part_of_series" headline="This article is part of the series"] The source code is available at the Adapter Design Pattern - Source Code. For the main page of this series check out C# Design Patterns. This article is divided into the following sections:

      Initial Project

      Let’s imagine that we have functionality in which we convert the list of car manufacturers into JSON format and write it to the screen. But instead of a list, we have been provided with an API that provides us with all the manufacturers in the XML format. Let’s say we can’t modify the existing API functionality (because of the technical restrictions such as being imported into our project from another solution that we mustn’t modify or as a NuGet package) so we have to find a way around it. And the proper way to do it is to implement the Adapter pattern to solve this problem. Let’s start with the creation of the Manufacturer model and a simple object to XML converter example:
      public class Manufacturer
      {
          public string Name { get; set; }
          public string City { get; set; }
          public int Year { get; set; }
      }
      
      public static class ManufacturerDataProvider
      {
          public List<Manufacturer> GetData() =>
             new List<Manufacturer>
             {
                  new Manufacturer { City = "Italy", Name = "Alfa Romeo", Year = 2016 },
                  new Manufacturer { City = "UK", Name = "Aston Martin", Year = 2018 },
                  new Manufacturer { City = "USA", Name = "Dodge", Year = 2017 },
                  new Manufacturer { City = "Japan", Name = "Subaru", Year = 2016 },
                  new Manufacturer { City = "Germany", Name = "BMW", Year = 2015 }
             };
      }
      
      public class XmlConverter
      {
          public XDocument GetXML()
          {
              var xDocument = new XDocument();
              var xElement = new XElement("Manufacturers");
              var xAttributes = ManufacturerDataProvider.GetData()
                  .Select(m => new XElement("Manufacturer", 
                                      new XAttribute("City", m.City),
                                      new XAttribute("Name", m.Name),
                                      new XAttribute("Year", m.Year)));
      
              xElement.Add(xAttributes);
              xDocument.Add(xElement);
      
              Console.WriteLine(xDocument);
      
              return xDocument;
          }
      }
      
      As we can see, this is a pretty straightforward code. We are collecting manufacturer data, creating a root Manufacturers element and all the Manufacturer sub-elements with its attributes. After that, we are printing results to the console window to show how the final XML looks like. This is how the xDocument should look like: Xml Conver - Adapter Design Pattern Now let’s implement a JsonConverter class:
      public class JsonConverter
      {
          private IEnumerable<Manufacturer> _manufacturers;
      
          public JsonConverter(IEnumerable<Manufacturer> manufacturers)
          {
              _manufacturers = manufacturers;
          }
      
          public void ConvertToJson()
          {
              var jsonManufacturers = JsonConvert.SerializeObject(_manufacturers, Formatting.Indented);
      
              Console.WriteLine("\nPrinting JSON list\n");
              Console.WriteLine(jsonManufacturers);
          }
      } 
      
      This code is even simpler because we only serialize our manufacturer list into a JSON format. Of course, for the serialization to work we need to install the Newtonsoft.Json library, so don’t forget to do that. Excellent, we have our JSON functionality and the provided XML interface. But now, we need to solve a real problem. How to combine those two interfaces to accomplish our task, which is converting manufacturers from XML to JSON format.

      Adapter Implementation

      As we can see, there is no way to pass an xDocument to the JsonConverter class and there shouldn’t be one, so we need to create the adapter class which will make these two interfaces work together. To do this, we are going to start with the IXmlToJson interface to define the behavior of our adapter class:
      public interface IXmlToJson
      {
          void ConvertXmlToJson();
      }
      
      Then, let’s continue with the XmlToJsonAdapter class which is going to implement the IXmlToJson interface:
      public class XmlToJsonAdapter : IXmlToJson
      {
          private readonly XmlConverter _xmlConverter;
      
          public XmlToJsonAdapter(XmlConverter xmlConverter)
          {
              _xmlConverter = xmlConverter;
          }
      
          public void ConvertXmlToJson()
          {
              var manufacturers = _xmlConverter.GetXML()
                      .Element("Manufacturers")
                      .Elements("Manufacturer")
                      .Select(m => new Manufacturer
                                   {
                                      City = m.Attribute("City").Value,
                                      Name = m.Attribute("Name").Value,
                                      Year = Convert.ToInt32(m.Attribute("Year").Value)
                                   });
      
              new JsonConverter(manufacturers)
                  .ConvertToJson();
          }
      }
      
      Excellent. We have created our adapter class which converts the Xml document object into the list of manufacturers and provides that list to the JsonConverter class. So, as you can see, we have enabled collaboration between two completely different interfaces by just introducing an adapter class to our project. Now, we can make a call to this adapter class from our client class:
      class Program
      {
         static void Main(string[] args)
          {
              var xmlConverter = new XmlConverter();
              var adapter = new XmlToJsonAdapter(xmlConverter);
              adapter.ConvertXmlToJson();
          }
      }
      
      Once we start our application, we are going to see the following result: Adapter - JSON Convert Great job. We have finished our implementation.

      When to Use Adapter

      We should use the Adapter class whenever we want to work with the existing class but its interface is not compatible with the rest of our code. Basically, the Adapter pattern is a middle-layer which serves as a translator between the code implemented in our project and some third party class or any other class with a different interface. Furthermore, we should use the Adapter when we want to reuse existing classes from our project but they lack a common functionality. By using the Adapter pattern in this case, we don’t need to extend each class separately and create a redundant code.

      Conclusion

      The Adapter pattern is pretty common in the C# world and it is quite used when we have to adapt some existing classes to a new interface. It can increase a code complexity by adding additional classes (adapters) but it is worth an effort for sure. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47505 0 0 0 773 0 0 774 773 0 1311 0 0 1312 1311 0 1461 0 0 1462 1461 0
      C# Design Patterns - Composite https://code-maze.com/composite/ Mon, 11 Mar 2019 07:00:06 +0000 https://code-maze.com/?p=47532
    • Builder Design Pattern and Fluent Builder
    • Fluent Builder Interface With Recursive Generics 
    • Faceted Builder 
    • Factory Method 
    • Singleton 
    • Adapter
    • Composite (Current article)
    • Decorator
    • Command
    • Strategy
    • [sc name="part_of_series" headline="This article is part of the series"] The source code is available at the Composite Design Pattern - Source Code. For the main page of this series check out C# Design Patterns. This article is divided into the following sections:

      Composite Design Pattern Structure

      The Composite design pattern consists of the following parts:
      • Component
      • Leaf
      • Composite
      A component is an interface that describes operations that are common to either simple or complex elements of the tree. A leaf is a single object, that doesn’t have sub-elements. Our tree structure consists of more leaf objects. Composite is an object that does have sub-elements (leaves or other composite objects). Interesting thing is that the Composite object isn’t familiar with the concrete classes of its children. It communicates with its children via the Component interface. Finally, we have a client, which works with all the elements through the Component interface. But enough of the theory, let’s start with the concrete example.

      Composite Design Pattern Implementation

      Let’s imagine that we need to calculate the total price of a gift which we are selling in our shop. The gift could be a single element (toy) or it can be a complex gift that consists of a box with two toys and another box with maybe one toy and the box with a single toy inside. As we can see, we have a tree structure representing our complex gift so, implementing the Composite design pattern will be the right solution for us. So, let’s start with the Component part:
      public abstract class GiftBase
      {
          protected string name;
          protected int price;
      
          public GiftBase(string name, int price)
          {
              this.name = name;
              this.price = price;
         }
      
          public abstract int CalculateTotalPrice();
      }
      
      We can see that our component consists of two protected fields and one abstract method. These fields and method are going to be used as an interface between the Leaf and the Composite part of our pattern. Now, in many examples, we can see additional operations like add and remove inside the abstract class, but we are not going to add them in this class, because our Leaf class doesn’t need them. What we are going to create instead is a new interface - IGiftOperations:
      public interface IGiftOperations
      {
          void Add(GiftBase gift);
          void Remove(GiftBase gift);
      }
      
      Only our composite object will implement this interface, but the leaf object won’t. This is much better because our leaf object doesn’t need to implement the methods it won’t use.

      The Composite Class Implementation

      So, let’s continue on with the Composite class:
      public class CompositeGift : GiftBase, IGiftOperations
      {
          private List<GiftBase> _gifts;
      
          public CompositeGift(string name, int price)
              :base(name, price)
          {
              _gifts = new List<GiftBase>();
          }
      
          public void Add(GiftBase gift)
          {
              _gifts.Add(gift);
          }
      
          public void Remove(GiftBase gift)
          {
              _gifts.Remove(gift);
          }
      
          public override int CalculateTotalPrice()
          {
              int total = 0;
      
              Console.WriteLine($"{name} contains the following products with prices:");
      
              foreach (var gift in _gifts)
              {
                  total += gift.CalculateTotalPrice();
              }
      
              return total;
          }
      }
      
      The class implementation is pretty straightforward. First, we have the GiftBase type list in which we store our Leaf or other Composite objects. We can add or remove those objects from our list by implementing Add and Remove methods from our IGiftOperations interface. Finally, we are calculating the total price of our Gift object with all the sub-gifts inside it.

      The Leaf Class Implementation

      Let’s continue on with the leaf part:
      public class SingleGift : GiftBase
      {
          public SingleGift(string name, int price)
              :base(name, price)
          {
          }
      
          public override int CalculateTotalPrice()
          {
              Console.WriteLine($"{name} with the price {price}");
      
              return price;
          }
      }
      
      And that is all we need for the Leaf implementation because it doesn’t have a sub-levels so it doesn’t require to add or remove features at all. Finally, we can implement our client part:
      class Program
      {
          static void Main(string[] args)
          {
              var phone = new SingleGift("Phone", 256);
              phone.CalculateTotalPrice();
              Console.WriteLine();
      
              //composite gift
              var rootBox = new CompositeGift("RootBox", 0);
              var truckToy = new SingleGift("TruckToy", 289);
              var plainToy = new SingleGift("PlainToy", 587);
              rootBox.Add(truckToy);
              rootBox.Add(plainToy);
              var childBox = new CompositeGift("ChildBox", 0);
              var soldierToy = new SingleGift("SoldierToy", 200);
              childBox.Add(soldierToy);
              rootBox.Add(childBox);
      
              Console.WriteLine($"Total price of this composite present is: {rootBox.CalculateTotalPrice()}");
          }
      }
      
      We can see, that we are creating one gift with just one element inside it and one complex gift with the toys and an additional box with a single toy inside. So, as soon as we run our app, we are going to get this result: Composite - result Excellent. That wraps it up for the Composite design pattern.

      Conclusion

      Even though this pattern might look a bit complex, it is very beneficial to use it when we have complex tree structures in our code. Furthermore, by having a single interface that is shared by all the elements in the Composite pattern, the client doesn’t have to worry about the concrete class it works with. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47532 0 0 0 962 https://www.brandwebdirect.com/website-design-portfolio-samples/web-design-portfolio-sample 0 0
      Basic Tips and Tricks to Boost Productivity in Visual Studio https://code-maze.com/visual-studio-productivity/ Mon, 18 Mar 2019 07:00:14 +0000 https://code-maze.com/?p=47573
    • .NET Core Tutorial
    • Implementing Action Filters in ASP.NET Core
    • How to Easily Create a PDF Document in ASP.NET Core Web API
    • C# Basics Tutorial
    • ASP.NET Core Authentication with JWT and Angular
    • [sc name="part_of_series" headline="Recommended Articles"] In this article, we are going to talk about some of these Visual Studio features that helped us a lot in our careers and increased our productivity many times over. We are not going to talk about any of the plugins or additional tools that could be installed in Visual Studio in this article. We are going to work only with the Visual Studio built-in features. This article is divided into the following sections:

      Navigation in Visual Studio

      Let’s see how we can improve our navigation in Visual Studio.

      Navigation Through the Active Windows

      To navigate through the active windows in Visual Studio, the logical solution would be to use our mouse and open a required window. But, while we write our code, we want to execute our side actions as fast as possible without moving our hands from a keyboard. So instead of using a mouse, we can use the CTRL+TAB combination. As soon as we do that, we can select a required window with the arrow keys: [caption id="attachment_47574" align="alignnone" width="1287"]Navigation through the active windows - Vidusal studio productivity Click for the full-screen view[/caption]

      Navigation Through the Active Tabs

      In the same way that we navigate through the active windows, we can navigate through the active tabs. So, all we need to do is to press CTRL+TAB key combination and to navigate with arrow keys. Furthermore, we don’t have to use arrow keys at all, after we press a required key combination while holding the CTRL key, we can just press the TAB key to navigate through the active tabs. We can achieve the same thing with a different key combination CTRL+F6 or CTRL+SHIFT+F6 for the reverse navigation: [caption id="attachment_47575" align="alignnone" width="1287"]Navigation through the active tabs - Visual studio productivity Click for the full-screen view[/caption]

      Closing the Currently Active Tab

      When we want to close the tab we are currently at, instead of using a mouse to do that, we can press the CTRL+F4 key combination. This is a faster way and the tab is going to be closed for sure: [caption id="attachment_47576" align="alignnone" width="1287"]Closing the current tab - Visual Studio productivity Click for the full-screen view[/caption]

      Navigation Through the Elements of a Class or the Whole Project

      In a real-world project, we often find ourselves with many different classes and methods and finding a certain method in a class can take some time. We can speed that up by using a Class Navigation Menu in the upper right corner of Visual Studio. As soon as we click on the menu’s arrow it will show us all the class members. Once we click on the required element, Visual Studio will navigate us to the required selection: [caption id="attachment_47582" align="alignnone" width="1287"]Visual Studio class navigation Click for the full-screen view[/caption] To achieve a similar effect, we can use the GoTo options (Edit => Go To), which provide us with different navigation features. But the one that we use the most is GoTo All option, which we can activate by hitting the CTLR+T key combination. It will search for the required term in the current class or an entire project as well: [caption id="attachment_47583" align="alignnone" width="1287"]Using GoTo option in Visual Studio Click for the full-screen view[/caption]

      Navigation Through the Definition and Implementation

      When we want to see the definition of a member implemented in a class we are currently in, we can do that by right click on that member’s name and then choose the Go To Definition option. And this works well, but the faster and better way is to just place a cursor on that member we want to see the definition for and to hit the F12 button. Once we are done with the definition and want to go back, we can hit CTRL+ - key combination, to return to the location we came from. But in many cases inspecting the definition of the member isn’t enough for us. Instead, we want to see the implementation of a particular member. To do that, we can hit the CTRL+F12 key combination. To find all the references for the required member, we can use the SHIFT+F12 key combination. Then we can simply navigate through the opened window with all the references. Finally, we can examine the member’s definition but without moving to the file this definition exists in. To do that, we can use the ALT+F12 key combination: [caption id="attachment_47584" align="alignnone" width="1410"]Visual studio navigation and implementation Click for the full-screen view[/caption]

      Debugging

      Debugging is an inevitable process of software development and even though it helps us a lot to analyze the code, it could be time-consuming if not used properly. There are several techniques which can speed up the debugging process.

      Placeholders and Stepping Through Code

      Instead of placing breakpoints with the left mouse click and then selecting the breakpoint option, we can hit the F9 key to insert a breakpoint. After that, we can use the F5 key to start our app in a debugging mode. Once we are in debugging mode, we can navigate through our code by using our mouse and commands that are usually on top of the Visual Studio’s screen: Visual Studio debugging commands But, of course, there is a better and faster solution without using a mouse and clicking on those buttons. That’s right, we can use our keyboard. To step over the line, we can use the F10 key and instead of the step into a button, we can use F11 key. Finally, instead of the step out button, we can use SHIFT+F11 key combination. Once we are finished with debugging, we can hit SHIFT+F5 to stop debugging: [caption id="attachment_47592" align="alignnone" width="1287"]Visual Studio placeholder and stepping Click for the full-screen view[/caption]

      Conditional Debugging

      Sometimes we have a situation where we need to debug through a list of items to figure out what is wrong with a certain item. If we imagine that we, for example, have up to a hundred items in our list and the required item is on the 80th place, we realize how time-consuming debugging would be. Fortunately, we can use conditional debugging to specify which item we want our breakpoint to be active for. To configure conditional debugging, we need to place a breakpoint first and then to use ALT+F9 key combination and right after to hit a C key: [caption id="attachment_47593" align="alignnone" width="1287"]Conditional Debugging in Visual Studio Click for the full-screen view[/caption]

      Debugging Threads in Visual Studio

      If we try to debug a code which is called from more than a single thread, that can prove to be quite challenging and time-consuming. That’s because our debugging process will switch from thread to thread, making us repeat debugging process for every new thread and continue debugging process for the previous ones. In order to avoid thread switching during debugging, we can lock our debug process for a single required thread. To do that, we have to repeat the steps required for the conditional debugging and then in the conditional window to select the Filter option in the Add condition input field. Finally, we have to add the thread id or a thread name in the last input field in the format „ThreadId = number“ or „ThreadName = “Name“ “: Visual studio multi thread debug

      Using Immediate Window

      We can use the Immediate window to debug and execute expressions in our project. It is quite helpful when we want to check the results of the expressions or the variable values during the debug process. Of course, it doesn’t have to be used only during the debug process. We can execute any statement in the design-time as well. To select the Immediate window, we can use the CTRL+ALT+I key combination: [caption id="attachment_47578" align="alignnone" width="1410"]Immediate window in Visual Studio Click for the full-screen view[/caption]

      Using Code Snippets

      The code snippets are quite an important feature in Visual Studio because they provide a faster way of writing code in our project. Visual Studio has a lot of those snippets, but we are going to show only those that we think are used the most.

      Snippet for Constructor

      To create a constructor in our code, all we have to do is to type ctor and press TAB: [caption id="attachment_47579" align="alignnone" width="886"]ctor snippet in Visual Studio Click for the full-screen view[/caption]

      Snippets for Properties

      To create auto-implemented property, we have to type the prop key. If we want to have a private setter in our property, we need the propg key. In case that we want a full property along with its private variable, we can type propfull. With the TAB key, we can switch between the yellow fields which act as placeholders for our inputs. Finally, once we are done, we can press the Enter key: [caption id="attachment_47586" align="alignnone" width="886"]Property snippet in Visual Studio Click for the full-screen view[/caption]

      Conditional Statements, Using and Try Catch

      To create an if-else statement in our code we can use two code snippets: if and else. For the using block, we are provided with the using code snippet. Of course, we all want to write safe code. So, in order to do that we can use try snippet or tryf to even add the finally block: [caption id="attachment_47587" align="alignnone" width="886"]Conditions TryCatch and Using snippets in Visual Studio Click for the full-screen view[/caption]

      Snippets for Loops and Console.WriteLine

      Loops are always an important part of our code, and of course, Visual Studio provides us with a couple of snippets for that as well:
      • for => generates a for loop
      • forr => reversed for loop
      • foreach => generates foreach loop
      • while => a snippet for the while loop
      • do => creates a do-while loop
      The faster way of creating the Console.WriteLine() statement is by using a cw snippet. There are various built-in code snippets in Visual Studio, and we can find them all by pressing the CTRL+K key combination and right after the X key: [caption id="attachment_47588" align="alignnone" width="886"]Loop snippets in Visual Studio Click for the full-screen view[/caption]

      Creating Custom Snippets

      If we have a code part (a parameterless method for example) which we use a lot in our project, it makes sense for us to create a snippet for that code. Let us show how to do that. The first thing we are going to do is to create an XML file named Method.xml:
      <?xml version="1.0" encoding="utf-8" ?>
      <CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
        <CodeSnippet Format="1.0.0">
          <Header>
            <Title>method</Title>
            <Shortcut>method</Shortcut>
            <Description>Code snippet for parameterless method</Description>
            <Author>Code Maze Blog</Author>
            <SnippetTypes>
              <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
          </Header>
          <Snippet>
            <Declarations>
              <Literal>
                <ID>type</ID>
                <Default>void</Default>
                <ToolTip>Type</ToolTip>
              </Literal>
              <Literal>
                <ID>name</ID>
                <Default>Method</Default>
                <ToolTip>Method Name</ToolTip>
              </Literal>
            </Declarations>
            <Code Language="csharp">
              <![CDATA[public $type$ $name$()
      	 {
      	    $selected$ $end$
      	 }]]>
            </Code>
          </Snippet>
        </CodeSnippet>
      </CodeSnippets>
      
      To continue on, we need to save this file but with the .snippet extension. So our file will have a Method.snippet name. Now, we need to import this file into the Code Snippet Manager. To do that, let’s take the following steps: [caption id="attachment_47589" align="alignnone" width="886"]Custom snippet in visual studio Click for the full-screen view[/caption]

      Additional Shortcuts to Increase Coding Speed

      So far, we have used many different shortcuts to show how we can improve our productivity In this section, we are going to introduce several more, that can help us speed up our development.

      New Files, Code Formatting and Outlining

      Instead of adding a new file in our project by right-clicking in the Solution Explorer window and then choosing the Add option, we can use the CTRL+SHIFT+A key combination. It will navigate us to the Class file by default or to the lastly added file. Visual Studio provides us with a great feature to format our code by simply pressing the CTRL+K, CTRL+D key combinations. This is going to format our code by the predefined rules, thus making it cleaner and readable as well. If you work in a big team we suggest you use default rules to avoid merge conflicts because of the different code formatting. If we want to collapse the code in our class, whether we want all the members or just the single one, we can do that with several key combinations. To toggle the selected part in our class (entire class, namespace or single method), we can use the CTRL+M, M key combination. But if we want to collapse all the members inside a class we can use the CTRL+M, O and expand CTRL+M, L key combinations: [caption id="attachment_47590" align="alignnone" width="1287"]Outlining in Visual Studio Click for the full-screen view[/caption]

      Extracting Code Parts

      It is quite common that we want to refactor our code by extracting some part of that code into a new method. If that occurs, instead of writing a new method manually, we can use the CTRL+R, M key combination. This way, we are going to have a new method automatically created and the call towards it as well. As well as we can extract part of our code into a new method, we can create a new interface from our existing class, which is always a preferable way because interfaces should emerge from the class implementations and not another way around. So, in order to extract a new interface from our class, we can use the CTRL+R,  I key combination, which is going to create a new interface with the selected members, and create inheritance between our class and a newly created interface: [caption id="attachment_47591" align="alignnone" width="886"]Code Extracting in Visual Studio Click for the full-screen view[/caption]

      Bookmarks, Renaming, Suggestions

      When working on projects, stuff comes up that draws our attention. Due to the lack of time to work on it at that moment, we want to come back to it later. In these situations, the best solution is to place a bookmark on that code line, to serve us as a reminder. To add a bookmark into the code, we have to use the CTRL+K, K key combination. The same key combination is used to remove a bookmark. We can list all the bookmarks by using the CTRL+W, B key combinations. When it comes to the rename actions, it could be misleading to think that this is just a simple delete and add new name action. But what if that member is used all over our class, or in many other classes as well, we want to rename those occurrences too. Well, there is an easier solution by pressing the F2 key on top of the member we want to rename. This action is going to rename the current member and all of its occurrences all over the code. Finally, suggestions are a very powerful feature in Visual Studio. It helps us to refactor our code, to import missing directives, fix errors in the code etc. So, instead of using a mouse click on the yellow bulb to show all the suggestions, we can use the CTRL+. (or ALT+ENTER) key combinations: [caption id="attachment_47580" align="alignnone" width="886"]Bookmarks, Rename, Suggestions in Visual Studio Click for the full-screen view[/caption]

      Working With File Content

      In this section, we are going to learn a few more interesting features which can improve our speed while working with the file content. To copy and paste a single line of code, the usual procedure is to mark the entire line then right mouse click and choose copy. Then we place our cursor on the desired location and right mouse click and paste. But, there is a faster way, all we have to do is to place our cursor on the required line and press CTRL+C and then the CTRL+V key combination. It will paste a new line right below the copied one. The same way is with the cut option, all we have to do is to press CTRL+X key combination. To cut an entire line of code, all we have to do is to press SHIFT+Delete key combination. To insert it, we can do it by pressing SHIFT+Insert combo. Sometimes if we want to execute the same action on multiple lines, instead of doing that line by line, we can use the CTRL+ALT+click combination and change multiple lines at the same time. Finally, if we want to switch place of a certain line or a full method, instead of cutting it and pasting it on a required location, we can use the ALT+arrow combination to execute that: [caption id="attachment_47581" align="alignnone" width="886"]Working with content in Visual Studio Click for the full-screen view[/caption]

      Conclusion

      We are sure that all of these features could improve developers productivity because they proved valuable in real-world practice. Of course, there are many more shortcuts provided by Visual Studio, and you can learn about those from Default keyboard shortcuts in Visual studio article. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47573 0 0 0
      C# Design Patterns - Decorator https://code-maze.com/decorator-design-pattern/ Sun, 24 Mar 2019 08:46:19 +0000 https://code-maze.com/?p=47710
    • Builder Design Pattern and Fluent Builder
    • Fluent Builder Interface With Recursive Generics 
    • Faceted Builder 
    • Factory Method 
    • Singleton 
    • Adapter
    • Composite
    • Decorator (Current article)
    • Command
    • Strategy
    • [sc name="part_of_series" headline="This article is part of the series"] The source code is available at the Decorator Design Pattern - Source Code. For the main page of this series check out C# Design Patterns. This article is divided into the following sections:

      About the Decorator Design Pattern

      A Decorator is a structural design pattern that allows us to extend the behavior of objects by placing these objects into a special wrapper class. The Decorator design pattern is quite popular in C# due to the fact that it helps us dynamically add behaviors to the wrapped objects. The structure of this pattern consists of a Component class and a Concrete Component class from one part and a Decorator class and a Concrete Decorator class on the other side. The Concrete Decorator class is going to add additional behavior to our Concrete Component. So, when do we use this pattern? Well, we should use this pattern when we have a need to add additional behavior to our objects. Furthermore, we should use it when it is too complicated to use inheritance or when it doesn’t make sense at all (too many inherit layers, urge to modify existing inheritance hierarchy by adding some additional layers, etc.). We are going to see how all these elements work together inside the Decorator design pattern through the practical example, which is going to make this pattern easier to comprehend.

      Decorator Design Pattern Implementation

      Let’s imagine that we have a simple application to calculate the total order price in our shop. But also, we have some additional requests. If a buyer orders our products in a preorder period we are going to give a 10 percent discount. So, let’s start first with our Component class:
      public abstract class OrderBase
      {
          protected List<Product> products = new List<Product>
          {
              new Product {Name = "Phone", Price = 587},
              new Product {Name = "Tablet", Price = 800},
              new Product {Name = "PC", Price = 1200}
          };
      
          public abstract double CalculateTotalOrderPrice();
      }
      
      The Component class contains functionality that will be shared with other Concrete Component classes. Having that in mind, let’s create the Concrete Components:
      public class RegularOrder : OrderBase
      {
          public override double CalculateTotalOrderPrice()
          {
              Console.WriteLine("Calculating the total price of a regular order");
              return products.Sum(x => x.Price);
          }
      }
      
      public class Preorder : OrderBase
      {
          public override double CalculateTotalOrderPrice()
          {
              Console.WriteLine("Calculating the total price of a preorder.");
              return products.Sum(x => x.Price) * 0.9;
          }
      }
      
      This code is pretty clean and easy to understand. We just override our abstract method CalculateOrderPrice and calculate the total price. So, right now, we can start using these concrete components:
      class Program
      {
          static void Main(string[] args)
          {
              var regularOrder = new RegularOrder();
              Console.WriteLine(regularOrder.CalculateTotalOrderPrice());
              Console.WriteLine();
      
              var preOrder = new PreOrder();
              Console.WriteLine(preOrder.CalculateTotalOrderPrice());
              Console.WriteLine();
          }
      }
      
      This works just fine. But now, we receive an additional request to allow an additional 10 percent discount to our premium users for the preorder. Of course, we could only change the Preorder class with one if statement to check if our user is a premium user, but that would break the Open Closed Principle. So, in order to implement this additional request, we are going to start with a Decorator class which is going to wrap our OrderBase object:
      public class OrderDecorator : OrderBase
      {
          protected OrderBase order;
      
          public OrderDecorator(OrderBase order)
          {
              this.order = order;
          }
      
          public override double CalculateTotalOrderPrice()
          {
              Console.WriteLine($"Calculating the total price in a decorator class");
              return order.CalculateTotalOrderPrice();
          }
      }
      
      Now, we can implement the PremiumPreorder (Concrete Decorator) class:
      public class PremiumPreorder : OrderDecorator
      {
          public PremiumPreorder(OrderBase order) 
              : base(order)
          {
          }
      
          public override double CalculateTotalOrderPrice()
          {
              Console.WriteLine($"Calculating the total price in the {nameof(PremiumPreorder)} class.");
              var preOrderPrice =  base.CalculateTotalOrderPrice();
      
              Console.WriteLine("Adding additional discount to a preorder price");
              return preOrderPrice * 0.9;
          }
      }
      
      In this class, we are calculating the total price of the OrderBase object but also adding the additional discount behavior. Finally, we can modify the Program.cs class:
      class Program
      {
          static void Main(string[] args)
          {
              var regularOrder = new RegularOrder();
              Console.WriteLine(regularOrder.CalculateTotalOrderPrice());
              Console.WriteLine();
      
              var preOrder = new Preorder();
              Console.WriteLine(preOrder.CalculateTotalOrderPrice());
              Console.WriteLine();
      
              var premiumPreorder = new PremiumPreorder(preOrder);
              Console.WriteLine(premiumPreorder.CalculateTotalOrderPrice());
          }
      }
      
      As we can see, with the premiumPreorder object we are wrapping the preOrder object to which we add an additional behavior: Decorator Design Pattern - Result Now we can clearly see how our Decorator class wraps the preorder object. Excellent.

      Conclusion

      In this article, we have learned:
      • What is the Decorator design pattern
      • When should we use it
      • How to implement this pattern in practice
      [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47710 0 0 0 959 https://brandwebdirect.com/website-design-portfolio-samples/website-design-for-ecommerce-online-sales-web-development-service 0 0 1251 0 0 1253 1251 0 1448 0 0 1449 1448 0
      C# Design Patterns - Command https://code-maze.com/command/ Mon, 01 Apr 2019 06:16:46 +0000 https://code-maze.com/?p=47758
    • Builder Design Pattern and Fluent Builder
    • Fluent Builder Interface With Recursive Generics 
    • Faceted Builder 
    • Factory Method 
    • Singleton 
    • Adapter
    • Composite
    • Decorator
    • Command (Current article)
    • Strategy
    • [sc name="part_of_series" headline="This article is part of the series"] The source code is available at the Command Design Pattern - Source Code. For the main page of this series check out C# Design Patterns. This article is divided into the following sections:

      Implementation of Command Design Pattern

      The Command design pattern consists of the Invoker class, Command class/interface, Concrete command classes and the Receiver class.  Having that in mind, in our example, we are going to follow the same design structure. So, what we are going to do is write a simple app in which we are going to modify the price of the product that will implement the Command design pattern. That being said, let’s start with the Product receiver class, which should contain the base business logic in our app:
      public class Product
      {
          public string Name { get; set; }
          public int Price { get; set; }
      
          public Product(string name, int price)
          {
              Name = name;
              Price = price;
          }
      
          public void IncreasePrice(int amount)
          {
              Price += amount;
              Console.WriteLine($"The price for the {Name} has been increased by {amount}$.");
          }
      
          public void DecreasePrice(int amount)
          {
              if(amount < Price)
              {
                  Price -= amount;
                  Console.WriteLine($"The price for the {Name} has been decreased by {amount}$.");
              }
          }
      
          public override string ToString() => $"Current price for the {Name} product is {Price}$.";
      }
      
      So this is our receiver class, which has a pretty straightforward logic. We just increase or conditionally decrease the product’s price. Finally, we have the ToString method, to be able to print our object. Now the Client class can instantiate the Product class and execute the required actions. But the Command design pattern states that we shouldn’t use receiver classes directly. Instead, we should extract all the request details into a special class - Command. And that is exactly what we are going to do. The first thing we are going to do is to add the ICommand interface:
      public interface ICommand
      {
          void ExecuteAction();
      }
      
      Just to enumerate our price modification actions, we are going to add a simple PriceAction enumeration:
      public enum PriceAction
      {
          Increase,
          Decrease
      }
      
      Finally, let’s add the ProductCommand class:
      public class ProductCommand : ICommand
      {
          private readonly Product _product;
          private readonly PriceAction _priceAction;
          private readonly int _amount;
      
          public ProductCommand(Product product, PriceAction priceAction, int amount)
          {
              _product = product;
              _priceAction = priceAction;
              _amount = amount;
          }
      
          public void ExecuteAction()
          {
              if(_priceAction == PriceAction.Increase)
              {
                  _product.IncreasePrice(_amount);
              }
              else
              {
                  DecreasePrice(_amount);
              }
          }
      }
      
      As we can see, the ProductCommand class has all the information about the request and based on that executes required action. To continue on, let’s add the ModifyPrice class, which will act as Invoker:
      public class ModifyPrice
      {
          private readonly List<ICommand> _commands;
          private ICommand _command;
      
          public ModifyPrice()
          {
              _commands = new List<ICommand>();
          }
      
          public void SetCommand(ICommand command) => _command = command;
      
          public void Invoke()
          {
              _commands.Add(_command);
              _command.ExecuteAction();
          }
      }
      
      This class can work with any command that implements the ICommand interface and store all the operations as well. Now, we can start working with the client part:
      class Program
      {
          static void Main(string[] args)
          {
              var modifyPrice = new ModifyPrice();
              var product = new Product("Phone", 500);
      
              Execute(product, modifyPrice, new ProductCommand(product, PriceAction.Increase, 100));
                 
              Execute(product, modifyPrice, new ProductCommand(product, PriceAction.Increase, 50));
      
              Execute(product, modifyPrice, new ProductCommand(product, PriceAction.Decrease, 25));
      
              Console.WriteLine(product);
          }
      
          private static void Execute(Product product, ModifyPrice modifyPrice, ICommand productCommand)
          {
              modifyPrice.SetCommand(productCommand);
              modifyPrice.Invoke();
          }
      }
      
      This should be our result: Command design pattern - working solution Excellent. We can see the order of our actions and the correct price after modifications. Having in mind that we are keeping track of our actions in the Invoker class, we can use that to Undo our operations if we want to. So, let’s try that.

      Implementing Undo Operation in the Command Design Pattern

      To implement the Undo operation let’s start with the ICommand interface modification:
      public interface ICommand
      {
          void ExecuteAction();
          void UndoAction();
      }
      
      Then, let’s modify the ProductCommand class by adding the UndoAction method:
      public void UndoAction()
      {
          if (_priceAction == PriceAction.Increase)
          {
              _product.DecreasePrice(_amount);
          }
          else
          {
              _product.IncreasePrice(_amount);
          }
      }
      
      Of course, we have to modify the ModifyPrice class as well by adding the UndoActions method:
      public void UndoActions()
      {
          foreach (var command in Enumerable.Reverse(_commands))
          {
              command.UndoAction();
          }
      }
      
      Please note that we are not using the Linq Reverse method but the Enumerable.Reverse() instead. That’s because the Linq method would mutate our list and we don’t want that. All we want is just a reversed list but without mutating the original list itself. Now, when the Client class calls the UndoActions method, it will iterate through all the operations from the list and execute the opposite operation from the previously required. Let’s try that:
      class Program
      {
          static void Main(string[] args)
          {
              var modifyPrice = new ModifyPrice();
              var product = new Product("Phone", 500);
      
              Execute(product, modifyPrice, new ProductCommand(product, PriceAction.Increase, 100));
                
              Execute(product, modifyPrice, new ProductCommand(product, PriceAction.Increase, 50));
      
              Execute(product, modifyPrice, new ProductCommand(product, PriceAction.Decrease, 25));
      
              Console.WriteLine(product);
              Console.WriteLine();
      
              modifyPrice.UndoActions();
              Console.WriteLine(product);
          }
      
          private static void Execute(Product product, ModifyPrice modifyPrice, ICommand productCommand)
          {
              modifyPrice.SetCommand(productCommand);
              modifyPrice.Invoke();
          }
      }
      
      The result: Undo action - Command pattern Everything works as it supposed to.

      Improving the Solution

      We have implemented the Command design pattern into our app, and there is nothing wrong with that. But there is one flaw in our solution which is not related to the Command pattern but to the business logic overall. If we were to change the decreased amount from 25 to 2500 for example, what would happen? Well, we have the protection for that in the DecreasePrice method and it will not affect the result, and you are right. But it is going to affect the Undo actions. Let’s see how:
      ...
      
      Execute(product, modifyPrice, new ProductCommand(product, PriceAction.Decrease, 2500));
      
      ...
      
      This is going to be the result: Undo wrong result - command pattern As you can see, our price hasn’t been decreased but the operation has been preserved in our store which caused the Undo action to fail. So, let’s fix that. The first thing we are going to do is to modify the DecreasePrice method in the Product class:
      public bool DecreasePrice(int amount)
      {
          if(amount < Price)
          {
              Price -= amount;
              Console.WriteLine($"The price for the {Name} has been decreased by {amount}$.");
              return true;
          }
          return false;
      } 
      
      Now, we can modify the ProductCommand class as well:
      public class ProductCommand : ICommand
      {
          private readonly Product _product;
          private readonly PriceAction _priceAction;
          private readonly int _amount;
      
          public bool IsCommandExecuted { get; private set; }
      
          public ProductCommand(Product product, PriceAction priceAction, int amount)
          {
              _product = product;
              _priceAction = priceAction;
              _amount = amount;
          }
      
          public void ExecuteAction()
          {
              if(_priceAction == PriceAction.Increase)
              {
                  _product.IncreasePrice(_amount);
                  IsCommandExecuted = true;
              }
              else
              {
                  IsCommandExecuted = _product.DecreasePrice(_amount);
              }
          }
      
          public void UndoAction()
          {
              if (!IsCommandExecuted)
                  return;
      
              if (_priceAction == PriceAction.Increase)
              {
                  _product.DecreasePrice(_amount);
              }
              else
              {
                  _product.IncreasePrice(_amount);
              }
          }
      }
      
      And that is it. Now if we start our app with a decreased amount of 2500 the result would be correct: finall solution - command pattern Great job.

      Conclusion

      Even though the Command design pattern introduces complexity to our code, it can be very useful. With it, we can decouple classes that invoke operations from classes that perform these operations. Additionally, if we want to introduce new commands, we don’t have to modify existing classes. Instead, we can just add those new command classes to our project. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47758 0 0 0 1424 0 0 1489 0 0 1490 1489 0 1491 1490 0 1492 1491 0 1559 0 0 1560 1559 0 1637 0 0
      Securing TeamCity on Windows with Let's Encrypt https://code-maze.com/securing-teamcity-https-windows/ Mon, 20 May 2019 05:10:49 +0000 https://code-maze.com/?p=47766 one of the best build servers out there. It works exclusively on-premises and that means it requires it's own machine to run on. That is great for companies and individuals that want to have full control of their resources and JetBrains has made the installation as easy as possible. We are going to assume that you've already installed TeamCity on your machine and one build agent on a different machine (best practice). If you haven't yet you can download TeamCity and start working with it immediately since it is free for 3 build agents and 100 build configurations (enough for a medium-sized startup). If you followed the installation wizard, your TeamCity is now running on a port 80 of the localhost and you've created an administrator account. But since we don't want our build server running on HTTP, we are going to create a sub-domain and bind a certificate to it. After that, we are going to redirect all traffic to HTTPS. So, we are going to: Let's start.

      What is Let's Encrypt

      To quote its website, Let's Encrypt is a free, automated, and open Certificate Authority. Certificates were and even now are sold by Certificate Authorities. Let's Encrypt has been founded to stop that practice and to help create more secure and open internet for everyone for free. If you are not sure if they are secure, take a look at their sponsors' page and take note of the companies that support them. That should give you a clear picture if you should use Let's Encrypt. Recently they've announced support for wildcard certificates. That was a major step forward since now you can create a certificate for your domain like this: https://*.your-domain.com which will secure all subdomains on that domain. We encourage you to use Let's Encrypt since certificates should not and need not be paid for. But enough of that, let's get to the meat of the article.

      Creating a Subdomain for the Machine

      One of the prerequisites for the creation of the certificate is that you connected a domain/subdomain to the machine TeamCity Server is installed on. The other is that you need to open both port 80 and 443 on the machine to be able to access TeamCity on HTTP and HTTPS respectively. This step is easy, but you must have a domain and access to the DNS management panel. Once you do, you just need to create A record pointing to the machine. For example, you can make a subdomain like this: [table id=16 /] That's it, easy enough, now you can access your TeamCity server by typing http://tc.your-domain.com. But if you try to get to the HTTPS version of it, you won't be able to. Once you have, it might take up to 48h to propagate, depending on the registrar, so make sure to create it in advance. We are using CloudFlare, where it usually happens really fast. Now that we prepared our subdomain, let's make it secure and create a certificate.

      Selecting a Let's Encrypt Client

      Since this article is about configuring TeamCity on a Windows machine, we are going to select a client appropriate for that. A useful list of the certification clients can be found on the Let's Encrypt clients page. For the purposes of this post, we are going to use Posh-ACME which is a PowerShell module and very easy to use and install. It doesn't require any file downloads and all the work is done through the PowerShell console. Another advantage of using Posh-ACME is that it's not reliant on the existence of IIS or any website like some other clients are. Now that we've decided which client we want to use, let's see how to create a certificate with it.

      Creating a Let's Encrypt Certificate for Our Subdomain

      To begin with, let's set our PowerShell's policy to RemoteSigned:
      Set-ExecutionPolicy RemoteSigned
      After that we need to install Posh-ACME to be able to start creating certs:
      Install-Module -Name Posh-ACME
      After a few moments, Posh-ACME is installed, and we can proceed to the main event. To create a new certificate we need to type:
      New-PACertificate site1.example.com -AcceptTOS -Contact admin@gmail.com
      Of course, you need to change site1.example.com with your own domain or subdomain. Also, try not to use admin@gmail.com as a contact email :) We guarantee you won't get a renewal notice like that. If you want to create a certificate like this you can, but you'll get a warning and you'll need to add a new TXT record as an acme challenge: posh acme new certificate As you can see, the warning is about missing DnsPluign.

      Configuring the DnsPlugin

      Now, what you can do is proceed to add the TXT record to the DNS records of your domain registrar, or you can set the appropriate plugin and let Posh-ACME do that for you automatically. We recommend the second way, because it's easier and because it's built-in, so why not utilize it. There is a list of supported DNS providers that Posh-ACME supports in the current release. It's doesn't support every provider out there and thus the manual method for adding challenges as we've already seen. Say for example that we use a CloudFlare provider. We would need to go to the "Usage guide" for CloudFlare and follow the instructions on how to add the DnsPlugin for it. And it's basically these two commands:
      $CFParams = @{CFAuthEmail='xxxxxxxx'; CFAuthKey='xxxxxxxx'}
      New-PACertificate site1.example.com -DnsPlugin Cloudflare -PluginArgs $CFParams -Contact admin@gmail.com
      Once again, change the site1.example.com and the contact email to your own. As for the CFParams, every provider has its own way to provide them. For Cloudflare, you can find CFAuthKey right at the bottom of the profile page: cloudflare api key You'll be prompted to enter the password to your account to view the key. Once you get the key, set the CFAuthKey field, and of course, set the CFAuthEmail field to your CloudFlare account email. Now we can run these two commands and wait for the wizard to finish. After the process has finished, you can find your certificates by typing:
      Get-PACertificate | Format-List
      This command will list the certificate files location. Usually, they are found at C:\Users\<Username>\AppData\Local\Posh-ACME\acme-v02.api.letsencrypt.org

      Configuring the TeamCity Server

      So, now we have a certificate. But how do we configure TeamCity to use that certificate? There are two things we have to do. First, we need to navigate to C:\TeamCity\conf (or any other TeamCity location you've selected during installation) and configure web.xml first:
      <security-constraint>
      <web-resource-collection>
           <web-resource-name>Entire Application</web-resource-name>
           <url-pattern>/*</url-pattern>
          </web-resource-collection>
      <!-- auth-constraint goes here if you requre authentication -->
      <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
          </user-data-constraint>
      </security-constraint>
      After that, we need to edit server.xml too:
      <Connector port="80" protocol="org.apache.coyote.http11.Http11NioProtocol"
                  connectionTimeout="60000"
                  useBodyEncodingForURI="true"
                  socket.txBufSize="64000"
                  socket.rxBufSize="64000"
                  tcpNoDelay="1"
                  redirectPort="443"
      />
       
      <!-- To make https work on port 443 -->
      <Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
              maxThreads="150" SSLEnabled="true">
          <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol"/>
          <SSLHostConfig>
              <Certificate certificateKeyFile="C:\Users\<Username>\AppData\Local\Posh-ACME\acme-v02.api.letsencrypt.org\54133256\site1.example.com\cert.key"
                  certificateFile="C:\Users\<Username>\AppData\Local\Posh-ACME\acme-v02.api.letsencrypt.org\54133256\site1.example.com\cert.cer"
                  certificateChainFile="C:\Users\<Username>\AppData\Local\Posh-ACME\acme-v02.api.letsencrypt.org\54133256\site1.example.com\chain.cer"
                  type="RSA" />
          </SSLHostConfig>
      </Connector>
      So, we're adding redirection port to our existing connector, and then creating a new connector for the port 443 which is used for HTTPS. Make sure to include redirectPort in the Connector for port 80. In the Certificate element, we need to set the certificateKeyFile, the certificateFile, and the certificateChainFile attributes to the cert.key, cert.cer, and chain.cer files respectively. The example from the snippet above is just an approximate location of the certificates. OK, that's that.

      Checking out the Certificate

      Now restart the TeamCity server service and try it out! HTTP traffic should be redirected to the HTTPS, and the HTTPS should show a valid Let's Encrypt certificate. That's it, navigate to your TeamCity domain and check if the certificate has been configured. You should see something like this: certificate teamcity Now, let's set up automatic certificate renewal so we don't worry about our certificate expiring in 3 months.

      Certificate Renewal

      Unfortunately, the certificate renewal process isn't automatic with Posh-ACME. But it isn't hard to set it up either. There are a few commands that you can use to renew your certificate:
      Submit-Renewal
      Submit-Renewal -AllOrders
      Submit-Renewal -AllAccount
      
      With these, you can renew all the certificates on one account or even across accounts. Set-Renewal is useful if you've used DnsPlugin while creating a certificate. Now that you know which commands are used for certificate renewal, you can just make a scheduled task in the Task Scheduler to run a PowerShell script containing this command on a monthly basis to renew them.

      Conclusion

      That was a short guide on how to set up a secure connection for your own TeamCity server. The process is simple and straight-forward once you know what needs to be done. So what we have learned in this article:
      • A bit about Let's Encrypt
      • How to set up a subdomain for our server
      • How to create a certificate for our subdomain
      • Properly configure TeamCity server to acknowledge that certificate
      Hope this article will help you easily deal with this problem the next time you encounter it. [sc name="subscribe" formNumber="3820" contentType="CI/CD"]]]>
      47766 0 0 0
      C# Design Patterns - Strategy Design Pattern https://code-maze.com/strategy/ Mon, 08 Apr 2019 05:53:06 +0000 https://code-maze.com/?p=47926
    • Builder Design Pattern and Fluent Builder
    • Fluent Builder Interface With Recursive Generics 
    • Faceted Builder 
    • Factory Method 
    • Singleton 
    • Adapter
    • Composite
    • Decorator
    • Command
    • Strategy (Current article)
    • [sc name="part_of_series" headline="This article is part of the series"] So without further ado, let’s dive dipper into the Strategy design pattern implementation. The source code is available at the Strategy Design Pattern - Source Code. For the main page of this series check out C# Design Patterns. This article is divided into the following sections:

      Strategy Design Pattern Structure

      As we stated above, the Strategy design pattern consists of the Context object which maintains the reference towards the strategy object. But it is not the only part of the puzzle. For the complete implementation, we need the Strategy object (interface) to define a way for the Context object to execute the strategy and the Concrete Strategies objects which implements the Strategy interface. The Strategy design pattern is quite common in the C# language due to its various uses to provide changing behavior of a class without modifying it. This complies with the rules of the Open Closed Principle, which we talked about in one of our previous articles.

      Implementation of the Strategy Design Pattern

      To implement the Strategy pattern, we are going to reuse an example from our Open Closed Principle article. So, the main task is to calculate the total cost for the developer's salaries, but for the different developer level, the salary is calculated differently. Now, we are going to modify that example by introducing the Strategy design pattern. So let’s start with the DeveloperLevel enumeration and a simple DeveloperReport object:
      public enum DeveloperLevel
      {
          Senior,
          Junior
      }
      
      public class DeveloperReport
      {
          public int Id { get; set; }
          public string Name { get; set; }
          public DeveloperLevel Level { get; set; }
          public int WorkingHours { get; set; }
          public double HourlyRate { get; set; }
      
          public double CalculateSalary() => WorkingHours * HourlyRate; 
      }
      
      To continue on, let’s create the Strategy interface named ISalaryCalculator:
      public interface ISalaryCalculator
      {
          double CalculateTotalSalary(IEnumerable<DeveloperReport> reports);
      }
      
      Now what we need is the concrete strategy objects which will accept all the reports and calculate the total salary for the required developer levels:
      public class JuniorDevSalaryCalculator : ISalaryCalculator
      {
          public double CalculateTotalSalary(IEnumerable<DeveloperReport> reports) => 
              reports
                  .Where(r => r.Level == DeveloperLevel.Junior)
                  .Select(r => r.CalculateSalary())
                  .Sum();
      }
      
      public class SeniorDevSalaryCalculator : ISalaryCalculator
      {
          public double CalculateTotalSalary(IEnumerable<DeveloperReport> reports) =>
              reports
                  .Where(r => r.Level == DeveloperLevel.Senior)
                  .Select(r => r.CalculateSalary() * 1.2)
                  .Sum();
      }
      
      As we can see, for the senior developers, we are adding a 20% bonus to the salary. Additionally, we have separated our calculation logic for the different developer levels and made easier to add calculation logic for the medior developers for example. All we would have to do is to add an additional strategy object which implements the ISalaryCalculator interface. Once we have the strategy objects, we need the context object in our implementation:
      public class SalaryCalculator
      {
          private ISalaryCalculator _calculator;
      
          public SalaryCalculator(ISalaryCalculator calculator)
          {
              _calculator = calculator;
          }
      
          public void SetCalculator(ISalaryCalculator calculator) => _calculator = calculator;
      
          public double Calculate(IEnumerable<DeveloperReport> reports) => _calculator.CalculateTotalSalary(reports);
      }
      
      In this context object, we provide initialization of the strategy object with the constructor in a compile-time or with the SetCalculator method in the application's runtime. Furthermore, the Calculate method just executes the strategy object functionality. So, to connect all the dots together, let’s modify the Program.cs class:
      class Program
      {
          static void Main(string[] args)
          {
              var reports = new List<DeveloperReport>
              {
                  new DeveloperReport {Id = 1, Name = "Dev1", Level = DeveloperLevel.Senior, HourlyRate = 30.5, WorkingHours = 160 },
                  new DeveloperReport { Id = 2, Name = "Dev2", Level = DeveloperLevel.Junior, HourlyRate = 20, WorkingHours = 120 },
                  new DeveloperReport { Id = 3, Name = "Dev3", Level = DeveloperLevel.Senior, HourlyRate = 32.5, WorkingHours = 130 },
                  new DeveloperReport { Id = 4, Name = "Dev4", Level = DeveloperLevel.Junior, HourlyRate = 24.5, WorkingHours = 140 }
              };
      
              var calculatorContext = new SalaryCalculator(new JuniorDevSalaryCalculator());
              var juniorTotal = calculatorContext.Calculate(reports);
              Console.WriteLine($"Total amount for junior salaries is: {juniorTotal}");
      
              calculatorContext.SetCalculator(new SeniorDevSalaryCalculator());
              var seniorTotal = calculatorContext.Calculate(reports);
              Console.WriteLine($"Total amount for senior salaries is: {seniorTotal}");
      
              Console.WriteLine($"Total cost for all the salaries is: {juniorTotal+seniorTotal}");
          }
      }
      
      This should be our result: Strategy Design Pattern - Complete

      When Should We Use the Strategy Design Pattern

      We should use this pattern whenever we have different variations for some functionality in an object and we want to switch from one variation to another in a runtime. Furthermore, if we have similar classes in our project that only differ on how they execute some behavior, the Strategy pattern should be the right choice for us. We should consider introducing this pattern in situations where a single class has multiple conditions over different variations of the same functionality. That’s because the Strategy pattern lets us extract those variations into separate classes (concrete strategies). Then we can invoke them into the context class.

      Conclusion

      As you can see, implementing the Strategy design pattern isn’t hard or complex at all. It makes our code more readable and easier to maintain. Yes, it requires an implementation of additional classes in the project, but that’s the price worth paying for. So, we have learned:
      • What is the Strategy design pattern
      • How to implement it in C#
      • And, when to use it
      [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47926 0 0 0 957 0 0 934 0 0 935 934 0 945 935 0 951 945 0 1246 0 0 1248 1246 0
      Getting Started with GraphQL in ASP.NET Core https://code-maze.com/graphql-aspnetcore-basics/ Mon, 15 Apr 2019 06:05:45 +0000 https://code-maze.com/?p=48142
    • Getting Started with GraphQL in .NET Core (Current article)
    • Advanced GraphQL Queries, Error Handling, Data Loader
    • GraphQL Mutations (POST, PUT, DELETE Actions)
    • Consume GraphQL API with ASP.NET Core
    • Consuming a GraphQL API with Angular
    • [sc name="part_of_series" headline="This article is part of the series"] To download the source code, visit the GraphQL in ASP.NET Core Project Source Code. For the complete navigation of this tutorial visit GraphQL ASP.NET Core Tutorial. We’ve divided this article into the following sections:

      About GraphQL and How it’s Different from REST

      GraphQl is a query language. It executes queries by using type systems which we define for our data. GraphQL isn’t tied to any specific language or a database, just the opposite, it is adaptable to our code and our data as well.

      Let’s talk a bit about how GraphQL differs from REST:

      • GraphQL requires fewer roundtrips to the server and back to fetch all the required data for our view or template page. With REST, we have to visit several endpoints (api/subjects, api/professors, api/students ...) to get all the data we need for our page, but that’s not the case with GraphQL. With GraphQL, we create only one query which calls several resolvers (functions) on the server-side and returns all the data from different resources in a single request.
      • With REST, as our application grows, the number of endpoints grows as well, and that requires more and more time to maintain. But, with GraphQL we have only one endpoint api/graphql and that is all.
      • By using GraphQL, we never face a problem of getting too much or too little data in our response. That’s because we are defining our queries with the fields which state what we need in return. That way, we are always getting what we have requested. So, if we send a query like this one:
      query OwnersQuery {
        owners {
          name
          account {
            type
          }
        } 
      }
      
      We are 100% sure that we will get this response back:
      {
        "data": {
          "owners": [
           {
            "name": "John Doe",
            "accounts": [
              {
                "type": "Cash"
              },
              {
                "type": "Savings"
              }
            ]
           }
          ]
        }
      }
      
      With REST this is not the case. Sometimes we get more than we need and sometimes less, it depends on how actions on a certain endpoint are implemented. These are the most important differences between REST and GraphQL. Now that we know that, let’s create a basic project to demonstrate how to set up GraphQL.

      Introduction to a Starter ASP.NET Core Web API Project

      We have prepared a starter ASP.NET Core project, which you can download here GraphQL ASP.NET Core Starter Project. We strongly recommend you download this project, but if you want, you can create your own as well. The starter project has the following structure: Starter project structure - GraphQL in ASP.NET Core The Contracts folder contains interfaces required for our repository logic:
      namespace GraphQLDotNetCore.Contracts
      {
          public interface IOwnerRepository
          {
          }
      }
      
      
      namespace GraphQLDotNetCore.Contracts
      {
          public interface IAccountRepository
          {
          }
      }
      In the Entities folder, we keep model classes with a context class and the seed configuration classes:
      public class Owner
      {
          [Key]
          public Guid Id { get; set; }
          [Required(ErrorMessage = "Name is required")]
          public string Name { get; set; }
          public string Address { get; set; }
      
          public ICollection<Account> Accounts { get; set; }
      }
      
      public class Account
      {
          [Key]
          public Guid Id { get; set; }
          [Required(ErrorMessage = "Type is required")]
          public TypeOfAccount Type { get; set; }
          public string Description { get; set; }
      
          [ForeignKey("OwnerId")]
          public Guid OwnerId { get; set; }
          public Owner Owner { get; set; }
      }
      
      public enum TypeOfAccount
      {
          Cash,
          Savings,
          Expense,
          Income
      }
      
      public class ApplicationContext : DbContext
      {
          public ApplicationContext(DbContextOptions options)
              :base(options)
          {
          }
      
          protected override void OnModelCreating(ModelBuilder modelBuilder)
          {
              var ids = new Guid[] { Guid.NewGuid(), Guid.NewGuid() };
      
              modelBuilder.ApplyConfiguration(new OwnerContextConfiguration(ids));
              modelBuilder.ApplyConfiguration(new AccountContextConfiguration(ids));
          }
      
          public DbSet<Owner> Owners { get; set; }
          public DbSet<Account> Accounts { get; set; }
      }
      
      And in a Repository folder, we have classes related to the data fetching logic:
      public class OwnerRepository : IOwnerRepository
      {
          private readonly ApplicationContext _context;
      
          public OwnerRepository(ApplicationContext context)
          {
              _context = context;
          }
      }
      
      public class AccountRepository : IAccountRepository
      {
          private readonly ApplicationContext _context;
      
          public AccountRepository(ApplicationContext context)
          {
              _context = context;
          }
      }
      
      This repository logic has the basic setup without any additional layers. But if you want to learn more about Repository Pattern in ASP.NET Core, we have a great article for you to read ASP.NET Core Web API – Repository Pattern. The context class and repository classes are registered inside the Startup.cs class:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext<ApplicationContext>(opt =>
              opt.UseSqlServer(Configuration.GetConnectionString("sqlConString")));
      
          services.AddScoped<IOwnerRepository, OwnerRepository>();
          services.AddScoped<IAccountRepository, AccountRepository>();
      
          services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
              .AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
      }
      
      [learn_more caption="Additional note about reference loop" state="open"] From the ASP.NET Core version 3.0, instead of the AddJson() method, we have to use: .AddNewtonsoftJson( o => o.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore) Of course, for that to work, we have to install the NewtonsoftJson library. [/learn_more] So, at this point, all you have to do is to modify connection string (if you have to) in the appsettings.json file and to navigate to the Package Manager Console and run the update-database command. Once you do that, we are ready to move on. As you can see, we are using the Code-First approach in the starter project.

      Integration of GraphQL in ASP.NET Core

      Since the GraphQL support is not provided inside the ASP.NET Core applications, we have to install a few new libraries. So,  the first library we are going to install is GraphQL and we can install it via NuGet package manager: GraphQL package - GraphQL in ASP.NET Core In addition, we can use the Package Manager Console: PM> Install-Package GraphQL -Version 2.4.0 The second library we want to install is GraphQL.Server.Transports.AspNetCore, which will help us to get GraphQL.NET as a dependency: Second NuGet package - GraphQL in ASP.NET Core The Package Manager command: PM> Install-Package GraphQL.Server.Transports.AspNetCore -Version 3.4.0 Finally, we are going to install GraphQL.Server.Ui.Playground library, which will help us send the GraphQL queries to the server: Ui Playground package - GraphQL in ASP.NET Core The PM command: PM> Install-Package GraphQL.Server.Ui.Playground -Version 3.4.0 Once we are finished with the installations, we can move on to the integration logic.

      Creating GraphQL Specific Objects (Type, Query, Schema)

      Let’s start by creating a new folder named GraphQL and inside a new one with the name GraphQLSchema with a single class AppSchema.cs: GraphQL structure - GraphQL in ASP.NET Core
      public class AppSchema : Schema
      {
          public AppSchema(IDependencyResolver resolver)
              :base(resolver)
          {
      
          }
      }
      
      This class must inherit from the Schema class which resides in the GraphQL.Types namespace. Inside the constructor, we inject the IdependencyResolver which is going to help us resolve our Query, Mutation or Subscription objects. What’s important to know is that each of the schema properties (Query, Mutation or Subscription) implements IObjectGraphType which means that the objects we are going to resolve must implement the same type as well. This also means that our GraphQL API can’t return our models directly as a result but GraphQL types which implement IObjectGraphType instead. So, let’s leave this class for a moment and create a new folder GraphQLTypes with a single class OwnerType.cs inside the GraphQL folder:
      public class OwnerType : ObjectGraphType<Owner>
      {
          public OwnerType()
          {
              Field(x => x.Id, type: typeof(IdGraphType)).Description("Id property from the owner object.");
              Field(x => x.Name).Description("Name property from the owner object.");
              Field(x => x.Address).Description("Address property from the owner object.");
          }
      }
      
      This is the OwnerType class which we use as a replacement for the Owner model inside a GraphQL API. This class inherits from a generic ObjectGraphType<Owner> class which at some point (in the hierarchy) implements IObjectGraphType interface. With the Field method, we specify the fields which represent our properties from the Owner model class. To continue on, let’s create another folder GraphQLQueries with a class AppQuery, inside the GraphQL folder. But before we modify this class, let’s modify the IOwnerRepository interface:
      public interface IOwnerRepository
      {
          IEnumerable<Owner> GetAll();
      }
      
      And the OwnerRepository class:
      public class OwnerRepository : IOwnerRepository
      {
          private readonly ApplicationContext _context;
      
          public OwnerRepository(ApplicationContext context)
          {
              _context = context;
          }
      
          public IEnumerable<Owner> GetAll() => _context.Owners.ToList();
      }
      
      Now, we can modify the AppQuery class to reflect those changes:
      public class AppQuery : ObjectGraphType
      {
          public AppQuery(IOwnerRepository repository)
          {
              Field<ListGraphType<OwnerType>>(
                 "owners",
                 resolve: context => repository.GetAll()
             );
          }
      }
      

      AppQuery Explanation

      As we can see, this class inherits from the ObjectGraphType as well, just the non-generic one. Moreover, we inject our repository object inside a constructor and create a field to return the result for the specific query. In this class, we use the generic version of the Field method which accepts some „strange“ type as a generic parameter. Well, this is the GraphQL.NET representation for the normal .NET types. So, ListGraphType is the representation of the List type, and of course, we have IntGraphType or StringGraphType, etc... For the complete list visit SchemaTypes in GraphQL .NET. The „owners“ parameter is a field name (query from the client must match this name) and the second parameter is the result itself. Having  done our preparations, we can now modify our AppSchema class:
      public class AppSchema : Schema
      {
          public AppSchema(IDependencyResolver resolver)
              :base(resolver)
          {
              Query = resolver.Resolve<AppQuery>();
          }
      } 
      
      Well done. Let’s proceed to schema registration.

      Libraries and Schema Registration

      In a Startup class, we need to register our installed libraries and the created schema class as well. So, let’s do that by modifying the ConfigureServices and Configure methods:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext<ApplicationContext>(opt =>
              opt.UseSqlServer(Configuration.GetConnectionString("sqlConString")));
      
          services.AddScoped<IOwnerRepository, OwnerRepository>();
          services.AddScoped<IAccountRepository, AccountRepository>();
      
          services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));
          services.AddScoped<AppSchema>();
      
          services.AddGraphQL(o => { o.ExposeExceptions = false; })
              .AddGraphTypes(ServiceLifetime.Scoped);
      
          services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                      .AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
      }
      
      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
          else
          {
              app.UseHsts();
          }
      
          app.UseHttpsRedirection();
      
          app.UseGraphQL<AppSchema>();
          app.UseGraphQLPlayground(options: new GraphQLPlaygroundOptions());
      
          app.UseMvc();
      }
      
      In the ConfigureServices method, we register the DependencyResolver (for our queries) and the schema class as well. Furthermore, we register GraphQL with the AddGraphQL method and register all the GraphQL types (Query and Type classes) with the AddGraphTypes method. Without this method, we would have to register all the types and queries manually in our API. Finally, in the Configure method, we are adding our schema to the request’s pipeline as well as the Playground UI tool which we are just about to use.

      Sending the First Query

      To test our GraphQL API, we are going to use the GraphQL.UI.Playground tool. So, let’s first start our server application and then navigate to the https://localhost:5001/ui/playground address: Sending the first query Excellent, we see that everything is working just as it supposed to. As soon as we send our query, with the name „owners“ (which must match the query name we created in the AppQuery file), we get the required result.

      Conclusion

      Our integration is complete. We have seen how in few easy steps we can integrate GrapnQL in ASP.NET Core and how to retrieve required results with the query. In the next article, we are going to learn about advanced GraphQL Queries, how to handle errors during queries and how to use data loader to cache our results. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48142 0 0 0 1217 0 0 1218 1217 0 1266 0 0 1268 1266 0 1270 1268 0 1271 1270 0 1272 1271 0 1466 0 0 1467 1466 0 1468 1467 0 1469 1468 0
      Advanced GraphQL Queries, Error Handling, Data Loader https://code-maze.com/advanced-graphql-queries/ Mon, 22 Apr 2019 05:51:18 +0000 https://code-maze.com/?p=48187 previous article, we have learned about the GraphQL integration with the ASP.NET Core application. We have created our first GraphQL query and fetched some data from the server side application. But we won’t stop on just basic queries. In this article, we are going to learn how to write advanced GraphQL queries and while doing that, we are going to show you how to handle errors and how to cache data inside a query. Furthermore, we are going to learn how to enhance our query (from a client side) with the named queries, aliases, arguments, and fragments. [sc name="part_of_series" headline="This article is part of the series"] To download the source code, visit the Advanced GraphQL Queries Source Code. For the complete navigation of this tutorial visit GraphQL ASP.NET Core Tutorial. We are going to divide this article into the following sections:

      Creating Complex Types for GraphQL Queries

      If we take a look at our „owners“ query, we are going to see that it returns the ListGraphType<OwnerType> result. Furthermore, if we inspect the OwnerType class, we are going to see that it contains the Id, Name and Address fields. But a single Owner can have multiple accounts related to it, this can be confirmed by inspecting the Owner model class in the Entities folder. So, this is exactly what we want to add to the OwnerType class as well. But, before we start with adding additional Accounts field into the OwnerType class, we need to create the AccountType class first. So let’s do that inside the GraphQLTypes folder:
      public class AccountType : ObjectGraphType<Account>
      {
          public AccountType()
          {
              Field(x => x.Id, type: typeof(IdGraphType)).Description("Id property from the account object.");
              Field(x => x.Description).Description("Description property from the account object.");
              Field(x => x.OwnerId, type: typeof(IdGraphType)).Description("OwnerId property from the account object.");
          }
      }
      
      Yes, we are missing the Type property from the Account class, but we will get to that. Now, that we have AccountType, we can add a list of accounts to our „owners“ query result. So, let’s start by modifying the IAccountRepository interface first and then the AccountRepository class:
      public interface IAccountRepository
      {
          IEnumerable<Account> GetAllAccountsPerOwner(Guid ownerId);
      }
      
      public class AccountRepository : IAccountRepository
      {
          private readonly ApplicationContext _context;
      
          public AccountRepository(ApplicationContext context)
          {
             _context = context;
          }
      
          public IEnumerable<Account> GetAllAccountsPerOwner(Guid ownerId) => _context.Accounts
              .Where(a => a.OwnerId.Equals(ownerId))
              .ToList();
      }
      
      Now, we can modify the OwnerType class:
      public class OwnerType : ObjectGraphType<Owner>
      {
          public OwnerType(IAccountRepository repository)
          {
              Field(x => x.Id, type: typeof(IdGraphType)).Description("Id property from the owner object.");
              Field(x => x.Name).Description("Name property from the owner object.");
              Field(x => x.Address).Description("Address property from the owner object.");
              Field<ListGraphType<AccountType>>(
                  "accounts",
                  resolve: context => repository.GetAllAccountsPerOwner(context.Source.Id)
              );
          }
      }
      
      So, there is nothing in this code, that we haven’t seen so far. In the same way that we have created a field in the AppQuery class, we create a field in the OwnerType class. One interesting thing is that the GetAllAccountsPerOwner method requires an id parameter but we provide context.Source.Id. That’s because the context contains the Source property which is of the Owner type in this specific case: Source property in context object - Queries in GraphQL Finally, we can modify the „owners“ query in the UI.Playground: Modified query - GraphQL Queries

      Adding Enumerations in GraphQL Queries

      In the AccountType class, we are missing the Type field. We left it out deliberately, and now it's the right time to add it. To add enumeration to the AccountType class, we need to invest just a little bit more effort than with the regular scalar types in GraphQL. The first thing we are going to do is to create a new class AccountTypeEnumType in the GraphQLTypes folder:
      public class AccountTypeEnumType : EnumerationGraphType<TypeOfAccount>
      {
          public AccountTypeEnumType()
          {
              Name = "Type";
              Description = "Enumeration for the account type object.";
          }
      }
      
      We can see that the AccountTypeEnumType class must inherit from the generic EnumerationGraphType which for the generic parameter has the enumeration that we have created in our starter project. One important thing to mention is that the value for the Name property must mache the name of the same enumeration property inside the Account class: Enumeration type - GraphQL Queries After this modification, let’s add this enumeration type in the AccountType class:
      public class AccountType : ObjectGraphType<Account>
      {
          public AccountType()
          {
              Field(x => x.Id, type: typeof(IdGraphType)).Description("Id property from the account object.");
              Field(x => x.Description).Description("Description property from the account object.");
              Field(x => x.OwnerId, type: typeof(IdGraphType)).Description("OwnerId property from the account object.");
              Field<AccountTypeEnumType>("Type", "Enumeration for the account type object.");
          }
      }
      
      Finally, we can add the enumeration type to our client query: Enumeration in the query - GraphQL Queries

      Implementing a Cache in the GraphQL Queries with Data Loader

      Our query is returning expected result, we’ve seen that, but it is not that optimized enough yet. So, what is the problem? Well, the problem is that our query is working in such a way that it extracts id’s from all the owners and then for every single id sends the additional SQL query to the database to fetch related accounts. We can see that from the logs: Not optimized query Of course, this is not a problem when we have only two owner entities, but what if we have a thousand? We can optimize this query by using DataLoader which is provided by GraphQL, with a couple of modifications. Let’s start by modifying the IAccountRepository file:
      public interface IAccountRepository
      {
          IEnumerable<Account> GetAllAccountsPerOwner(Guid ownerId);
          Task<ILookup<Guid, Account>> GetAccountsByOwnerIds(IEnumerable<Guid> ownerIds);
      }
      
      We need to have a method that returns Task<ILookup<TKey, T> because DataLoader requires a method with that return type in its signature. Then, we need to implement this additional method inside the AccountRepository class:
      public class AccountRepository : IAccountRepository
      {
          private readonly ApplicationContext _context;
      
          public AccountRepository(ApplicationContext context)
          {
              _context = context;
          }
      
          public IEnumerable<Account> GetAllAccountsPerOwner(Guid ownerId) => _context.Accounts
              .Where(a => a.OwnerId.Equals(ownerId))
              .ToList();
      
          public async Task<ILookup<Guid, Account>> GetAccountsByOwnerIds(IEnumerable<Guid> ownerIds)
          {
              var accounts = await _context.Accounts.Where(a => ownerIds.Contains(a.OwnerId)).ToListAsync();
              return accounts.ToLookup(x => x.OwnerId);
          }
      }
      
      To continue on, we are going to modify the OwnerType class:
      public class OwnerType : ObjectGraphType<Owner>
      {
          public OwnerType(IAccountRepository repository, IDataLoaderContextAccessor dataLoader)
          {
              Field(x => x.Id, type: typeof(IdGraphType)).Description("Id property from the owner object.");
              Field(x => x.Name, type: typeof(IdGraphType)).Description("Name property from the owner object.");
              Field(x => x.Address, type: typeof(IdGraphType)).Description("Address property from the owner object.");
              Field<ListGraphType<AccountType>>(
                  "accounts",
                  resolve: context =>
                  {
                      var loader = dataLoader.Context.GetOrAddCollectionBatchLoader<Guid, Account>("GetAccountsByOwnerIds", repository.GetAccountsByOwnerIds);
                      return loader.LoadAsync(context.Source.Id);
                  });
          }
      }
      
      Great job. As you can see, we inject the IDataLoaderContextAccessor in the constructor and use it with the Context.GetOrAddCollectionBatchLoader method with the name of the loader key as a first parameter and our created method as the second parameter. The final thing to do is to register DataLoader in the Startup class, by attaching it to the AddGraphQL method:
      services.AddGraphQL(o => { o.ExposeExceptions = false; })
              .AddGraphTypes(ServiceLifetime.Scoped)
              .AddDataLoader();
      
      Now, as soon as we send the same query again, we can inspect our logs: Optimized query As we can see, we have only one query for all the accounts per each owner.

      Using Arguments in Queries and Handling Errors

      Until now, we’ve been returning all the owners with their accounts in a single query. But we want to return a single owner by provided id parameter as well. So to do that, we need to include arguments in our queries. Let’s do that by modifying the IOwnerRepository interface first:
      public interface IOwnerRepository
      {
          IEnumerable<Owner> GetAll();
          Owner GetById(Guid id);
      }
      
      Next, we are going to implement the GetById method:
      public class OwnerRepository : IOwnerRepository
      {
          private readonly ApplicationContext _context;
      
          public OwnerRepository(ApplicationContext context)
          {
              _context = context;
          }
      
          public IEnumerable<Owner> GetAll() => _context.Owners.ToList();
      
          public Owner GetById(Guid id) => _context.Owners.SingleOrDefault(o => o.Id.Equals(id));
      }
      
      Finally, we can modify the AppQuery class:
      public class AppQuery : ObjectGraphType
      {
          public AppQuery(IOwnerRepository repository)
          {
              Field<ListGraphType<OwnerType>>(
                 "owners",
                 resolve: context => repository.GetAll()
              );
      
              Field<OwnerType>(
                  "owner",
                  arguments: new QueryArguments(new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "ownerId" }),
                  resolve: context =>
                  {
                      var id = context.GetArgument<Guid>("ownerId");
                      return repository.GetById(id);
                  }
              );
          }
      }
      
      So, we create a new field with the OwnerType return value. The name is „owner“ and we use the arguments part to create an argument for this query. Our argument can’t be null (NonNullGraphType) and it must be of the IdGraphType type with the „ownerId“ name. The resolve part is pretty self explanatory. But what if the id parameter is not of the Guid type, then, we would like to return a message to the client. So let’s add a slight modification in the resolve part:
      Field<OwnerType>(
          "owner",
          arguments: new QueryArguments(new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "ownerId" }),
          resolve: context =>
          {
              Guid id;
              if (!Guid.TryParse(context.GetArgument<string>("ownerId"), out id))
              {
                  context.Errors.Add(new ExecutionError("Wrong value for guid"));
                  return null;
              }
      
               return repository.GetById(id);
           }
      );
      
      After these changes, we can use our UI tool to send a new query: Arguments and error handling - GraphQL queries

      Aliases, Fragments, Named Queries, Variables, Directives

      We can modify our queries, on the client side, by introducing the aliases. They are very useful when we want to change the name of our result, or any field in the result. Furthermore, if we have two same queries but with a different argument, we can use aliases to differentiate those queries. To use them, all we have to do is to type the required word in front of the query or field: Aliases in GraphQL Queries As we can see from a previous example, we have two queries with the same fields. Now, let’s imagine if we need a 10 of them with the same fields just different argument, that would be a bit hard to read, wouldn’t it? Well, we can solve this by using fragments. Fragments allow us to separate common fields, for different queries, into a separate section and then just reuse that section in all the queries: Fragments in GraphQL Queries To create a named query, we have to use a „query“ keyword in front of the entire query with the query name as well. Then we can pass arguments for the query if we have some. The important thing with the named queries is if a query has an argument we need to use the QUERY VARIABLES window to assign a value for that argument: Named GraphQL Queries Finally, we can add or remove some fields conditionally from our result by using directives in our queries. There are two directives we can use, include and skip: Directives in GraphQL Quereis

      Conclusion

      Excellent. We did a great job here. To sum up the article, we have learned:
      • How to add complex types in GraphQL
      • How to write GraphQL queries with arguments
      • The way to work with enumerations
      • To cache some data by using DataLoader
      • How to modify client queries with aliases, names, and fragments
      In the next article, we are going to learn about mutations in GraphQL. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48187 0 0 0 1006 0 0 1007 1006 0 1032 0 0 1033 1032 0 1275 0 0 1277 1275 0 1454 Name and Description in AccountTypeEnum's constructor if you are gonna place it in AccountType with Field("Type", "Enumeration for the account type object."); as well? After some experimenting I've found out that: Field(nameof(Account.Type), resolve: context => context.Source.Type); and public class AccountTypeEnumType : EnumerationGraphType { public AccountTypeEnumType() { Description = "Enumeration for the account type object."; } } also worked as intended. But now i wondered what was the role of the Name property in AccountTypeEnumType? Turns out that if `Name` property is omitted in the constructor in the "Docs" section of UI Playground field "type" will be of "TypeOfAccount" https://uploads.disquscdn.com/images/92da0b0fc4a14df06b9fc64c2419ebb6d13cd6d2b53520e65ced6cf885ef23a6.png But if i set it to something random like Name = "MyName" then the "Docs" will say that "type" is of type "MyName" https://uploads.disquscdn.com/images/67d4360e2af7d5d5448857ec90ed80c30aeeb790b75e1dfc3f3e78eb17d91fcb.png Both of these cases seem to work on my machine with the code I specified above Also i would like to note that even Field("myFieldName", resolve: context => context.Source.Type); worked as intended ("myFieldName" in this case only seems to note how field value when doing query will be called (it makes most sense to match the value name of entity type but nobody forces us) So in summary what really matters in my example seems to be the resolve: context => context.Source.Type which in my opinion is a bit better solution because it allows us to refactor code more easily. I hope that this helps slightly confused readers (such as myself).]]> 0 0 1455 1454 0 1457 1455 0
      GraphQL Mutations in ASP.NET Core https://code-maze.com/graphql-mutations/ Mon, 29 Apr 2019 05:53:22 +0000 https://code-maze.com/?p=48225
    • Getting Started with GraphQL in ASP.NET Core
    • Advanced GraphQL Queries, Error Handling, Data Loader
    • GraphQL Mutations (POST, PUT, DELETE Actions) (Current Article)
    • Consume GraphQL API with ASP.NET Core
    • Consuming a GraphQL API with Angular
    • [sc name="part_of_series" headline="This article is part of the series"] To download the source code, visit the GraphQL Mutations Source Code. For the complete navigation of this tutorial visit GraphQL ASP.NET Core Tutorial. We are going to divide this article into the following sections:

      Input Types and Schema Enhancing for the GraphQL Mutations

      Let’s start with creating a new class OwnerInputType inside the Types folder:
      public class OwnerInputType : InputObjectGraphType
      {
          public OwnerInputType()
          {
              Name = "ownerInput";
              Field<NonNullGraphType<StringGraphType>>("name");
              Field<NonNullGraphType<StringGraphType>>("address");
          }
      }
      
      This is the type which we are going to send from a client as an argument for our mutations. As we can see, this class derives from the InputObjectGraphType class and not from the ObjectGraphType as before. In the constructor, we just populate the Name property and create two fields. We can see that we don’t have the Id and Accounts properties because we don’t need them for the mutations. If we can recall the starting article of this series, there we can see that for the queries we had to create the AppQuery class. Well, it is the same for mutations, just we are not going to create the AppQuery class but the AppMutation class inside the GraphQLQueries folder:
      public class AppMutation : ObjectGraphType
      {
          public AppMutation()
          {
          }
      }
      
      Finally, we need to enhance our Schema class, with the Mutation property:
      public class AppSchema : Schema
      {
          public AppSchema(IDependencyResolver resolver)
              :base(resolver)
          {
              Query = resolver.Resolve<AppQuery>();
              Mutation = resolver.Resolve<AppMutation>();
          }
      }
      
      Excellent. We are ready to create some mutations in our project.

       Create Mutation

      Let’s start with the IOwnerRepository interface modification:
      public interface IOwnerRepository
      {
          IEnumerable<Owner> GetAll();
          Owner GetById(Guid id);
          Owner CreateOwner(Owner owner);
      }
      
      
      We see that the CreateOwner method returns a newly created owner object, which is quite common in GraphQL. Now, we can add the CreateOwner method inside the OwnerRepository class:
      public Owner CreateOwner(Owner owner)
      {
          owner.Id = Guid.NewGuid();
          _context.Add(owner);
          _context.SaveChanges();
          return owner;
      }
      
      Finally, we can modify the AppMutation class:
      public class AppMutation : ObjectGraphType
      {
          public AppMutation(IOwnerRepository repository)
          {
              Field<OwnerType>(
                  "createOwner",
                  arguments: new QueryArguments(new QueryArgument<NonNullGraphType<OwnerInputType>> { Name = "owner" }),
                  resolve: context =>
                  {
                      var owner = context.GetArgument<Owner>("owner");
                      return repository.CreateOwner(owner);
                  }
              );
          }
      }
      
      So, we create a field to return the OwnerType object, with the „createOwner“ name, a single argument of the OwnerInputType type and with the resolve action which is going to execute the CreateOwner method from our repository. And that is it. Let’s start our project, open Playground and send a request: Create Mutation - GraphQL Mutations So, instead of the query keyword, we use the mutation keyword for mutations. And this is the only new difference. We have an argument, a call to the createOwner mutation and the fields that we require as a part of the result. On the Playground’s right side, we can see that the creation has been successful and we have a new Owner object returned. Awesome, now we can continue on.

      Update Mutation

      As we did in a previous section of this article, we are going to start with IOwnerRepository modification:
      public interface IOwnerRepository
      {
          IEnumerable<Owner> GetAll();
          Owner GetById(Guid id);
          Owner CreateOwner(Owner owner);
          Owner UpdateOwner(Owner dbOwner, Owner owner);
      }
      
      Let’s move on to the OwnerRepository file:
      public Owner UpdateOwner(Owner dbOwner, Owner owner)
      {
          dbOwner.Name = owner.Name;
          dbOwner.Address = owner.Address;
      
          _context.SaveChanges();
      
          return dbOwner;
      }
      
      Lastly, we have to add an additional field in a constructor of the AppMutation class:
      Field<OwnerType>(
          "updateOwner",
          arguments: new QueryArguments(
              new QueryArgument<NonNullGraphType<OwnerInputType>> { Name = "owner" }, 
              new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "ownerId" }),
          resolve: context =>
          {
              var owner = context.GetArgument<Owner>("owner");
              var ownerId = context.GetArgument<Guid>("ownerId");
      
              var dbOwner = repository.GetById(ownerId);
              if (dbOwner == null)
              {
                  context.Errors.Add(new ExecutionError("Couldn't find owner in db."));
                  return null;
              }
      
              return repository.UpdateOwner(dbOwner, owner);
          }
      );
      
      Excellent. It is time to test this: Update mutation - GraphQL Mutations Everything is working as it supposed to. Let’s move on.

      Delete Mutation

      Following the same pattern, let’s modify the interface first:
      public interface IOwnerRepository
      {
          IEnumerable<Owner> GetAll();
          Owner GetById(Guid id);
          Owner CreateOwner(Owner owner);
          Owner UpdateOwner(Owner dbOwner, Owner owner);
          void DeleteOwner(Owner owner);
      }
      
      Then, let’s continue with the OwnerRepository modification:
      public void DeleteOwner(Owner owner)
      {
          _context.Remove(owner);
          _context.SaveChanges();
      }
      
      The last thing we need to do is to modify AppMutation file:
      Field<StringGraphType>(
          "deleteOwner",
          arguments: new QueryArguments(new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "ownerId" }),
          resolve: context =>
          {
              var ownerId = context.GetArgument<Guid>("ownerId");
              var owner = repository.GetById(ownerId);
              if (owner == null)
              {
                  context.Errors.Add(new ExecutionError("Couldn't find owner in db."));
                  return null;
              }
      
              repository.DeleteOwner(owner);
              return $"The owner with the id: {ownerId} has been successfully deleted from db.";
          }
      );
      
      And let’s test this as well: Delete mutation - GraphQL Mutations You can send a query to fetch all the owners and see for your self that this owner entity is not in the database anymore.

      Conclusion

      There we go, all the mutations are completed now. Now we know, how to use Input type files for the mutations, how to create different mutation actions and how to create mutation requests from a client side. In the next part of this tutorial, we are going to create two applications, ASP.NET Core Web API and Angular, to consume the GraphQL application that we created until now. So, see you there. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48225 0 0 0 1276 0 0 1278 1276 0
      Consuming a GraphQL API with ASP.NET Core https://code-maze.com/consume-graphql-api-with-asp-net-core/ Mon, 06 May 2019 06:00:43 +0000 https://code-maze.com/?p=48239 previous article, we have finished creating mutations in the GraphQL application, thus finishing the GraphQL integration in ASP.NET Core Web API. In this article, we are going to consume GraphQL API by creating an ASP.NET Core app. To consume GraphQL API with the ASP.NET Core app, we are going to use the GraphQL.Client library which will help us in the process. We won’t dive deep into the ASP.NET Core logic, just as much as we need to create our consuming application. But if you want to read in great detail about ASP.NET Core project development you can do that by reading ASP.NET Core Tutorial. [sc name="part_of_series" headline="This article is part of the series"] To download the source code, visit the Consuming a GraphQL API with ASP.NET Core Source Code. For the complete navigation of this tutorial visit GraphQL ASP.NET Core Tutorial. We are going to divide this article into the following sections:

      Preparing the ASP.NET Core Client Project

      Let’s start, by creating new ASP.NET Core Web API project. As soon as the project is created, we are going to modify the launchsettings.json file, by setting the launchBrowser property to false and applicationUrl property to https://localhost:5003;http://localhost:5004. To learn more about ASP.NET Core project configuration, you can read the ASP.NET Core Project Configuration article. To continue on, let’s modify the appsettings.json file, by adding address towards the GraphQL application:
      {
        "Logging": {
          "LogLevel": {
            "Default": "Warning"
          }
        },
        "GraphQLURI": "https://localhost:5001/graphql",
        "AllowedHosts": "*"
      }
      
      Excellent. Now, we can install the required library: GraphQL Client Library - Consume GraphQL API Or by using the Package Manager Console: PM> Install-Package GraphQL.Client -Version 1.0.3 After the installation, we are going to register it in the Startup class:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddScoped(x => new GraphQL.Client.GraphQLClient(Configuration["GraphQLURI"]));
      
          services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
      }
      
      Our application's name is GraphQLClient, thus the full name for registering the library GraphQL.Client.GraphQLClient. If you named your app differently, just simple GraphQLClient would be enough. The next step is creating the OwnerConsumer class, which will store all the queries and mutations:
      public class OwnerConsumer
      {
          private readonly GraphQL.Client.GraphQLClient _client;
      
          public OwnerConsumer(GraphQL.Client.GraphQLClient client)
          {
              _client = client;
          }
      }
      
      Now let’s register this class:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddScoped(x => new GraphQL.Client.GraphQLClient(Configuration["GraphQLURI"]));
          services.AddScoped<OwnerConsumer>();
      
          services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
      }
      
      That is it regarding configuration.

      Creating Model Classes

      In the first article of this tutorial, Getting Started with GraphQL in ASP.NET Core, you can see that we have the model classes in the starting project. Those classes are also required for the client application, so let’s create them:
      public enum TypeOfAccount
      {
          Cash,
          Savings,
          Expense,
          Income
      }
      
      public class Account
      {
          public Guid Id { get; set; }
          public TypeOfAccount Type { get; set; }
          public string Description { get; set; }
      }
      
      public class Owner
      {
          public Guid Id { get; set; }
          public string Name { get; set; }
          public string Address { get; set; }
      
          public ICollection<Account> Accounts { get; set; }
      }
      
      We are not done yet. In the third part of this tutorial, GraphQL Mutations, we have created the Input type class for the mutation actions. This class is required as well, so let’s create it:
      public class OwnerInput
      {
          public string Name { get; set; }
          public string Address { get; set; }
      }
      
      Awesome. Now we have everything prepared and are ready to start creating queries and mutations.

      Creating Queries and Mutations to Consume GraphQL API

      Let’s open the OwnerConsumer class and add the GetAllOwners method:
      public async Task<List<Owner>> GetAllOwners()
      {
          var query = new GraphQLRequest
          {
              Query = @"
                      query ownersQuery{
                        owners {
                          id
                          name
                          address
                          accounts {
                            id
                            type
                            description
                          }
                        }
                      }"
          };
      
          var response = await _client.PostAsync(query);
          return response.GetDataFieldAs<List<Owner>>("owners");
      }
      
      As you can see, we are creating a new GraphQLRequest object which contains a Query property for the query we want to send to the GraphQL API. This query is the same as the one we used with the UI.Playground tool in previous articles. To execute the query, we call the PostAsync method which returns a response. Finally, we convert our result in the required type by using the GetDataFieldsAs method. We need to pay attention to the GetDataFieldAs method’s argument though. That value must match the query name: Query match names - Consume GraphQL API Here are all the other queries and mutations from the same class: Get Query:
      public async Task<Owner> GetOwner(Guid id)
      {
          var query = new GraphQLRequest
          {
              Query = @"
                      query ownerQuery($ownerID: ID!) {
                        owner(ownerId: $ownerID) {
                          id
                          name
                          address
                          accounts {
                            id
                            type
                            description
                          }
                        }
                      }",
              Variables = new { ownerID = id }
          };
      
          var response = await _client.PostAsync(query);
          return response.GetDataFieldAs<Owner>("owner");
      }
      
      Create Mutation:
      public async Task<Owner> CreateOwner(OwnerInput ownerToCreate)
      {
          var query = new GraphQLRequest
          {
              Query = @"
                      mutation($owner: ownerInput!){
                        createOwner(owner: $owner){
                          id,
                          name,
                          address
                        }
                      }",
              Variables = new {owner = ownerToCreate}
          };
      
          var response = await _client.PostAsync(query);
          return response.GetDataFieldAs<Owner>("createOwner");
      }
      
      Update Mutation:
      public async Task<Owner> UpdateOwner(Guid id, OwnerInput ownerToUpdate)
      {
          var query = new GraphQLRequest
          {
              Query = @"
                      mutation($owner: ownerInput!, $ownerId: ID!){
                        updateOwner(owner: $owner, ownerId: $ownerId){
                          id,
                          name,
                          address
                        }
                     }",
              Variables = new { owner = ownerToUpdate, ownerId = id }
          };
      
          var response = await _client.PostAsync(query);
          return response.GetDataFieldAs<Owner>("updateOwner");
       }
      
      Delete Mutation:
      public async Task<string> DeleteOwner(Guid id)
      {
          var query = new GraphQLRequest
          {
              Query = @"
                     mutation($ownerId: ID!){
                        deleteOwner(ownerId: $ownerId)
                      }",
              Variables = new { ownerId = id }
          };
      
          var response = await _client.PostAsync(query);
          return response.Data.deleteOwner;
      }
      

      Implementing a Controller

      We are going to use the default ValuesController for the testing purpose:
      [Route("api/[controller]")]
      [ApiController]
      public class ValuesController : ControllerBase
      {
          private readonly OwnerConsumer _consumer;
      
          public ValuesController(OwnerConsumer consumer)
          {
              _consumer = consumer;
          }
      
          [HttpGet]
          public async Task<IActionResult> Get()
          {
              var owners = await _consumer.GetAllOwners();
              return Ok(owners);
          }
          … other actions
      }
      
      Now we can test this via Postman: Postman Result Excellent, it works like a charm. Of course, you can test all the other queries and mutations by using Postman as well.

      Conclusion

      In this article we have learned:
      • How to prepare ASP.NET Core Client application to consume the GraphQL app
      • What library do we need to help us with the consuming process
      • How to create queries and mutations from a consuming application
      In the next article, we are going to create an Angular application to consume the GraphQL API, by using the Apollo library. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48239 0 0 0 1596 0 0
      Consuming a GraphQL API with Angular https://code-maze.com/consuming-graphql-api-angular/ Mon, 13 May 2019 06:01:59 +0000 https://code-maze.com/?p=48295 previous article, we have created an ASP.NET Core application to consume a GraphQL API. But we don't have to use only ASP.NET Core app to consume a GraphQL API, we can do the same thing with the Angular application. To accomplish that, we are going to use the Apollo library which will help us in the process. We won’t dive deep into the Angular project development, for that, we have a great Angular Tutorial on that subject as well. [sc name="part_of_series" headline="This article is part of the series"] To download the source code, visit the Consuming a GraphQL API with Angular Source Code. For the complete navigation of this tutorial visit GraphQL ASP.NET Core Tutorial. We are going to divide this article into the following sections:

      Preparing the Angular Project

      After we have finished with the ASP.NET Core client app, let’s create an Angular app as well. We are going to start by creating a new Angular project without navigation module and with CSS as default styles. As soon as creation is over, we are going to install a set of libraries required for the Apollo Client to work with Angular:
      npm install apollo-angular apollo-angular-link-http apollo-client apollo-cache-inmemory graphql-tag graphql -- save
      The next step is to modify app.module.ts file:
      import { BrowserModule } from '@angular/platform-browser';
      import { NgModule } from '@angular/core';
      import { ApolloModule } from 'apollo-angular';
      import { HttpLinkModule } from 'apollo-angular-link-http';
      import { HttpClientModule } from '@angular/common/http';
      
      import { AppComponent } from './app.component';
      
      @NgModule({
        declarations: [
          AppComponent
        ],
        imports: [
          BrowserModule,
          ApolloModule,
          HttpLinkModule,
          HttpClientModule
        ],
        providers: [],
        bootstrap: [AppComponent]
      })
      export class AppModule { }
      
      We import the ApolloModule and HttpLinkModule files, required for the Appolo integration with Angular. But, we can see that HttpClientModule is included as well. That’s because in order to work properly, HttpLinkModule internally uses HttpClientModule. Let’s move on. In the same way that we created model files for the ASP.NET Core client application, we are going to create them here. So, let’s create a new folder „types“ and inside several type files:
      export type OwnerInputType = {
          name: string;
          address: string;
      }
      
      export type AccountType = {
          'id': string;
          'description': string;
          'ownerId' : string;
          'type': string;
      }
      
      import { AccountType } from './accountType';
      
      export type OwnerType = {
          'id': string;
          'name': string;
          'address': string;
          'accounts': AccountType[];
      }
      
      Now, we can create a graphql.service.ts file and modify it as well:
      import { Injectable } from '@angular/core';
      import { Apollo } from 'apollo-angular';
      import { HttpLink } from 'apollo-angular-link-http';
      import { InMemoryCache } from 'apollo-cache-inmemory';
      import gql from 'graphql-tag';
      import { OwnerType } from './types/ownerType';
      import { OwnerInputType } from './types/ownerInputType';
      
      @Injectable({
        providedIn: 'root'
      })
      export class GraphqlService {
        public owners: OwnerType[];
        public owner: OwnerType;
        public createdOwner: OwnerType;
        public updatedOwner: OwnerType;
      
        constructor(private apollo: Apollo, httpLink: HttpLink) {
          apollo.create({
            link: httpLink.create({ uri: 'https://localhost:5001/graphql' }),
            cache: new InMemoryCache()
          })
        }
      }
      
      We have an instance of the Apollo service with all the required configuration (link and cache). Both properties are required and must be populated. After these configuration actions, we are ready to create some queries.

      Creating Queries and Mutations

      Let’s modify the graphql.service.ts file, by adding our first query:
      public getOwners = () => {
          this.apollo.query({
            query: gql`query getOwners{
            owners{
              id,
              name,
              address,
              accounts{
                id,
                description,
                type
              }
            }
          }`
          }).subscribe(result => {
            this.owners = result.data as OwnerType[];
      	console.log(this.owners);
          })
        }
      
      We are using the Apollo service with its query function to write the entire GraphQL query. We’re using the imported gql tag as well, in order to be able to write GraphQL code as a multi-line string. Now, let’s modify the app.component.ts file in order to test this query:
      import { Component, OnInit } from '@angular/core';
      import { GraphqlService } from './graphql.service';
      
      @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
      })
      export class AppComponent  implements OnInit{
      
        constructor(private service: GraphqlService) {
        }
      
        ngOnInit(): void {
          this.service.getOwners();
        }
        title = 'angulargraphqlclient';
      }
      
      As soon as we start the Angular application, we can inspect the result: Angular GraphQL Query Result - Using Angular To Consume GraphQL API Excellent. Here are all the other queries and mutations from the graphql.service.ts file:
      public getOwner = (id) => {
          this.apollo.query({
            query: gql`query getOwner($ownerID: ID!){
            owner(ownerId: $ownerID){
              id,
              name,
              address,
              accounts{
                id,
                description,
                type
              }
            }
          }`,
            variables: { ownerID: id }
          }).subscribe(result => {
            this.owner = result.data as OwnerType;
          })
        }
      
      public createOwner = (ownerToCreate: OwnerInputType) => {
          this.apollo.mutate({
            mutation: gql`mutation($owner: ownerInput!){
              createOwner(owner: $owner){
                id,
                name,
                address
              }
            }`,
            variables: {owner: ownerToCreate}
          }).subscribe(result => {
            this.createdOwner = result.data as OwnerType;
          })
        }
      
      public updateOwner = (ownerToUpdate: OwnerInputType, id: string) => {
          this.apollo.mutate({
            mutation: gql`mutation($owner: ownerInput!, $ownerId: ID!){
              updateOwner(owner: $owner, ownerId: $ownerId){
                id,
                name,
                address
              }
            }`,
            variables: {owner: ownerToUpdate, ownerId: id}
          }).subscribe(result => {
            this.updatedOwner = result.data as OwnerType;
          })
        }
      
      public deleteOwner = (id: string) => {
          this.apollo.mutate({
            mutation: gql`mutation($ownerId: ID!){
              deleteOwner(ownerId: $ownerId)
             }`,
            variables: { ownerId: id}
          }).subscribe(res => {
            console.log(res.data);
          })
        }
      
      You can test them by modifying the app.component.ts file, or if you want to create a new component to consume all these results.

      Conclusion

      So, there we go. We have learned a lot of great stuff about GraphQL and its integration with ASP.NET Core. Of course, with these last two articles, we went even further, by creating two client applications to consume our GraphQL app. We hope you have enjoyed this tutorial and if you have any suggestions or question, don’t hesitate to leave a comment in the comment section. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48295 0 0 0 1105 http://angular-meteor.com/ 0 0 1116 1105 0 1117 http://angular-meteor.com/ 1116 0 1283 0 0
      Views, Partial Views, and Layouts in ASP.NET Core MVC https://code-maze.com/views-partial-views-and-layouts-in-asp-net-core-mvc/ Mon, 10 Jun 2019 06:00:15 +0000 https://code-maze.com/?p=47443 In the MVC pattern, Views handle the application's data presentation and user interaction. They also help to establish a Separation of Concerns (SoC) within an MVC application by separating the user interface markup from other parts of the application. A view is an HTML template with the embedded Razor markup. It has the .cshtml extension and is based on C#. Razor markup interacts with HTML markup to produce a web page which is then sent to the client. Usually, there are separate view files corresponding to each controller action method and view files are grouped into folders named for each of the controllers. Views are stored in the Views folder at the root of the application. Let’s take a look at the BookStore app that we created in the previous article: folders and files The views for the BooksController are stationed inside the Books folder within the Views folder. The Books folder contains the views for the Create, Delete, Details, Edit, and Index methods. When a user requests one of these actions, action methods in the BooksController uses the appropriate view to build a web page and return it to the user. In this article, we’ll reuse the model and controller that we created in the previous part with some minor changes. But we’ll create the views from scratch. We strongly recommend visiting the complete navigation of this series: ASP.NET Core MVC Series. To download this article's source code visit: Views, Partial Views and Layouts Source Code. We have divided this article into the following sections:

      Defining Model & Controller

      Let’s reuse our existing model:
      public class Book
      {
          public int Id { get; set; }
      
          [Display(Name = "Book Title")]
          [Required]
          public string Title { get; set; }
      
          public string Genre { get; set; }
      
          [DataType(DataType.Currency)]
          [Range(1, 100)]
          public decimal Price { get; set; }
      
          [Display(Name = "Publish Date")]
          [DataType(DataType.Date)]
          public DateTime PublishDate { get; set; }
      }
      
      To keep things simple, we are going to implement an index, details and edit views from scratch. This will cover all common scenarios that we encounter while creating views. Now let’s slightly modify our existing controller by removing the action methods that we do not use:
      public class BooksController : Controller
      {
          private readonly BookStoreWithDataContext _context;
      
          public BooksController(BookStoreWithDataContext context)
          {
              _context = context;
          }
      
          // GET: Books
          public async Task<IActionResult> Index()
          {
              return View(await _context.Book.ToListAsync());
          }
      
          // GET: Books/Details/5
          public async Task<IActionResult> Details(int? id)
          {
              if (id == null)
              {
                  return NotFound();
              }
      
              var book = await _context.Book
                  .FirstOrDefaultAsync(m => m.Id == id);
              if (book == null)
              {
                  return NotFound();
              }
      
              return View(book);
          }
      
          // GET: Books/Edit/5
          public async Task<IActionResult> Edit(int? id)
          {
              if (id == null)
              {
                  return NotFound();
              }
      
              var book = await _context.Book.FindAsync(id);
              if (book == null)
              {
                  return NotFound();
              }
              return View(book);
          }
      
          // POST: Books/Edit/5
          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
          [HttpPost]
          [ValidateAntiForgeryToken]
          public async Task<IActionResult> Edit(int id, [Bind("Id,Title,Genre,Price,PublishDate")] Book book)
          {
              if (id != book.Id)
              {
                  return NotFound();
              }
      
              if (ModelState.IsValid)
              {
                  try
                  {
                      _context.Update(book);
                      await _context.SaveChangesAsync();
                  }
                  catch (DbUpdateConcurrencyException)
                  {
                      if (!BookExists(book.Id))
                      {
                          return NotFound();
                      }
                      else
                      {
                          throw;
                      }
                  }
                  return RedirectToAction(nameof(Index));
              }
              return View(book);
          }
      
          private bool BookExists(int id)
          {
              return _context.Book.Any(e => e.Id == id);
          }
      }
      
      We have the model and controller ready now. Next step is to create the views.

      Using Razor Markup to Create Views

      Views that are specific to a controller will be placed in the Views/[ControllerName] folder. Views that are shared among controllers are placed in the Views/Shared folder. To create a view, let’s add a new file and give it the same name as its associated controller action with the .cshtml file extension. For example, to create a view that corresponds to the Index action in the BooksController, we need to create an Index.cshtml file in the Views/Books folder. By doing so we'll have a view for the index page. In the first part of this series, we used HTML Helper methods to create our views. In this article, we are going to use a different approach for creating views using tag helpers. Tag helpers provide an HTML-friendly development experience. For the most part, Razor markup using Tag Helpers looks like standard HTML.Tag Helpers reduce the explicit transitions between HTML and C# in Razor views. In many cases, Tag Helpers provide an alternative approach to a specific HTML Helper, but it's important to understand that Tag Helpers can't replace HTML Helpers because some HTML Helpers don’t have a Tag Helper equivalent. So in some cases, we’ll still have to use HTML helpers.

      Index View

      Now let’s create the view for the Index page:
      @model IEnumerable<WorkingWithViews.Models.Book>
      
      @{
          ViewData["Title"] = "Index";
          Book firstBook = Model.ToList().FirstOrDefault();
      }
      
      <h1>Index</h1>
      
      <p>
          <a asp-action="Create">Create New</a>
      </p>
      <table class="table">
          <thead>
              <tr>
                  <th>
                      <label asp-for="@firstBook.Id"></label>
                  </th>
                  <th>
                      <label asp-for="@firstBook.Title"></label>
                  </th>
                  <th>
                      <label asp-for="@firstBook.Genre"></label>
                  </th>
                  <th>
                      <label asp-for="@firstBook.Price"></label>
                  </th>
                  <th>
                      <label asp-for="@firstBook.PublishDate"></label>
                  </th>
                  <th></th>
              </tr>
          </thead>
          <tbody>
              @foreach (var item in Model)
              {
                  <tr>
                      <td>
                          <label>@item.Id</label>
                      </td>
                      <td>                    
                          <label>@item.Title</label>
                      </td>
                      <td>                    
                          <label>@item.Genre</label>
                      </td>
                      <td>                    
                          <label>@item.Price</label>
                      </td>
                      <td>                    
                          <label>@item.PublishDate</label>
                      </td>
                      <td>
                          <a asp-action="Edit" asp-route-id="@item.Id">Edit</a>
                          <a asp-action="Details" asp-route-id="@item.Id">Details</a>
                          <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
                      </td>
                  </tr>
              }
          </tbody>
      </table>
      
      We are using a strongly typed model here and the model is of IEnumerable<Book> type. First, we declare a variable and assign the first book in the list to it. We do this to get the property names and assign them to the headers:
      Book firstBook = Model.ToList().FirstOrDefault();
      The asp-for attribute extracts the name of the specified model property into the rendered HTML. So let’s see how to render a label with the name of the Title property:
       <label asp-for="@firstBook.Title"></label>
      Great! Now we need to render all the items in the books collection. For that purpose, we use a foreach loop which helps us render an HTML table. We can render a label with property values:
      <label>@item.Title</label>
      For creating action links, we can use the asp-action attribute and for passing parameters, we can use asp-route-{parametername} format. So in this case, for id parameter we use asp-route-id:
      <a asp-action="Edit" asp-route-id="@item.Id">Edit</a>

      Details View

      Now, let’s create the details view:
      @model WorkingWithViews.Models.Book
      
      @{
          ViewData["Title"] = "Details";
      }
      
      <h1>Details</h1>
      
      <div>
          <h4>Book</h4>
          <hr />
          <dl class="row">
              <dt class="col-sm-2">
                  <label asp-for="Title"></label>
              </dt>
              <dd class="col-sm-10">
                  @Model.Title
              </dd>
              <dt class="col-sm-2">
                  <label asp-for="Genre"></label>            
              </dt>
              <dd class="col-sm-10">            
                  @Model.Genre
              </dd>
              <dt class="col-sm-2">
                  <label asp-for="Price"></label>            
              </dt>
              <dd class="col-sm-10">            
                  @Model.Price.ToString("C")
              </dd>
              <dt class="col-sm-2">
                  <label asp-for="PublishDate"></label>            
              </dt>
              <dd class="col-sm-10">            
                  @Model.PublishDate.ToShortDateString()
              </dd>
          </dl>
      </div>
      <div>    
          <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a>|
          <a asp-action="Index">Back to List</a>
      </div>
      
      We’ve created this similar to the index view. But the model is of Book type. For retrieving the property name, we can use the asp-for attribute:
      <label asp-for="Title"></label>
      For displaying the property values, we can access the model properties using @Model directive:
      <dd class="col-sm-10">
          @Model.Title
      </dd>

      Edit View

      As soon as we are finished with the Details view, we can continue with the Edit view creation:
      @model WorkingWithViews.Models.Book
      
      @{
          ViewData["Title"] = "Edit";
      }
      
      <h1>Edit</h1>
      
      <h4>Book</h4>
      <hr />
      <div class="row">
          <div class="col-md-4">
              <form asp-action="Edit">
                  <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                  <input type="hidden" asp-for="Id" />
                  <div class="form-group">
                      <label asp-for="Title" class="control-label"></label>
                      <input asp-for="Title" class="form-control" />
                      <span asp-validation-for="Title" class="text-danger"></span>
                  </div>
                  <div class="form-group">
                      <label asp-for="Genre" class="control-label"></label>
                      <input asp-for="Genre" class="form-control" />
                      <span asp-validation-for="Genre" class="text-danger"></span>
                  </div>
                  <div class="form-group">
                      <label asp-for="Price" class="control-label"></label>
                      <input asp-for="Price" class="form-control" />
                      <span asp-validation-for="Price" class="text-danger"></span>
                  </div>
                  <div class="form-group">
                      <label asp-for="PublishDate" class="control-label"></label>
                      <input asp-for="PublishDate" class="form-control" />
                      <span asp-validation-for="PublishDate" class="text-danger"></span>
                  </div>
                  <div class="form-group">
                      <input type="submit" value="Save" class="btn btn-primary" />
                  </div>
              </form>
          </div>
      </div>
      
      <div>
          <a asp-action="Index">Back to List</a>
      </div>
      
      For the edit view, the model is of Book type as well. The asp-validation-summary tag helper is used for displaying the validation summary:
      <div asp-validation-summary="ModelOnly" class="text-danger"></div>
      ValidationSummary.ModelOnly will display only validation messages that apply to the model level. ValidationSummary.All will display both property and model level validations messages. For each property, we have created a label for displaying the property name, an input field for editing the value and a span element for displaying the validation messages specific to that property:
      <label asp-for="Title" class="control-label"></label>
      <input asp-for="Title" class="form-control" />
      <span asp-validation-for="Title" class="text-danger"></span>
      
      We have a button for submitting the form towards the bottom:
      <input type="submit" value="Save" class="btn btn-primary" />
      When we try to save the page without providing valid values, validation errors will be displayed depending on the settings we provide for asp-validation-summary. So, let's see this in action. When we set the value as ModelOnly: validation without summary If the setting value is All: validation with summary That’s it. We have created views for the Index, Details and Edit pages.

      The Concept of Partial Views

      A partial view is a Razor markup file (.cshtml) that renders HTML output within another view's rendered output. Partial views are extremely useful in two scenarios. The first scenario is when we want to break up large markup files into smaller components. If our markup file is large, complex and composed of several logical pieces, we should split each piece into a partial view. Then the code in the markup file will be manageable because the markup will only contain the overall page structure and references to the partial views. The second scenario is when we want to reduce the duplication of common markup content across markup files. When we need to use the same markup elements across markup files, we can move that markup content into a partial view and reuse it. That way the future changes to that markup need to be done in just one place and we improved the modularity of our code. However, Partial view is not the recommended approach to maintain common layout elements. We’ll learn the best practice to create common layout elements in the next section. Let’s say we need to display the Authors information in multiple places in our BookStore application. Creating a partial view for displaying authors information will be the ideal approach to go for. Right click on the Shared folder and select Add -> View: Add partial views In the Add MVC View dialog box, we are going to give the View Name as _Authors, then check the Create as a Partial View option and click Add: Add partial views2 Let’s add some dummy text to the _authors.cshtml file:
      <h3>Authors</h3>
      
      <p>This section is used to display information about authors.</p>
      
      Now, let’s add this partial view into the book details view using the partial tag helper:
      <div>
              <partial name="_Authors" />
      </div>
      
      That’s it. We can see that the book details page now displays the Authors section as well: details page with authors section We can reuse this section in other views by just placing this partial view inside them. In this section, we’ve learned how to create a partial view and how to use it inside a view.

      Layouts in ASP.NET Core

      Most web applications have a common layout that provides the user with a consistent experience as they navigate between the pages. In an ASP.NET Core MVC application, we use a layout file to provide a consistent experience across the pages. The layout typically includes common user interface elements such as a header, menu, and a footer. Many pages within the application shares common resources such as scripts and stylesheets. We can define all of these shared elements in a layout file, which can then be referenced by any view within the application. Layouts help in reducing duplicate code in views. When we create an ASP.Net Core MVC application using the default template provided by Visual Studio, it generates a default layout file(_Layout.cshtml) and places in the Shared folder. While creating views, we have an option to specify a layout file. We can change this later by setting the Layout property of the view: layout file in solution explorer Now let’s examine the default layout file. The layout file contains a <head> section at the top which contains the Title, link to the stylesheet etc.
      <head>
          <meta charset="utf-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <title>@ViewData["Title"] - WorkingWithViews</title>
          <link rel="stylesheet" href="~/css/site.css" />
      </head>
      Then we have a <body> section which contains a header with the menu. The body also has a container div inside which the RenderBody() method is called. This method renders the content page:
      <div class="container">
          <partial name="_CookieConsentPartial" />
          <main role="main" class="pb-3">
              @RenderBody()
          </main>
      </div>
      This is followed by a <footer> section. We usually load the scripts towards the end of the document to ensure that all dependencies are loaded:
      <script src="~/js/site.js" asp-append-version="true"></script>
      @RenderSection("Scripts", required: false)
      
      In this section, we learned how to maintain a consistent look and feel for our application using a Layout file.

      Conclusion

      In this article we looked at the following topics:
      • Using Razor Markup to build Views
      • Reusing sections of pages using Partial Views
      • Creating a common look and feel for the application using Layout files
      In the the next part of this series, we’ll take a look at state management in ASP.NET Core MVC. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47443 0 0 0 1579 0 0 1580 1579 0
      State Management in ASP.NET Core MVC https://code-maze.com/state-management-in-asp-net-core-mvc/ Mon, 17 Jun 2019 06:00:37 +0000 https://code-maze.com/?p=47552 HTTP is a stateless protocol. So HTTP requests are independent messages that don't retain user values or app states. We need to take additional steps to manage state between the requests. In this article, we are going to look at various approaches to the HTTP state management that we can use in our application. We strongly recommend visiting the complete navigation of this series: ASP.NET Core MVC Series. To download this article's source code visit: State Management in ASP.NET Core  Source Code. We'are going to divide this article into the following sections:

      Cookies

      Cookies store data in the user's browser. Browsers send cookies with every request and hence their size should be kept to a minimum. Ideally, we should only store an identifier in the cookie and we should store the corresponding data using the application. Most browsers restrict cookie size to 4096 bytes and only a limited number of cookies are available for each domain. Users can easily tamper or delete a cookie. Cookies can also expire on their own. Hence we should not use them to store sensitive information and their values should not be blindly trusted or used without proper validations. We often use cookies to personalize the content for a known user especially when we just identify a user without authentication. We can use the cookie to store some basic information like the user's name. Then we can use the cookie to access the user's personalized settings, such as their preferred color theme.

      A Cookie Example

      Let’s create a new project and add a controller with endpoints to read and write values into cookies.
      public class HomeController : Controller
      {
          public IActionResult Index()
          {
              //read cookie from Request object  
              string userName = Request.Cookies["UserName"];
              return View("Index", userName);
          }
      
          [HttpPost]
          public IActionResult Index(IFormCollection form)
          {
              string userName = form["userName"].ToString();
                  
              //set the key value in Cookie              
              CookieOptions option = new CookieOptions();
              option.Expires = DateTime.Now.AddMinutes(10);
              Response.Cookies.Append("UserName", userName, option);
      
              return RedirectToAction(nameof(Index));
          }
      
          public IActionResult RemoveCookie()
          {
              //Delete the cookie            
              Response.Cookies.Delete("UserName");
              return View("Index");
          }        
      }
      
      The Get version of the Index() method reads the UserName from the cookie and pass it to the view. We use the Post version of the Index() method to get the value for userName from the form collection and assign it to the cookie. For removing the cookie value, we use the RemoveCookie() endpoint. Now let’s create the view:
      @model string
      
      @{
         ViewData["Title"] = "Home Page";
      }
      
      @if (!string.IsNullOrWhiteSpace(Model))
      {
         @:<div>Welcome back, @Model</div>
         @Html.ActionLink("Forget Me", "RemoveCookie")
      }
      else
      {
         @:
         <form asp-action="Index">
             <span>Hey, seems like it's your first time here!</span><br />
             <label>Please provide your name:</label>
             @Html.TextBox("userName")
             <div class="form-group">
                 <input type="submit" value="Update" class="btn btn-primary" />
             </div>
         </form>
      }
      Here, we pass the UserName value into the View as the model. If the UserName has a value, we greet the user by that name and give the user an option to forget the value by removing it from the cookie. In case the UserName is empty, we show an input field for the user to enter his name and a submit button to update this in the cookie. Now let’s run the application. Initially, the application asks the user to provide a name: page without cookie Once we provide a name and click update, the application greets us: page with cookie Even if we close and reopen the application, we can see that the cookie value persists. Once we click “Forget Me”, the application removes the cookie and asks for the name again.

      Inspecting the Cookies

      Now, where does the application store the cookies? It is stored in the user’s browser. To inspect a value from a cookie, let's get into the Chrome DevTools window by clicking the F12 key and navigate to the Application tab. We can see that the browser stores cookies for each application: inspect cookie We should be mindful of the European Union General Data Protection Regulations (GDPR) when issuing cookies and dealing with privacy concerns. For more information about the APIs and templates provided by ASP.NET Core to support GDPR requirements, visit: General Data Protection Regulation (GDPR) support in ASP.NET Core In this section, we have learned how to use cookies in our application.

      Session State

      Session state is an ASP.NET Core mechanism to store user data while the user browses the application. It uses a store maintained by the application to persist data across requests from a client. We should store critical application data in the user’s database and we should cache it in a session only as a performance optimization if required. ASP.NET Core maintains session state by providing a cookie to the client that contains a session ID. The browser sends this cookie to the application with each request. The application uses the session ID to fetch the session data. While working with the Session state, we should keep the following things in mind:
      • A Session cookie is specific to the browser session
      • When a browser session ends, it deletes the session cookie
      • If the application receives a cookie for an expired session, it creates a new session that uses the same session cookie
      • An Application doesn't retain empty sessions
      • The application retains a session for a limited time after the last request. The app either sets the session timeout or uses the default value of 20 minutes
      • Session state is ideal for storing user data that are specific to a particular session but doesn't require permanent storage across sessions
      • An application deletes the data stored in session either when we call the ISession.Clear implementation or when the session expires
      • There's no default mechanism to inform the application that a client has closed the browser or deleted the session cookie or it is expired

      A Session State Example

      We need to configure the session state before using it in our application. This can be done in the ConfigureServices() method in the Startup.cs class:
      services.AddSession();
      Then, we need to enable session state in the Configure() method in the same class:
      app.UseSession();
      The order of configuration is important and we should invoke the UseSession() before invoking UseMVC(). Let’s create a controller with endpoints to set and read a value from the session:
      public class WelcomeController : Controller
      {
          public IActionResult Index()
          {
              HttpContext.Session.SetString("Name", "John");
              HttpContext.Session.SetInt32("Age", 32);
      
              return View();
          }
      
          public IActionResult Get()
          {
              User newUser = new User()
              {
                  Name = HttpContext.Session.GetString("Name"),
                  Age = HttpContext.Session.GetInt32("Age").Value
              };
      
              return View(newUser);
          }
      }
      The Index() method sets the values into session and Get() method reads the values from the session and passes them into the view. Let's auto-generate a view to display the model values by right-clicking on the Get() method and using the “Add View” option. Now let’s run the application and navigate to /welcome. This will set the session values. Now let’s navigate to /welcome/get: get values from session We retrieve values from the session and display it on the page. In this section, we have seen how to set data in the session and retrieve it.

      Query strings

      We can pass a limited amount of data from one request to another by adding it to the query string of the new request. This is useful for capturing the state in a persistent manner and allows sharing of links with the embedded state. Let’s add a new method in our WelcomeController:
      public IActionResult GetQueryString(string name, int age)
      {
          User newUser = new User()
          {
              Name = name,
              Age = age
          };
          return View(newUser);
      }
      
      Model binding maps data from HTTP requests to action method parameters. So if we provide the values for name and age as either form values, route values or query strings, we can bind those to the parameters of our action method. For displaying the model values let’s auto-generate a view as we did in the previous section. Now let’s invoke this method by passing query string parameters: /welcome/getquerystring?name=John&age=31 page with query string values We can retrieve both the name and age values from the query string and display it on the page. As URL query strings are public, we should never use query strings for sensitive data. In addition to unintended sharing, including data in query strings will make our application vulnerable to Cross-Site Request Forgery (CSRF) attacks, which can trick users into visiting malicious sites while authenticated. Attackers can then steal user data or take malicious actions on behalf of the user. For knowing more about protecting our application against CSRF attacks, visit Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks. In this section, we have learned how to pass parameters as query strings and read them in our application.

      Hidden Fields

      We can save data in hidden form fields and send back in the next request. Sometimes we require some data to be stored on the client side without displaying it on the page. Later when the user takes some action, we’ll need that data to be passed on to the server side. This is a common scenario in many applications and hidden fields provide a good solution for this. Let’s add two methods in our WelcomeController:
      [HttpGet]
      public IActionResult SetHiddenFieldValue()
      {
          User newUser = new User()
          {
              Id = 101,
              Name = "John",
              Age = 31
          };
          return View(newUser);
      }
      
      [HttpPost]
      public IActionResult SetHiddenFieldValue(IFormCollection keyValues)
      {
          var id = keyValues["Id"];
          return View();
      }
      
       The GET version of theSetHiddenValue() method creates a user object and passes that into the view. We use the POST version of the SetHiddenValue() method to read the value of a hidden field Id from FormCollection. In the View, we can create a hidden field and bind the Id value from Model:
      @Html.HiddenFor(model => model.Id)
      Then we can use a submit button to submit the form:
      <input type="submit" value="Submit" />|
      Now let’s run the application and navigate to /Welcome/SetHiddenFieldValue: page with hidden field value On inspecting the page source, we can see that a hidden field is generated on the page with the Id as the value:
      <input id="Id" name="Id" type="hidden" value="101">
      Now let’s click the submit button after putting a breakpoint in the POST method. We can retrieve the Id value from the FormCollection: reading hidden field value - State Management in ASP.NET Core MVC Since the client can potentially tamper with the data, our application must always revalidate the data stored in hidden fields. In this section, we have learned how to store data in hidden fields and how to retrieve them.

      TempData

      ASP.NET Core exposes the TempData property which can be used to store data until it is read. We can use the Keep() and Peek() methods to examine the data without deletion. TempData is particularly useful when we require the data for more than a single request. We can access them from controllers and views. TempData is implemented by TempData providers using either cookies or session state.

      A TempData Example

      Let’s create a controller with three endpoints. In the First() method, let’s set a value into TempData. Then let’s try to read it in Second() and Third() methods:
      public class TempDataController : Controller
      {
          public IActionResult First()
          {
              TempData["UserId"] = 101;
              return View();
          }
      
          public IActionResult Second()
          {
              var userId = TempData["UserId"] ?? null;
              return View();
          }
      
          public IActionResult Third()
          {
              var userId = TempData["UserId"] ?? null;
              return View();
          }
      }
      
      Now let’s run the application by placing breakpoints in the Second() and Third() methods. We can see that the TempData is set in the First() request and when we try to access it in the Second() method, it is available. But when we try to access it in the Third() method, it is unavailable as is retains its value only till its read. Now let’s move the code to access TempData from the controller methods to the views. Let’s create a view for the Second() action method:
      @{
          ViewData["Title"] = "Second";
          var userId = TempData["UserId"]?.ToString();
      }
      
      <h1>Second</h1>
      User Id : @userId
      
      Similarly, let’s create a view for the Third() action method:
      @{
          ViewData["Title"] = "Third";
          var userId= TempData["UserId"]?.ToString();
      }
      
      <h1>Third</h1>
      User Id : @userId
      
      Let’s run the application and navigate to /first, /second and /third page with valid tempdata   page with invalid tempdata We can see that TempData is available when we read it for the first time and then it loses its value. Now, what if we need to persist the value of TempData even after we read it? We have two ways to do that:
      • TempData.Keep()/TempData.Keep(string key) - This method retains the value corresponding to the key passed in TempData. If no key is passed, it retains all values in TempData.
      • TempData.Peek(string key) - This method gets the value of the passed key from TempData and retains it for the next request.
      Let's slightly modify our second view with one of these methods:
      var userId = TempData["UserId"]?.ToString();
      TempData.Keep();
      Or
      var userId = TempData.Peek("UserId")?.ToString();
      Now let’s run the application and navigate to /first, /second and /third. We can see that the TempData value persists in the third page even after its read on the second page. Great! In this section, we have learned how to pass values from one controller action method to another or to the view using TempData.

      Passing Data into Views

      For passing data from the controller into the views, we can use two approaches:
      • Strongly Typed Data
      • Weakly Typed Data

      Strongly Typed Data

      In this approach, we pass a model from the controller into the view. We have explained this in detail in the first part of this series:
      return View(model);
      The advantage of this method is that we get a strongly typed model to work within the view:
      @model BookStore.Models.Book
      Then we can access the model properties from the view:
      @Model.Title

      Weakly Typed Data

      There are two approaches to passing a weakly typed data into the views:
      • ViewData
      • ViewBag

      ViewData

      ViewData is a dictionary object and we can get/set values using a key. ViewData exposes an instance of the ViewDataDictionary class. Let’s create a controller action method and set a value for UserId inside ViewData:
      public class ViewDataController : Controller
      {
          public IActionResult Index()
          {
              ViewData["UserId"] = 101;
              return View();
          }
      }
      Now let’s try to access the userId value inside the View:
      @{
          ViewData["Title"] = "Index";
          var userId = ViewData["UserId"]?.ToString();
      }
      
      <h1>ViewData</h1>
      
      User Id : @userId
      Then let's run the application and navigate to /viewdata: page with view data - State Management in ASP.NET Core MVC We can see the UserId value is read from ViewData and displayed on the page.

      ViewBag

      ViewBag is similar to ViewData but it is a dynamic object and we can add data into it without converting to a strongly typed object. In other words, ViewBag is just a dynamic wrapper around the ViewData. Let’s add a controller action method to set a few values in ViewBag:
      public class ViewBagController : Controller
      {
          public IActionResult Index()
          {
              ViewBag.UserId = 101;
              ViewBag.Name = "John";
              ViewBag.Age = 31;
      
              return View();
          }
      }
      Then let’s access it from the View and display the values:
      {
          ViewData["Title"] = "Index";
          var userId = ViewBag.UserId;
          var name = ViewBag.Name;
          var age = ViewBag.Age;
      }
      
      <h1>ViewBag</h1>
      
      User Id : @userId<br />
      Name : @name<br />
      Age : @age<br />
      
      Now let’s run the application and navigate to /viewbag: page with viewbag - State Management in ASP.NET Core MVC That’s it! In this section, we’ve learned how to set values in ViewData and ViewBag and how to display them in the Views.

      Conclusion

      In this article, we’ve learned the following topics:
      • Storing user-specific data in browser Cookies.
      • Using Session State to store data related to a user session.
      • Passing data between requests using Query Strings.
      • Using Hidden Fields to store data which are not displayed on a page.
      • Use TempData to pass data from a controller action method to another or a View.
      • Passing Data from a Controller into a View using strongly typed and weakly typed data such as ViewData and ViewBag.
      In the next part of the series, we will learn about routing capabilities in ASP.NET Core MVC. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      47552 0 0 0 1581 0 0
      Routing in ASP.NET Core MVC https://code-maze.com/routing-asp-net-core-mvc/ Mon, 24 Jun 2019 06:00:28 +0000 https://code-maze.com/?p=48120 In this article, we’re going to discuss the routing capabilities in ASP.NET Core MVC. We're also going to learn how to set up routes in two different ways. If you've missed some of the previous articles in the series we recommend visiting the series page: ASP.NET Core MVC Series. To download this article’s source code visit: Routing in ASP.NET Core MVC. We have divided this article into the following sections: Let's dive into the material.

      Routing in ASP.NET Core MVC

      Routing is the process through which the application matches an incoming URL path and executes the corresponding action methods. ASP.NET Core MVC uses a routing middleware to match the URLs of incoming requests and map them to specific action methods. We can define the routes either in the startup code or as attributes. They describe how we can match the URL paths with the action methods. We can also use routes to generate URLs for links that are sent out in responses. There are two types of routing for action methods:

      Conventional Routing

      When we create a new ASP.NET Core MVC application using the default template, the application configures a default routing. Let’s create a new project and examine this. After creating a new project with the default ASP.NET Core MVC template, let’s have a look at the startup.cs class. We can see that the application has configured a default routing in the Configure() method:
      app.UseMvc(routes =>
      {
          routes.MapRoute(
              name: "default",
              template: "{controller=Home}/{action=Index}/{id?}");
      });
      Inside the call to UseMvc(), we use the MapRoute() method to create a route by giving the name default. MVC configures the default route template as {controller=Home}/{action=Index}/{id?}. This will match the Index() method in HomeController with an optional parameter id by default. This can also match a URL path like /Books/Details/5 and will extract the route values { controller = Books, action = Details, id = 5 } by tokenizing the path. MVC will attempt to locate a controller named BooksController and run the action method Details() by passing the id parameter as 5. Let’s run the application and place a breakpoint in the Index() method of HomeController. We can see that this method is executed by default. This is because we have defined these as the default values in the route. Now let’s change the default route. For that, let’s add a new controller BooksController with an action method Details() and an optional parameter id:
      public class BooksController : Controller
      {
          public IActionResult Details(int id)
          {
              ViewBag.Id = id;
              return View();
          }
      }
      Then let’s create a view for the Details() action method:
      @{
          ViewData["Title"] = "Details";
          int id = ViewBag.Id;
      }
      
      <h1>Details</h1>
      
      Book Id : @id 
      
      Once these are in place, let’s change the default route in the startup class:
      app.UseMvc(routes =>
      {
          routes.MapRoute(
              name: "default",
              template: "{controller=Books}/{action=Details}/{id?}");
      });
      Now let’s run the application once again. We can see that the call goes to Details() method of the BooksController by default: modified default route In this section, we have learned how Conventional Routing works and how to configure the default route for an application. Now let's see how to configure multiple routes.

      Multiple Routes

      We can add multiple routes inside UseMvc() by calling MapRoute() multiple times. This allows us to define multiple conventions or to add routes that are dedicated to a specific action:
      app.UseMvc(routes =>
      {
         routes.MapRoute("blog", "blog/{*article}",
                  defaults: new { controller = "Blog", action = "Article" });
      
         routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
      });
      The blog route here is a dedicated conventional route. It uses the conventional routing system but is dedicated to a specific action. Since controller and action don't appear in the route template as parameters, they can only have the default values, and thus this route will always map to the action method BlogController.Article(). Routes in the route collection are ordered and will be processed in the order we add them. So in this example, MVC will try the blog route before the default route. It is always good to specify a route name while creating the routes. It gives the route a logical name so that we can use the named route for URL generation. So far, we have looked at how to configure the Conventional Routing. Next, let's learn about Attribute Routing.

      Attribute Routing

      By placing a route on the controller or the action method, we can make use of the Attribute Routing feature. Let’s modify the Configure() method in the startup.cs class and remove the default routes that we had defined earlier.
      app.UseMvc();
      Now let’s modify the BooksController by giving custom attributes as routes:
      public class BooksController : Controller
      {
          [Route("")]
          [Route("Home")]
          [Route("Home/Index")]
          public IActionResult Index()
          {
              return View();
          }
      
          [Route("Home/Details/{id:int}")]
          public IActionResult Details(int id)
          {
              ViewBag.Id = id;
              return View();
          }
      }
      We have specified the URL paths /, /Home, or /Home/Index for the BooksController.Index() action and /Home/Details/{id} for the Details() method. When using Attribute Routing, the controller name and action method name plays no role in selecting which action method to execute. We can also use Http[Verb] attributes for Attribute Routing:
      [HttpGet("/books")]
      public IActionResult ListBooks()
      {
         // ...
      }
      
      [HttpPost("/books")]
      public IActionResult CreateBook(...)
      {
         // ...
      }
      
      For the URL path /books, MVC will execute the ListBooks() action when the HTTP verb is GET and CreateBook() when the HTTP verb is POST. Route attributes defined on the controller are combined with route attributes on the individual action methods to form the complete URL. Any route templates defined on the controller are prepended to route templates on all of its action methods. When we place a route attribute on the controller, all actions in the controller use attribute routing. Attribute routes support token replacement by enclosing a token in square-braces ([, ]). The tokens [action], [area], and [controller] are replaced with the values of the action name, area name, and controller name from the action where the route is defined:
      [Route("[controller]/[action]")]
      public class BooksController : Controller
      {
          [HttpGet]
          public IActionResult List() 
          {
              // ...
          }
      
          [HttpGet("{id}")]
          public IActionResult Edit(int id) 
          {
              // ...
          }
      }
      The List() action method matches the URL /Books/List and Edit() method matches the URL /Books/Edit/{id}. In this section, we've learned how to configure Attribute Routing. Now let's look at how to specify multiple routes as attributes.

      Multiple Routes

      Attribute routing supports defining multiple routes that reach the same action. The most common usage of this is to achieve the functionality of the default conventional route:
      [Route("[controller]")]
      public class BooksController : Controller
      {
         [Route("")]     // Matches 'Books'
         [Route("Index")] // Matches 'Books/Index'
         public IActionResult Index()
      }
      
      By defining two routes, the Index() method matches the URL paths /Books and Books/Index. That's it, let's summarize what we've learned.

      Conclusion

      In this article we have learned the following topics:
      • Creating a Conventional Routing pattern for an ASP.NET Core MVC application
      • Configuring Attribute Routing for controllers and action methods
      • Setup multiple routes for both conventional and attribute routing
      In the next part of this series, we’ll look into file upload in ASP.NET Core MVC, so stay tuned. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48120 0 0 0
      Dependency Injection in ASP.NET Core MVC https://code-maze.com/dependency-injection-aspnetcore-mvc/ Mon, 08 Jul 2019 06:00:59 +0000 https://code-maze.com/?p=48347 In this article, we are going to discuss an important concept of ASP.NET Core MVC - Dependency Injection. If you’ve missed some of the previous articles in the series we recommend visiting the series page: ASP.NET Core MVC Series. To download this article’s source code visit: Dependency Injection in ASP.NET Core MVC. We have divided this article into the following sections: So, let's get down to it.

      Dependency Inversion and Dependency Injection

      Dependency Inversion is one of the core software design principles which we use to create modules that are loosely coupled. While creating applications, we can achieve this using a technique called Dependency Injection. Dependency Injection (DI) is the method of injecting the dependent modules into our classes. We have discussed this in detail in one of our other article Dependency Inversion Principle. There, we discussed the concept of Dependency Injection and how to implement it. So, in this section, we are going to look at the support for Dependency Injection in an ASP.NET Core MVC application.

      Injecting Dependencies into Controllers

      ASP.NET Core supports Dependency Injection(DI) between classes and their dependencies. MVC Controllers request dependencies explicitly via constructors. Furthermore, ASP.NET Core has built-in support for dependency injection, hence making the application easier to test and maintain. We add services as a constructor parameter and the runtime resolves the service from the service container. We typically define services using interfaces. When we implement a repository pattern in ASP.NET Core MVC application, we make use of Dependency Injection in our controllers. We have explained how to implement a simple data repository in the article section Implementing a simple data repository. Let’s create an ASP.NET Core MVC application and implement a simple data repository as described in the article. First of all, let’s create an interface IDataRepository:
      public interface IDataRepository<TEntity>
      {
          IEnumerable<TEntity> GetAll();
      
          void Add(Employee employee);
      }
      Then let’s create a class EmployeeManager implementing the IDataRepository interface:
      public class EmployeeManager : IDataRepository<Employee>
      {
          public void Add(Employee employee)
          {
              throw new NotImplementedException();
          }
      
          IEnumerable<Employee> IDataRepository<Employee>.GetAll()
          {
              return new List<Employee>() {
                  new Employee(){ }
              };
          }
      }
      Next step is to add the service to the service container. We need to do that in the ConfigureServices() method in the Startup.cs class:
      services.AddScoped<IDataRepository<Employee>, EmployeeManager>();
      By doing so, we have configured the repository using Dependency Injection. Next, let’s create the EmployeeController with the Index() action method to get the list of all employees:
      public class EmployeeController : Controller
      {
          private readonly IDataRepository<Employee> _dataRepository;
      
          public EmployeeController(IDataRepository<Employee> dataRepository)
          {
              _dataRepository = dataRepository;
          }
      
          public IActionResult Index()
          {
              IEnumerable<Employee> employees = _dataRepository.GetAll();
              return View(employees);
          }
      }
      Here, we first declare a _dataRepository variable of type IDataRepository<Employee>. Later, we inject it through the constructor. We can also inject a service directly into an action method without using constructor injection. We can use the [FromServices] attribute for that:
      public IActionResult Index([FromServices]  IDataRepository<Employee> _dataRepository)
      {
          IEnumerable<Employee> employees = _dataRepository.GetAll();
          return View(employees);
      }
      TheFromServices attribute specifies that an action parameter should be bound using the request services. Great. We have learned how to use Dependency Injection to provide dependencies into a Controller. Now, let’s look at how to Inject dependencies into Views.

      Injecting Dependencies into Views

      ASP.NET Core supports injecting dependencies into Views. 

      When to Inject Dependencies into Views

      Injecting services into views is a deviation from the MVC concept. But in some cases, we may have view-specific services which return the data that we use only for populating the view elements. An example is a service which gives a set of values that we need to display in a list control. In such scenarios, we can inject services directly into views. View injection can be useful to populate options in UI elements, such as dropdown lists. Consider a form to create a book that includes options for specifying a genre. Rendering the data using a standard MVC approach would require the controller to request data access services for this set of options and then populate a Model or ViewBag with the set of options to be bound. An alternative approach is to inject the services directly into a view. This approach minimizes the amount of code required by the controller as we move the view element construction logic into the view itself.

      How to Inject services into Views

      We can inject a service into a view using the @inject directive. @inject adds a property to our view and initialize it using DI:
      @inject <type> <name>
      Let’s create a controller action method to display a book creation form:
      public class BooksController : Controller
      {
          public IActionResult Create()
          {
              return View();
          }
      }
      Then let’s create a Book model class:
      public class Book
      {
          public int Id { get; set; }
      
          [Display(Name = "Book Title")]
          public string Title { get; set; }
      
          public string Genre { get; set; }
      
          [DataType(DataType.Currency)]
          [Range(1, 100)]
          public decimal Price { get; set; }
      
          [Display(Name = "Publish Date")]
          [DataType(DataType.Date)]
          public DateTime PublishDate { get; set; }
      }
      For the next step, let’s define a BooksLookupService for supplying the list data for Genres:
      public class BooksLookupService
      {
          public List<string> GetGenres()
          {
              return new List<string>()
              {
                  "Fiction",
                  "Thriller",
                  "Comedy",
                  "Autobiography"
              };
          }
      }
      Then let’s create a view and inject an instance of BooksLookupService into it:
      @model WorkingWithDI.Models.Book
      @inject WorkingWithDI.Models.Services.BooksLookupService BooksLookupService
      
      @{
          ViewData["Title"] = "Create";
          var genres = BooksLookupService.GetGenres();
      }
      
      <h1>Create</h1>
      
      <h4>Book</h4>
      <hr />
      <div class="row">
          <div class="col-md-4">
              <form asp-action="Create">
                  <div asp-validation-summary="ModelOnly" class="text-danger"></div>
      
                  <div class="form-group">
                      <label asp-for="Title" class="control-label"></label>
                      <input asp-for="Title" class="form-control" />
                      <span asp-validation-for="Title" class="text-danger"></span>
                  </div>
                  <div>
                      <label asp-for="Genre" class="control-label"></label>
                      <select asp-items="@(new SelectList(genres))" class="form-control" ></select>
                  </div>
                  <div class="form-group">
                      <label asp-for="Price" class="control-label"></label>
                      <input asp-for="Price" class="form-control" />
                      <span asp-validation-for="Price" class="text-danger"></span>
                  </div>
                  <div class="form-group">
                      <label asp-for="PublishDate" class="control-label"></label>
                      <input asp-for="PublishDate" class="form-control" />
                      <span asp-validation-for="PublishDate" class="text-danger"></span>
                  </div>
                  <div class="form-group">
                      <input type="submit" value="Create" class="btn btn-primary" />
                  </div>
              </form>
          </div>
      </div>
      
      <div>
          <a asp-action="Index">Back to List</a>
      </div>
      This will supply the list values into the view. As the last step, we need to register the types that we request through dependency injection in Startup.ConfigureServices(). If a type is unregistered, it throws a runtime exception.
      services.AddTransient<BooksLookupService>();
      That’s it. Now let’s run the application and navigate to Create Books form: create books screen We can see that the list of Genres is populated by getting the values from the BooksLookupService. Excellent, we have learned how to inject a dependency directly into the view.

      Conclusion

      In this article we have learned the following topics:
      • The principle of Dependency Inversion and how to achieve it using Dependency Injection
      • Injecting dependencies into Controllers
      • Injecting dependencies into Views of an ASP.NET Core MVC application
      In the next part of this series, we are going to learn about unit testing in ASP.NET Core MVC. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48347 0 0 0 1244 0 0 1245 1244 0
      File Upload in ASP.NET Core MVC https://code-maze.com/file-upload-aspnetcore-mvc/ Mon, 01 Jul 2019 06:00:44 +0000 https://code-maze.com/?p=48434 File Upload is the process of uploading files from the user’s system to the web application’s storage. ASP.NET Core MVC actions support uploading of one or more files using simple model binding. We have covered the file upload support in ASP.NET Core Web API in detail in the article Uploading Files With .NET Core Web API and Angular. There we looked at how to upload a file using an angular app on the client-side and an ASP.NET Core Web API on the server-side. In this article, we are going to look at how to achieve the same functionality in an ASP.NET Core MVC application. If you've missed some of the previous articles in the series we recommend visiting the series page: ASP.NET Core MVC Series. To download this article’s source code visit: File Upload in ASP.NET Core MVC. This article is divided into the following sections: Let's get down to it.

      Creating a File Input Control

      To upload files, let’s create a new ASP.NET Core MVC application, new FileUploadController and design an HTML form with a file input control for the Index action:
      <form method="post" enctype="multipart/form-data" asp-controller="FileUpload" asp-action="Index">
          <div class="form-group">
              <div class="col-md-10">
                  <p>Upload one or more files using this form:</p>
                  <input type="file" name="files" multiple />
              </div>
          </div>
          <div class="form-group">
              <div class="col-md-10">
                  <input type="submit" value="Upload" />
              </div>
          </div>
      </form>
      In order to support file uploads, we must specify the enctype as multipart/form-data. The enctype attribute specifies how the form data should be encoded when submitting it to the server. The enctype attribute can be used only if the form method is POST. The file input element supports uploading multiple files. By removing the multiple attribute on the input element, we can restrict it to support just a single file.

      The Role of Model Binding

      We can access the individual files uploaded to the application through Model Binding using the IFormFile interface. Model Binding in ASP.NET Core MVC maps data from HTTP requests to action method parameters. IFormFile represents a file that is sent with the HttpRequest and has the following structure:
      public interface IFormFile
      {
          string ContentType { get; }
          string ContentDisposition { get; }
          IHeaderDictionary Headers { get; }
          long Length { get; }
          string Name { get; }
          string FileName { get; }
          Stream OpenReadStream();
          void CopyTo(Stream target);
          Task CopyToAsync(Stream target, CancellationToken cancellationToken = null);
      }
      
      As a security consideration, We should never rely on or trust the FileName property without validation. When uploading files using model binding and the IFormFile interface, the action method can accept either a single IFormFile or an IEnumerable<IFormFile> representing multiple files. We can loop through one or more uploaded files, save them to the local file system and then use the files as per our application's logic:
      public class FileUploadController : Controller
      {
          ...
      
          [HttpPost("FileUpload")]
          public async Task<IActionResult> Index(List<IFormFile> files)
          {
              long size = files.Sum(f => f.Length);
                              
              var filePaths = new List<string>();
              foreach (var formFile in files)
              {
                  if (formFile.Length > 0)
                  {
                      // full path to file in temp location
                      var filePath = Path.GetTempFileName(); //we are using Temp file name just for the example. Add your own file path.
                      filePaths.Add(filePath);
      
                      using (var stream = new FileStream(filePath, FileMode.Create))
                      {
                          await formFile.CopyToAsync(stream);
                      }
                  }
              }
      
              // process uploaded files
              // Don't rely on or trust the FileName property without validation.
      
              return Ok(new { count = files.Count, size, filePaths });
          }
      }
      Let's place a breakpoint on the Index() method and run the application: upload files screen Once we choose files and click Upload, we can debug the code to see how files are uploaded to the server file system. Here we just return the total number and size of files uploaded along with file paths. Files uploaded using the IFormFile technique are buffered in memory or on disk on the web server before being processed. Inside the action method, the IFormFile contents are accessible as a stream. In addition to the local file system, files can be streamed to Azure Blob storage or Entity Framework. OK, that's it for the file upload, let's summarize what we've learned.

      Conclusion

      In this article we have learned the following topics:
      • Creating a file upload control in ASP.NET Core MVC Application
      • Utilize model binding to get the uploaded files
      • Read and copy files to stream
      In the next part of this series, we’ll look at Dependency Injection in ASP.NET Core MVC. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48434 0 0 0
      Filters in ASP.NET Core MVC https://code-maze.com/filters-in-asp-net-core-mvc/ Mon, 22 Jul 2019 06:00:56 +0000 https://code-maze.com/?p=48461 Filters in ASP.NET Core MVC allows us to run certain actions before or after specific stages in the request processing pipeline. There are some built-in filters in ASP.NET Core. We can also write custom filters to execute actions at various stages of the request pipeline. They also help us to handle cross-cutting concerns and avoid duplication of codes. We have discussed Filters in detail in the Implementing Action Filters in ASP.NET Core article. In that article, we have explained how to implement an Action Filter in an ASP.NET Core Web API application. In this article, we’ll explain the different Filter types available in ASP.NET Core MVC and how to implement each type. If you’ve missed some of the previous articles in the series we recommend visiting the series page: ASP.NET Core MVC Series. To download this article’s source code visit: Filters in ASP.NET Core MVC. We have divided the article into the following sections: First, let’s look at the different types of Filters available in ASP.NET Core MVC.

      Filter types

      Each filter type is executed at a different stage in the request pipeline. Authorization filters are run first and are used to determine whether the current user is authorized for the current request. They can short-circuit the pipeline if a request is unauthorized. Resource filters come right after the authorization. We can use it to implement caching or short-circuit the filter pipeline for performance reasons. Action filters can run code immediately before and after an individual action method is called. We can use it to manipulate the arguments passed into action and the result returned from the action. Razor Pages do not support Action filters. Exception filters are used to globally handle all unhandled exceptions that occur in the application. Result filters can run code immediately before and after the execution of individual action results. They run only when the action method has executed successfully. 

      Authorization Filter

      Authorization filters control access to action methods. They are the first filters to be executed within the filter pipeline. They have a before method called OnAuthorization(), but they don’t have an after method.

      Implementation

      Let’s implement an Authorization Filter. For that, we need to implement IAuthorizationFilter interface and the OnAuthorization() method inside it. In the filter, let’s define a field called _permission which is supplied via the constructor. We can also create a method CheckUserPermission() that performs the authorization logic.  For testing purposes, let’s assume that the hypothetical user always has just the Read permission and no Write permission. When the user is not authorized, we can set the Result property of the HTTP Context as UnauthorizedResult which will short circuit the execution pipeline. Let’s create the AuthorizeActionFilter class:
      public class AuthorizeActionFilter : IAuthorizationFilter
      {        
          private readonly string _permission;
      
          public AuthorizeActionFilter(string permission)
          {            
              _permission = permission;
          }
      
          public void OnAuthorization(AuthorizationFilterContext context)
          {
              bool isAuthorized = CheckUserPermission(context.HttpContext.User, _permission);
      
              if (!isAuthorized)
              {
                  context.Result = new UnauthorizedResult();
              }
          }
      
          private bool CheckUserPermission(ClaimsPrincipal user, string permission)
          {
              // Logic for checking the user permission goes here. 
                  
              // Let's assume this user has only read permission.
              return permission == "Read";            
          }
      }
      Now, let’s create an Attribute for the filter that we just created using the TypeFilterAttribute
      public class AuthorizeAttribute : TypeFilterAttribute
      {
          public AuthorizeAttribute(string permission)
              : base(typeof(AuthorizeActionFilter))
          {
              Arguments = new object[] { permission };
          }
      }
      We have the Authorize attribute ready for use now. Now let’s use it in our application. For testing this attribute, let’s create 3 controller actions - Index(), Read() and Edit(). Let’s leave Index() method without specifying the Authorize attribute. For the Read()  method, let’s specify Authorize attribute with Read permission. Let's specify the Authorize attribute with Write permission for the Edit() method:
      public IActionResult Index()
      {
          return View();
      }
      
      [Authorize("Read")]
      public IActionResult Read()
      {
          return View();
      }
      
      [Authorize("Write")]
      public IActionResult Edit()
      {
          return View();
      }

      Testing

      Remember that for testing purposes, we implemented the filter in such a way that only Read permission is available for all users. Let’s run the application and navigate to \home\index: home-index Since no authorization is required, it works fine. Now let’s navigate to \home\read: home-read This action method requires Read permission which is available for the user.  If we place a breakpoint in the OnAuthorized() method in the AuthorizationFilter, We can verify that it is not executed for the Index() method but is executed for the Read() method. Now let’s navigate to \home\edit: home-edit This action requires the Write permission which is not available for the user and hence it throws an HTTP ERROR 401 denoting unauthorized request. What we see here is the standard browser error page for HTTP 401 Response. We can also create custom error pages corresponding to various status codes in our application for better user experience. In this section, we have learned how to implement an Authorization Filter.

      Resource Filters 

      As we understand from the name, Resource filters can be used for handling resources and helps to short circuit the request execution pipeline if required. A common usage of this type of filter is the implementation of Caching. This can avoid the rest of the pipeline when a Cache hit happens.

      Implementation

      Let’s look at how we can implement caching using the resource filter. First, let’s define a class CacheResourceFilter implementing IResourceFilter interface. The Resource filter has a before method OnResourceExecuting() and an after method  OnResourceExecuted(). Let’s define a dictionary object _cache for holding the cached value and a string value _cacheKey for storing the Cache key:
      public class CacheResourceFilter : IResourceFilter
      {
          private static readonly Dictionary<string, object> _cache
                  = new Dictionary<string, object>();
          private string _cacheKey;
      
          public void OnResourceExecuting(ResourceExecutingContext context)
          {
              _cacheKey = context.HttpContext.Request.Path.ToString();
              if (_cache.ContainsKey(_cacheKey))
              {
                  var cachedValue = _cache[_cacheKey] as string;
                  if (cachedValue != null)
                  {
                      context.Result = new ContentResult()
                      { Content = cachedValue };
                  }
              }
          }
      
          public void OnResourceExecuted(ResourceExecutedContext context)
          {
              if (!String.IsNullOrEmpty(_cacheKey) && !_cache.ContainsKey(_cacheKey))
              {
                  var result = context.Result as ContentResult;
                  if (result != null)
                  {
                      _cache.Add(_cacheKey, result.Content);
                  }
              }
          }
      }
      In the OnResourceExecuting() method, we can check if the _cacheKey is present in the _cache dictionary. If it is present, we set the context.Result with the cached value. We can short-circuit the filter pipeline by setting the context.Result property.  In the OnResourceExecuting() method, we check if the _cacheKey is already present in the _cache. If not, we’ll insert the context.Result value into the _cache. Then, let’s create an attribute for this filter:
      public class CacheResourceAttribute : TypeFilterAttribute
      {
          public CacheResourceAttribute()
              : base(typeof(CacheResourceFilter))
          {
          }
      }
      Finally, let’s create a controller and an action method to return a text indicating the time at which the content was generated. Also, let’s decorate the controller with the CacheResource attribute we just created.
      [CacheResource]
      public class CachedController : Controller
      {
          public IActionResult Index()
          {
              return Content("This content was generated at " + DateTime.Now);
          }
      }

      Testing

       Now, let’s run the application and navigate to /cached: cached When we access this URL for the first time, we can see the content is generated with the current timestamp. Then, for all subsequent access to the same URL, we’ll get a cached version of the resource. We can verify this by checking the timestamp in the URL. Also, if we place a breakpoint in the controller action method, we can see that it is hit only in the first request. For all subsequent requests, we can see that we have short-circuited the execution pipeline by using the resource filter. Great! We have learned how to implement a Resource Filter.

      Action Filters

      Action filters run right before and after each action method is executed. We have discussed implementing action filter in detail in the article section: Action Filters Implementation. The linked article shows how to implement an action filter in an ASP.NET Core Web API application. We can follow the same steps for implementing action filters in an ASP.NET Core MVC application.

      Implementation

      Let’s create an action filter that handles invalid models. If the model is invalid, we are going to return a standard BadRequest response with a custom message. Our custom validation class needs to  inherit from the abstract ActionFilterAttribute class and override the OnActionExecuting() and OnActionExecuted() methods:  
      public class ValidateModelAttribute : ActionFilterAttribute
      {
          public override void OnActionExecuting(ActionExecutingContext context)
          {
              var param = context.ActionArguments.SingleOrDefault();
      
              if (param.Value == null)
              {
                  context.Result = new BadRequestObjectResult("Model is null");
                  return;
              }
      
              if (!context.ModelState.IsValid)
              {
                  context.Result = new BadRequestObjectResult(context.ModelState);
              }
          }
      
          public override void OnActionExecuted(ActionExecutedContext context)
          {
          }
      }
      The OnActionExecuting() runs before the action method and OnActionExecuted() after the action method. In the OnActionExecuting() method, we can check if the Model is null or ModelState is invalid. In both these cases, we can return a BadRequest response. This way we can handle this across the application instead of having to write this code in each of the action methods. Now let’s use this filter in an action method:
      [ValidateModel]
      [HttpPost]
      public IActionResult Create([FromForm]Book book)
      {
          return View();
      }

      Testing

      If we call this action method without supplying a valid Book model, we can see the standard BadRequest response with the custom message that we have given: model-validate-filter That’s it! In this section, we have looked at how to implement an action filter.

      Exception Filters

      Exception Filters are used to handle any unhandled exceptions that occur in our application. They do not have before or after methods. They just implement the OnException() method. This method will be called whenever an unhandled exception occurs in our application.

      Implementation

      To see the exception filter in action, let’s create an action method that generates an unhandled exception:
      public IActionResult GenerateError()
      {
          throw new NotImplementedException();
      }
      Now, let’s create a custom exception filter by implementing the IExceptionFilter interface. We can provide the required exception handling behavior in the OnException() method:
      public class CustomExceptionFilter : IExceptionFilter
      {
          private readonly IModelMetadataProvider _modelMetadataProvider;
      
          public CustomExceptionFilter(
              IModelMetadataProvider modelMetadataProvider)
          {
              _modelMetadataProvider = modelMetadataProvider;
          }
      
          public void OnException(ExceptionContext context)
          {
              var result = new ViewResult { ViewName = "CustomError" };
              result.ViewData = new ViewDataDictionary(_modelMetadataProvider, context.ModelState);
              result.ViewData.Add("Exception", context.Exception);
      
              // Here we can pass additional detailed data via ViewData
              context.ExceptionHandled = true; // mark exception as handled
              context.Result = result;
          }
      }
      In the OnException() method, we can set the ViewResult as CustomError . Then we can pass the exception details into the view as ViewData. After that, we'll create the CustomError view and display the error message in a user-friendly manner:
      @{
          ViewData["Title"] = "CustomError";
          var exception = ViewData["Exception"] as Exception;
      }
      
      <h1>An Error has Occurred</h1>
      
      <p>@exception.Message</p>

      Testing

      Now, let’s run the application and navigate to \home\generateerror: custom-error We can see that a custom error page is displayed with details of the exception which we can customize to show only the required information to the user. This way, we can give the user a much better experience and we can handle exceptions across the application in a consistent manner.

      Exception Handling - Middlewares vs Filters

      Additionally, we can use middleware for handling unhandled exceptions. So, when should we use an exception handling middleware and when should we go for an exception filter? If we are concerned about errors that may occur outside of the MVC context or our code, for example, we may want to capture an error that occurs inside a middleware or a filter, then we'll have to go for an exception handling middleware. On the other hand, if we want to get the MVC context during exception handling and perform some action based on that, then we'll have to use an exception filter. In this section, we have learned how to implement an exception filter in our application.

      Result Filters

      We can use Result filters to run code before or after the execution of controller action results. They are executed only if the controller action method is executed successfully. We can write logic to surround the view or to apply some customizations to all the action results in our application. Let’s say we want to add a particular value to the header of all the action results in our application.  We are going to take a look at how to implement this using a result filter. First, let’s create a class AddHeaderFilter that implements the IResultFilter interface. Result filter has a before method called OnResultExecuting() and an after method called OnResultExecuted() In the OnResultExecuting() method, let’s add a custom response header:
      public class AddHeaderFilter : IResultFilter
      {
          public void OnResultExecuting(ResultExecutingContext context)
          {
              context.HttpContext.Response.Headers.Add(
                  "OnResultExecuting",
                  "This header was added by result filter.");
          }
      
          public void OnResultExecuted(ResultExecutedContext context)
          {
          }
      }
      After that, let’s create an attribute for the result filter that we just created:
      public class AddHeaderAttribute : TypeFilterAttribute
      {
          public AddHeaderAttribute() : base(typeof(AddHeaderFilter))
          {
          }
      }
      Finally, let’s create an action method in the HomeController and apply the AddHeader result filter attribute.
      [AddHeader]
      public IActionResult TestResultFilter()
      {
          return View();
      }
      Let’s run the application and navigate to home\testresultfilter. On inspecting the headers, we can see that the custom header is present in the response: result-filter This way we can use result filter to customize the action results across the application. Great! In this section, we have learned how to implement a result filter.

      Conclusion

      In this article we have learned the following topics:
      • The different types of filters available in ASP.NET Core MVC.
      • Creating a custom Authorization Filter.
      • Implementing Caching using Resource Filter.
      • Using Exception filter to handle unhandled exceptions.
      • Adding a custom response header to all our action methods using Resource Filter.
      With that, we come to the end of the ASP.NET Core MVC series. Hope you enjoyed reading through and had some good learning in the process! [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48461 0 0 0 1338 [FromBody]Book book), if I change to FromForm the view works fine when the action takes place on Create button and displays {"Id":["The value '' is invalid."]}, but with Postman test, the value of variable param inside OnActionExecuting method is not null. Again, great job. Regards, Tam]]> 0 0 1339 https://code-maze.com/ 1338 0 1341 https://code-maze.com/ 1338 0 1343 0 0 1344 https://code-maze.com/ 1343 0 1345 0 0 1346 https://code-maze.com/ 1345 0 1347 0 0 1348 https://code-maze.com/ 1347 0
      Unit Testing in ASP.NET Core MVC https://code-maze.com/unit-testing-in-asp-net-core-mvc/ Mon, 15 Jul 2019 06:00:29 +0000 https://code-maze.com/?p=48490 ASP.NET Core MVC Series. To download this article’s source code visit: Unit Testing in ASP.NET Core MVC. We have divided this article into the following sections: Let's start.

      Why do We Need Unit Tests

      Our applications can fail in unexpected ways in response to changes. Hence, automatic testing is required after making changes for all applications. Manual testing is the slowest, least reliable, most expensive way to test an application. But if applications aren't designed to be testable, manual testing might be the only way available to us. So the first step is to make sure that the application is designed to be testable. Applications that follow good architectural principles like Separation of Concerns, Dependency Inversion, Single Responsibility, Don't Repeat Yourself (DRY), etc are easily testable. ASP.NET Core supports automated unit, integration, and functional testing. We have explained Unit testing in ASP.NET Core Web API in detail in one of our previous articles. There, we have explained how to implement unit testing in an ASP.NET Core Web API project. However, in this article, we’ll focus on how to write unit tests for an ASP.NET Core MVC project. A unit test is meant to test a single part of our application's logic. Our code may have dependencies on lower layers and external components. But these are not validated by unit tests. It runs completely in memory and in process. It doesn't communicate with the file system, the network, or a database. Unit tests should only test our code. Due to these reasons, unit tests should execute extremely quickly and we should be able to run them frequently. Ideally, we should run unit tests before each change is pushed to the source control repository and also with every automated build on the build server.

      Setting Up the Unit Testing Framework

      There are many test frameworks available in the market today. However, for this article, we are going to use the xUnit, which is a very popular testing framework. Even the ASP.NET Core and EF Core tests are written using it. We can add a xUnit test project in Visual Studio using the xUnit Test Project template that's available in the visual studio: add new unit test project We should always name our tests in a consistent fashion, with names that indicate what each test does. One good approach is to name test classes and methods according to the class and method which they are testing. It makes it extremely clear what each test is responsible for. We can add the behavior that is being tested to each test method name. This should include the expected behavior and any inputs or assumptions that should yield this behavior. Thus, when one or more tests fail, it's obvious from their names what cases have failed. We will follow this naming convention when we create unit tests in the next section. Let’s add a NuGet package reference for Moq, which is a mock object framework. This will provide us mocked objects, which are fabricated objects with a predetermined set of property and method behaviors used for testing.

      Unit Tests in Controller Methods

      Let’s say we have defined an EmployeeController with an Index() and Add() methods:
      public class EmployeeController : Controller
      {
          private readonly IDataRepository<Employee> _dataRepository;
      
          public EmployeeController(IDataRepository<Employee> dataRepository)
          {
              _dataRepository = dataRepository;
          }
      
          public IActionResult Index()
          {
              IEnumerable<Employee> employees = _dataRepository.GetAll();
              return View(employees);
          }
      
          [HttpPost]
          public IActionResult Add(Employee employee)
          {
              if (!ModelState.IsValid)
              {
                  return BadRequest(ModelState);
              }
      
              _dataRepository.Add(employee);
              return RedirectToAction(actionName: nameof(Index));
          }
      }
      Now let’s look at how to write unit tests for these action methods. Our controller uses Dependency Injection to get the value for _dataRepository. This makes it possible for the unit testing framework to supply a mock object and test the methods. The AAA (Arrange, Act, Assert) pattern is a common way of writing unit tests and we will follow the same pattern here. Arrange section of a unit test method initializes objects and sets the value for the inputs that are passed to the method under test. The Act section invokes the method under test with the arranged parameters. Assert section verifies that the action of the method under test behaves as expected.

      Testing the Index() method

      Now let’s write tests for the Index() method:
      [Fact]
      public void Index_ReturnsAViewResult_WithAListOfEmployees()
      {
          // Arrange
          var mockRepo = new Mock<IDataRepository<Employee>>();
          mockRepo.Setup(repo => repo.GetAll())
              .Returns(GetTestEmployees());
          var controller = new EmployeeController(mockRepo.Object);
      
          // Act
          var result = controller.Index();
      
          // Assert
          var viewResult = Assert.IsType<ViewResult>(result);
          var model = Assert.IsAssignableFrom<List<Employee>>(
              viewResult.ViewData.Model);
          Assert.Equal(2, model.Count());
      }
      
      private IEnumerable<Employee> GetTestEmployees()
      {
          return new List<Employee>()
          {
              new Employee()
              {
                  Id = 1,
                  Name = "John"
              },
              new Employee()
              {
                  Id = 2,
                  Name = "Doe"
              }
          };
      }
      We first mock the IDataRepository<Employee> service using the GetTestEmployees() method. GetTestEmployees() creates and returns a list of two mock Employee objects. Then the Index() method is executed and we assert the following on the result:
      1. It returns a ViewResult.
      2. The type of returned model is List<Employee>.
      3. There are two Employee objects in the returned Model.
      Each test method is decorated by a [Fact] attribute which indicates that it is a fact method that should be run by the test runner.

      Testing the Add() method

      Now, let’s write tests for the Add() method:
      [Fact]
      public void Add_ReturnsBadRequestResult_WhenModelStateIsInvalid()
      {
          // Arrange
          var mockRepo = new Mock<IDataRepository<Employee>>();
      
          var controller = new EmployeeController(mockRepo.Object);
          controller.ModelState.AddModelError("Name", "Required");
          var newEmployee = GetTestEmployee();
      
          // Act
          var result = controller.Add(newEmployee);
      
          // Assert
          var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
          Assert.IsType<SerializableError>(badRequestResult.Value);
      }
      
      [Fact]
      public void Add_AddsEmployeeAndReturnsARedirect_WhenModelStateIsValid()
      {
          // Arrange
          var mockRepo = new Mock<IDataRepository<Employee>>();
          mockRepo.Setup(repo => repo.Add(It.IsAny<Employee>()))
              .Verifiable();
          var controller = new EmployeeController(mockRepo.Object);
          var newEmployee = GetTestEmployee();
      
          // Act
          var result = controller.Add(newEmployee);
      
          // Assert
          var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
          Assert.Null(redirectToActionResult.ControllerName);
          Assert.Equal("Index", redirectToActionResult.ActionName);
          mockRepo.Verify();
      }
      
      private Employee GetTestEmployee()
      {
          return new Employee()
          {
              Id = 1,
              Name = "John"
          };
      }
      The first test verifies that when ModelState.IsValid is false, the action method returns a 400 Bad Request ViewResult with the appropriate data. We can test for an invalid model state by adding errors using AddModelError() method. The second test verifies that when ModelState.IsValid is true, the Add() method on the repository is called and a RedirectToActionResult is returned with the correct arguments. Mocked methods which we don't call are normally ignored, but by calling Verifiable() along with the setup we can validate that the mocked methods are called. We can validate this using mockRepo.Verify(), which fails the test if the expected method wasn't called.

      Running the Tests

      We can run the tests using the Test Explorer in Visual Studio: test explorer - Unit Tests In the Test Explorer, we can see all the available tests grouped by Solution, Project, Class, etc. We can select and execute the tests by either running or debugging those. Once we run the tests, we can see either a green tick mark or a red cross mark towards the left side of the test method name indicating success or failure on the last run. Towards the right, we can see the time taken to execute each test. That's it. Let's summarize what we've learned so far.

      Conclusion

      In this article we have learned the following topics:
      • Why we need unit tests in our ASP.NET Core MVC app
      • How to set it up
      • How to test the controller methods 
      In the next part of this series, we are going to learn about filters in ASP.NET Core MVC. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48490 0 0 0
      Getting Started with Entity Framework Core in ASP.NET Core – Models, DbContext, Configuration https://code-maze.com/getting-started-with-efcore/ Mon, 29 Jul 2019 06:02:51 +0000 https://code-maze.com/?p=48543 ASP.NET Core. We are going to show you how to create model classes and how to create and prepare a context class. Additionally, we are going to talk about the DbContext class in detail and show you how it helps with the database modeling process. Overall we are going to go through a variety of basic but crucial information that will be valuable to you when starting with an ASP.NET Core project that uses EF Core as ORM (object relational mapper). You can download the source code for this article on our GitHub repository. We strongly recommend you visit the main page for this tutorial which contains all the basic instructions and complete navigation. This post is divided into several parts:

      Entities (Models) – The Classes That Map to the Database

      Before we start talking about model classes, we need to create an ASP.NET Core project for the model classes to reside in. So, we are going to create an ASP.NET Core Web API project by using ASP.NET Core version 3.1: Creating ASP.NET Core App - EF Core We have to install Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.SqlServer libraries. You can do that by using the NuGet Manager or with the Package Manager Console window. The model (entity) class is a class that Entity Framework Core uses for mapping to the database table. So, let’s create our first entity and then explain how EF Core creates a table from the created class. Let’s create a folder Entities and inside a class named Student:
      public class Student
      {
          public Guid StudentId { get; set; }
          public string Name { get; set; }
          public int Age { get; set; }
      }
      There are a couple of rules that help EF Core to map this class to the database table and we are going to explain the whole process. First of all, by using the name of our class, EF Core has the information which table to use for the columns and configuration mappings (as soon as we create the context class it will become much easier to understand how EF Core knows that). Next, all the public properties from this class are going to be mapped to the table’s columns with the same names. Finally, EF Core uses a naming convention to create a primary key, from the StudentId property, in the database table (Later on, in the Configuring Nonerelational Properties section, we are going to see how).

      Providing the ConnectionString for EF Core in ASP.NET Core

      Creating a model class or model classes is just one part of a puzzle. In order for EF Core to have the required information about the database to work with, we need to provide a connection string by modifying the appsettings.json file. Placing a connection string into the appsettings.json file is a common practice (and we are going to use the same practice in this article) because we can use different appsettings files for different environments:
      • Development.json – Holds the settings related to a development environment
      • Production.json – Holds the settings related to a production environment
      • appsettings.json -  Holds the settings that are common to both environments
      But, we must state that using appsettings files to store connection string (or other sensitive data) isn’t the best practice, especially for the production environment. In such a case, the better way is to use environment variables. Because we are not going to publish our app to the production environment, we are going to modify the appsettings.json file:
      {
        "Logging": {
          "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
          }
        },
        "ConnectionStrings": {
          "sqlConnection": "server=.; database=CodeMaze; Integrated Security=true" 
        },
        "AllowedHosts": "*"
      }
      
      As you can see, we are providing the database server name, the name of the database itself and the authorization information. Of course, you need to provide a valid name for the server part. That is it, we can continue towards the context class.

      DbContext Class and its registration in ASP.NET Core

      The context class is another important part of the application. This class must inherit from the DbContext base class which contains information and configuration for accessing the database. So, let’s create our context class named ApplicationContext in the Entities folder:
      public class ApplicationContext : DbContext
      {
          public ApplicationContext(DbContextOptions options)
              :base(options)
          {
          }
      
          public DbSet<Student> Students { get; set; }
      }
      
      Additional options are sent to the base DbContext class through the ApplicationContext constructor by using DbContextOptions parameter. Finally, we see the Students property of type DbSet<Student> and this is quite an important part. EF Core looks for all the public DbSet properties, inside the application’s context class, and then maps their names to the names of the tables in the database. Then it goes inside a class which is provided in the DbSet<T> property (in our case it is a Student class) and maps all the public properties to the table columns in the table with the same names and types (StudentId, Name, and Age). If our Student class has any references towards other classes (right now it doesn’t but we will create relationships in the following articles), EF Core would use those reference properties and create relationships in the database.

      Registering a Context Class in the ASP.NET Core’s IOC

      After we have finished with creating the ApplicationContext class, we can proceed towards its registration. To do that, we are going to open the Startup.cs class and modify the ConfigureServices method:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext<ApplicationContext>(opts =>
              opts.UseSqlServer(Configuration.GetConnectionString("sqlConnection")));
      
          services.AddControllers();
      }
      
      We are using the AddDbContext extension method to register our ApplicationContext class into the IOC container. Inside the UseSqlSrver method we are providing the connection string to our context class and we can provide additional options as well (when it comes to that). [learn_more caption="About AddDbContextPool" state="open"] Instead of the AddDbContext method, we can use the AddDbContextPool method. We can use either the first or the second method, but with the second method, we enable DbContext pooling. This will not create a new instance every time but will check first if there are available instances in the pool and if there are, it will use one of those.[/learn_more] Right now our context class is ready to be used with the Dependency Injection (DI) inside our application. So let's add the Values (API empty) controller and modify it:
      public class ValuesController : ControllerBase
      {
          private readonly ApplicationContext _context;
      
          public ValuesController(ApplicationContext context)
          {
              _context = context;
          }
      
          [HttpGet]
          public IActionResult Get()
          {
              //Logic with the injected _context object.
          }
      }
      
      We can see that we are injecting our context class inside the controller’s constructor, which is the usual way for DI. To learn more about DI and IOC, and service lifetime overall, you can read the Configuring Services in ASP.NET Core part of our article.

      Diving Deeper into the DbContext Class

      Our ApplicationContext class currently accepts one parameter of type DbContextOptions inside a constructor. But we can provide the generic version of the DbContextOptions parameter as well:
      public ApplicationContext(DbContextOptions<ApplicationContext> options)
          :base(options)
      {
      }
      
      Whether we use the generic or non-generic version of the DbContextOptions parameter, our application is going to work the same. The main difference is that the non-generic version is not recommended if our application has multiple context types, which is not the case right now. If we navigate to the DbContext’s definition, we are going to see that it has three properties inside:
      • Database – This property is responsible for the Transactions, Database Migrations/Creations and Raw SQL queries (we are going to talk about all of these in the following articles)
      • ChangeTracker – This property is used to track states of the entities retrieved via the same context instance (this will be covered as well in the following articles)
      • Model – This property provides access to the database model that EF Core uses when connecting or creating a database.
      We can use the Model property to access the information for each entity and its properties. But before we do that, let's install the required Microsoft.EntityFrameworkCore.Relational package. After the installation, we can modify our Get action:
      var entity = _context.Model
          .FindEntityType(typeof(Student).FullName);
      
      var tableName = entity.GetTableName();
      var schemaName = entity.GetSchema();
      var key = entity.FindPrimaryKey();
      var properties = entity.GetProperties();
      
      These are just a few examples of using the Model property, but still, we can see its purpose.

      Conclusion

      Excellent. We’ve successfully integrated Entity Framework Core inside our ASP.NET Core application and we can move on towards all the different features it provides us. To sum up, we have learned:
      • What the entity (model) class in EF Core represents
      • How to provide connection string for our application
      • How to create the context class and why it is of such importance to us
      • Some additional information about the DbContext class
      In the next article, we are going to talk about Configuring Nonrelational Properties in EF Core. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48543 0 0 0 1332 0 0 1333 1332 0 1357 http://anit.me 0 0 1359 1357 0 1360 http://anit.me 1359 0 1439 0 0 1440 1439 3 1619 0 0 1620 var tableName = entity.GetTableName(); var schemaName = entity.GetSchema(); var key = entity.FindPrimaryKey(); var properties = entity.GetProperties(); Thank you again for the questions, I will add additional notes in the article. If you continue reading, you will find that I have already added some notes regarding the Migration article. Best regards.]]> 1619 0
      Configuring Nonrelational Properties in EF Core https://code-maze.com/configuring-nonrelational-properties/ Mon, 05 Aug 2019 06:00:33 +0000 https://code-maze.com/?p=48558 the previous article, we have created a basic setup for Entity Framework Core in our ASP.NET Core project. In this article, we are going to talk about the EF Core configuration and the different configuration approaches. You can download the source code for this article on our GitHub repository. To see all the basic instructions and complete navigation for this series, visit Entity Framework Core with ASP.NET Core Tutorial. There are three approaches to configuring Entity Framework Core:
      • By Convention
      • Data Annotations
      • Fluent API
      We are going to work with migrations in the next part of this series, but for the clarity of configuration examples, we are going to show you configuration results in a database, as if we’ve already executed migrations. This post is divided into several parts:

      EF Core Configuration By Convention

      Configuration by convention means that EF Core will follow a set of simple rules on property types and names and configure the database accordingly. This approach can be overridden by using Data Annotations or Fluent API approach. As we already mentioned, EF Core configures the primary key field from our Student model class by following the naming convention. This means that property is going to be translated as a primary key in the database if it has an “Id” property name or a combination <Name of a class> + Id (as it is a case with our StudentId property): Primary Key Convention - EF Core Configuration If we have a composite key in our class, we can’t use the configuration by Convention. Instead, we have to use either Data Annotations or Fluent API. When using configuration by convention, the nullability of a column is based on the property type from our model class. When EF Core uses configuration by convention it will go through all the public properties and map them by their name and type. So, this means that the Name property can have a Null as a value (because the default value for a string type is null) but the Age cannot (because it is a value type): Convetion type config - EF Core Configuration Of course, if we want the Age property to be nullable in a database, we can use the “?” suffix like so (int? Age) or we can use generic Nullable<T> like so (Nullable<int> Age): Convention nullability

      EF Core Configuration via Data Annotations

      Data Annotations are specific .NET attributes which we use to validate and configure the database features. There are two relevant namespaces from which we can get the annotation attributes: System.ComponentModel.DataAnnotations and System.ComponentModel.DataAnnotations.Schema. Attributes, from the first namespace, are mostly related to the property validation, while the attributes from the second namespace are related to the database configuration. So, let’s see the Data Annotation configuration in action. Let’s start by modifying the Student class, by adding some validation attributes:
      public class Student
      {
          public Guid StudentId { get; set; }
      
          [Required]
          [MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
          public string Name { get; set; }
      
          public int Age { get; set; }
      }
      
      With the Required attribute, we are stating that the Name field can’t be nullable and with the MaxLengh property, we are limiting the length of that column in a database. So, from this example, we can see how Data Annotations override configuration by Convention. Let’s see the result: Data Annotation Validation We can add additional modifications to the Student class, by adding the [Table] and [Column] attributes:
      [Table("Student")]
      public class Student
      {
          [Column("StudentId")]
          public Guid Id { get; set; }
      
          [Required]
          [MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
          public string Name { get; set; }
      
          public int? Age { get; set; }
      }
      

      Table attribute

      As you can see, by using the [Table] attribute, we are directing EF Core to the right table or schema to map to in the database. Right now, the name of the table in the database is Students because the DbSet<T> property is named Students in the ApplicationContext class. But the [Table] attribute is going to override that. So, if for any reason we need to change a class name, the [Table] attribute would prove to be quite useful. If a table, that we are mapping to, belongs to the non-default schema, we can use the [Table(“TableName”, Schema=”SchemaName”)] attribute, to provide the information about the required schema.

      Column attribute

      The [Column] attribute provides EF Core with the information about what column to map to in the database. So, if we want to have the Id property instead of the StudentId in our class, but in the database, we still want to have StudentId name for our column the [Column] attribute helps us with that. We can also provide the Order and the Database Type of the column with this attribute [Column(“ColumnName”, Order = 1, TypeName=”nvarchar(50)”)]. After these changes in our class, our table is going to have the same key field but a different name: Data Annotation Configuration

      Using the Fluent API Approach

      The Fluent API is a set of methods that we can use on the ModelBuilder class, which is available in the OnModelCreating method in our context (ApplicationContext) class. This approach provides a great variety of the EF Core configuration options that we can use while configuring our entities. So, let’s create the OnModelCreating method in the ApplicationContext class and add the same configuration as we did with the Data Annotations approach:
      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
          modelBuilder.Entity<Student>()
              .ToTable("Student");
          modelBuilder.Entity<Student>()
              .Property(s => s.Id)
              .HasColumnName("StudentId");
          modelBuilder.Entity<Student>()
              .Property(s => s.Name)
              .IsRequired()
              .HasMaxLength(50);
          modelBuilder.Entity<Student>()
              .Property(s => s.Age)
              .IsRequired(false);
      }
      
      In the beginning, we are selecting the entity to configure and with the Property method, we are specifying which property we want to add the constraint on. All the other methods are pretty self-explanatory. OnModelCreating is called the first time our application instantiates the ApplicationContext class. At that moment all three approaches are applied. As you can see, we haven’t used any method for the primary key, but our table has it nevertheless due to the naming convention: Fluent API EF Core Configuration

      Excluding Entities or Classes from the Mapping

      We have been talking about how Entity Framework Core maps all the properties into the table columns. But sometimes we might have some properties that we need in our class but we don’t want it as a column inside a table. Let’s see how to do that with the Data Annotations approach:
      public class Student
      {
          [Column("StudentId")]
          public Guid Id { get; set; }
      
          [Required]
          [MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
          public string Name { get; set; }
      
          public int? Age { get; set; }
      
          [NotMapped]
          public int LocalCalculation { get; set; }
      }
      
      The [NotMapped] attribute allows us to exclude certain properties from the mapping and thus avoid creating that column in a table. We can exclude a class as well if we need to:
      [NotMapped]
      public class NotMappedClass
      {
          //properties
      }
      
      Of course, we can do the same thing via the Fluent API approach:
      modelBuilder.Entity<Student>()
          .Ignore(s => s.LocalCalculation);
      
      modelBuilder.Ignore<NotMappedClass>();
      
      As you can see, if we are ignoring a property, then the Ignore method is chained directly to the Entity method, not on the Property method, as we did in a previous configuration. But if we are ignoring a class, then the Ignore method is called with the modelBuilder object itself.

      PrimaryKey Configuration with Data Annotations and Fluent API

      We have seen, in one of the previous sections, that EF Core automatically sets the primary key in the database by using the naming convention. But the naming convention won’t work if the name of the property doesn’t fit the naming convention or if we have a composite key in our entity. In these situations, we have to use either the Data Annotations approach or the Fluent API. So, to configure a PrimaryKey property via the Data Annotations approach, we have to use the [Key] attribute:
      public class Student
      {
          [Key]
          [Column("StudentId")]
          public Guid Id { get; set; }
      
          [Required]
          [MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
          public string Name { get; set; }
      
          public int? Age { get; set; }
      
          [NotMapped]
          public int LocalCalculation { get; set; }
      }
      
      If we want to use the Fluent API approach, we have to use the HasKey method:
      modelBuilder.Entity<Student>()
          .HasKey(s => s.Id);
      
      For the composite key, we have to use only the Fluent API approach because EF Core doesn’t support the Data Annotations approach for that. Let’s add an additional property to the Student class, just for the example sake:
      public Guid AnotherKeyProperty { get; set; }
      Now, we can configure the composite key:
      modelBuilder.Entity<Student>()
           .HasKey(s => new { s.Id, s.AnotherKeyProperty });
      
      So, we are using the same HasKey method, but we create an anonymous object that holds both key properties. This is the result: Composite kyes in Fluent API

      Working with Indexes and Default Values

      We are going to use the Fluent API approach to add indexes to the tables because it is the only supported way. To add an index to the required property, we can use a statement like this:
      modelBuilder.Entity<Student>()
          .HasIndex(s => s.PropertyName);
      
      For the multiple columns affected by index:
      modelBuilder.Entity<Student>()
          .HasIndex(s => new { s.PropertyName1, s.PropertyName2 });
      
      If we want to use a named index:
      modelBuilder.Entity<Student>()
          .HasIndex(s => s.PropertyName)
          .HasName("index_name");
      
      Adding unique constraint will ensure that a column has only unique values:
      modelBuilder.Entity<Student>()
          .HasIndex(s => s.PropertyName)
          .HasName("index_name")
          .IsUnique();
      
      Additionally, we can configure our properties to have the default values whenever a new row is created. To show how this feature works, we are going to add an additional property to the Student class:
      public bool IsRegularStudent { get; set; }
      Now, we can configure its default value via the Fluent API:
      modelBuilder.Entity<Student>()
          .Property(s => s.IsRegularStudent)
          .HasDefaultValue(true);
      
      This should be the result: Default Constraint in Fluent API

      Recommendations for Using EF Core’s Different Configuration Approaches

      Entity Framework Core does an awesome job in configuring our database by using the rules that we provide. Since now we know three different configuration approaches it can get a bit confusing which one to use. So, here are some recommendations.

      By Convention

      We should always start with the configuration by Convention. So, having the same class name as the table name, having a name for the primary key property that matches the naming convention and having the properties with the same name and type as the columns, should be our first choice. It is quite easy to prepare this type of configuration and it is time-saving as well.

      Data Annotations

      For the validation configuration, such as required or max length validation, you should always prefer the Data Annotations over Fluent API approach. And here’s why:
      • It is easy to see which validation rule is related to which property because it is placed right above the property and it is quite self-explanatory
      • Validations via Data Annotations can be used on the front end because as we’ve seen in the Student class, we can configure the error messages if validation fails
      • We want to use these validation rules prior to the EF Core’s SaveChanges method (we will talk about it in the following articles). This approach is going to make our validation code much simpler and easier to maintain

      Fluent API

      Let’s just say that we should use this approach for everything else. Of course, we must use this approach for the configuration that we can’t do otherwise or when we want to hide the configuration setup from the model class. So, indexes, composite keys, relationships are the things we should keep in the OnModelCreating method.

      Conclusion

      So, we have covered different configuration features that EF Core provides us with. Of course, there are additional configuration options related to Data Annotations and Fluent API, and the series is far from over, so we’re going to mention a few of them later on. In the next part of the series, we are going to learn about Migrations in EF Core and the Migration features provided by EF Core. So, stay tuned. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48558 0 0 0
      How to Configure PostgreSQL in Entity Framework Core https://code-maze.com/configure-postgresql-ef-core/ Mon, 09 Sep 2019 06:00:10 +0000 https://code-maze.com/?p=48586 PostgreSQL, a popular and reliable open-source relational database, in our .NET Core application, and connect it to Entity Framework Core to utilize its full potential. If you want to learn more about Entity Framework Core and how to configure it properly, check out our EF Core Series. You can download the source code for this article on our GitHub repository. This article will cover: Let's get down to it.

      Installing PostgreSQL Server and pgAdmin

      First off, we need to install our PostgreSQL Server and client. Download the appropriate version of PostgreSQL for your OS and select the newest version available. During the installation, you will be prompted to select pgAdmin (client). There are several good clients for PostgreSQL, but pgAdmin is the standard one, and a good one to begin with. Once you get used to it, you can try out other ones. You will also be prompted for superuser (postgresql) password. This password is the master password and you should make sure to remember it or write it down. You can leave the port on 5432 which is the default PostgreSQL port to avoid confusion. (You can choose another one if you already have PostgreSQL server on your machine) Finish the wizard and we're ready to start.

      Installing the Required Dependencies for PostgreSQL

      To enable PostgreSQL support in our Web API application, we need to to install the dependencies for PostgreSQL in our application first: We can do that by using the Package Manager Console: Install-Package Npgsql.EntityFrameworkCore.PostgreSQL -Version 2.2.4 Or by using the dotnet CLI: dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL --version 2.2.4 We are going to hardcode it to the version 2.2.4 since that is the newest version right now. Npgsql is the Entity Framework Core PostgreSQL provider. And that's all we need in regards to external dependencies for this project. Everything else we've already got out of the box. Let's see how hard it is to configure PostgreSQL in our Web API app.

      Configuring the PostgreSQL Provider and Connection String

      To configure our application and play around a bit with PostgreSQL, we need to add some code to the Startup.cs first. Let's modify our ConfigureServices() method:
      public void ConfigureServices(IServiceCollection services)
      {
      	services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
      
      	var connectionString = Configuration["PostgreSql:ConnectionString"];
      	var dbPassword = Configuration["PostgreSql:DbPassword"];
      
      	var builder = new NpgsqlConnectionStringBuilder(connectionString)
      	{
      		Password = dbPassword
      	};
      
      	services.AddDbContext<ApplicationContext>(options => options.UseNpgsql(builder.ConnectionString));
      }
      Aside from the AddMvc() method, everything else is what we need to configure our database. We are populating connection string and password from the configuration file. Don't do this for real-world projects, use secrets mechanism and environment variables instead. Then, we're using the NpgsqlConnectionStringBuilder to build our PostgreSQL connection string, and finally, connecting ApplicationContext to the database using that connection string. ApplicationContext is a simple DbContext class:
      public class ApplicationContext : DbContext
      {
      	public ApplicationContext(DbContextOptions options)
      			: base(options)
      	{
      	}
      
      	public DbSet<Student> Students { get; set; }
      }
      And we have one DbSet defined of the type Student.
      public class Student
      {
      	public Guid Id { get; set; }
      	public string Name { get; set; }
      	public int Age { get; set; }
      }
      That's it for the configuration. Now let's simply add the Students table to our database. We are going to use the Code-First approach for this and create a migration to update our database with. First, let add a migration in the Package Manager Console: PM> Add-Migration InitialEntities And then update our database: PM> Update-Database Our database should contain the table "Students" now and we're all set to play around with some queries.

      Writing a Simple Query to Insert Data in PostgreSQL

      First, let's prepare our Controller for the Actions we need. Let's repurpose ValuesController we get out-of-the-box and rename it to something more appropriate like the StudentsController. After that, let's do some cleanup, and add the methods we need to try our database out.
      [Route("api/[controller]")]
      [ApiController]
      public class StudentsController : ControllerBase
      {
      	private readonly ApplicationContext _context;
      
      	public StudentsController(ApplicationContext context)
      	{
      		_context = context;
      	}
      
      	[HttpGet]
      	public IActionResult Get()
      	{
      		return Ok();
      	}
      
      	[HttpGet("{id}, Name = "GetById")]
      	public IActionResult Get(string id)
      	{
      		var student = _context.Find<Student>(Guid.Parse(id));
      
      		return Ok(student);
      	}
      
      	[HttpPost]
      	public IActionResult Post([FromBody] Student student)
      	{
      		_context.Add(student);
      
      		_context.SaveChanges();
      
      		return CreatedAtRoute(nameof(GetById), new { id = student.Id }, student);
      	}
      }
      This is a simple controller, and all it has is three methods: GET api/students GET api/students/{id} POST api/students Let's use our POST method to insert some data into the database. For this purpose, we like to use Postman, but feel free to use any client of your choice. Let's add Chris Pratt to our Students table (although he might not be interested in programming classes):
      {
      	"Name": "Chris Pratt",
      	"Age": "40"
      }
      Here's the Postman request: insert request postman   As you can see, we got the 201 Created response, which means we successfully created a new student. And to be sure, let's check the pgAdmin: pgAdmin result If you see this record, that means you've completed everything successfully until this point. Great. Let's read some data.

      Reading Data from PostgreSQL

      Now to finish this example off, let's use postman to read our new student from the database. To do that, let's request a student we've just created. Since we know that we've added Chris Pratt recently and what his id is, our request should be (you'll have a different id, which EF Core generates automatically): https://localhost:5001/api/students/e4624adb-c03d-4c86-80b8-0c255960ec54 Here's the Postman request: get request postman This time we get the 200 OK response, and Chris Pratt in the response. That was everything we needed.

      Conclusion

      As you can see, working with PostgreSQL is not much different than working with MSSQL or MySQL that we've covered so far. So to sum it up, in this article we've learned:
      • How to install PostgreSQL server
      • Configure the PostgreSQL provider and our application to use it
      • How to make some simple requests to insert and read data from PostgreSQL
        [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48586 0 0 0
      Migrations and Seed Data with Entity Framework Core https://code-maze.com/migrations-and-seed-data-efcore/ Mon, 12 Aug 2019 06:02:22 +0000 https://code-maze.com/?p=48600 database model (entity and context classes) and applied different configuration options. Now it is time to transfer this database model to the real database in the SQL server. Our SQL database schema needs to be aligned with our application’s database model and using migrations will help us keep things that way. EF Core provides a method called Migrate to execute migration actions for us. All we have to do is to create model classes, context class, apply configuration (which we already did) and create and execute migrations with the set of simple commands. You can download the source code for this article on our GitHub repository. To see all the basic instructions and complete navigation for this series, visit Entity Framework Core with ASP.NET Core Tutorial. This post is divided into several parts:

      Creating and Applying Migrations in EF Core

      Using migrations is a standard way to create and update a database with Entity Framework Core. The migration process has two steps: Creating migration and Applying migration. As we already said, our database schema must be aligned with the database model and every change in a database model needs to be migrated to the database itself. Those changes might for example be:
      • Changes in the properties of the model class
      • Configuration changes
      • Adding or removing the DbSet<T> properties from the context class
      From ASP.NET Core 3.0, EF Core tools required for migrations are not preinstalled. Therefore, we have to install Microsoft.EntityFrameworkCore.Tools library. If you are following this series from the start, then you already have the Microsoft.EntityFrameworkCore library installed. To create a migration, we can use the Visual Studio’s Package Manager Console window or the command window (Windows command prompt). For the PMC window, the command is: Add-Migration MigrationName [options] Or through the dotnet CLI: dotnet ef migrations add MigrationName [options] In our application, we are going to use the PMC, so let’s do that by typing: PM> Add-Migration InitialMigration After we press the Enter key, our migration will be completed.

      Actions that Take Place Behind the Scene

      After we execute the Add-Migration command EF Core does several things behind the scenes to prepare our migration. The first thing it does is inspecting our class, associated entity classes (in this case only the Student class) and any configuration we applied. After that, it creates three different files in the Migrations folder: Migrations-folder The ApplicationContextModelSnapshot.cs file holds the model of the database and it is updated every time a new migration is added. The other two files: InitialMigration and InitialMigration.Designer are files that contain and describe the newly created migration. So, if you have followed all the steps from the previous articles, the content of the InitialMigration file should look like this:
      public partial class InitialMigration : Migration
      {
          protected override void Up(MigrationBuilder migrationBuilder)
          {
              migrationBuilder.CreateTable(
                  name: "Student",
                  columns: table => new
                  {
                      StudentId = table.Column<Guid>(nullable: false),
                      Name = table.Column<string>(maxLength: 50, nullable: false),
                      Age = table.Column<int>(nullable: true),
                      IsRegularStudent = table.Column<bool>(nullable: false, defaultValue: true)
                  },
                  constraints: table =>
                  {
                      table.PrimaryKey("PK_Student", x => x.StudentId);
                  });
          }
      
          protected override void Down(MigrationBuilder migrationBuilder)
          {
              migrationBuilder.DropTable(
                  name: "Student");
          }
      }
      
      This file has two methods conveniently named Up and Down. The Up method consists of commands that will be executed when we apply this migration. As an opposite action, the Down method will execute commands when we remove this migration (in this case it will just drop this created table).

      Applying Created Migration

      After we have successfully created our migration, we have to apply it for changes to take effect in the database. There are several ways of applying migrations (Using SQL scripts, using Database.Migrate method or using command line methods), and as we did with the creation, we are going to use the command line methods approach. For the Package Manager Console the command is : Update-Database [options] For the command prompt window the command is: dotnet ef database update [options] Since we already decided on PMC, let’s open the PMC window and execute the command: PM> Update-Database After we press the Enter key, we are going to see all the different actions EF Core does for us to apply created migration. As a result, we are going to have our Student table created with all the provided configuration from the previous articles: Migrations applied There are few more important facts we have to know about EF Core’s migrations. If we inspect our database, we are going to find another created table: _EFMigrationsHistoriy. EF Core uses this table to track all the applied migrations. So, this means that if we create another migration in our code and apply it, EF Core will apply only the newly created migration. But how does EF Core know what migration needs to be applied? Well, it stores a unique Id in the _EFMigrationsHistory, which is a unique name of the migration file created with the migration, and never executes files with the same name again: EFCoreMigrations table Each migration is applied within an SQL transaction, which means that whole migration either succeeds or fails. If we have multiple migrations to apply, then they will be applied in the exact order they are created.

      Adding a Custom Code in a Migration File

      We have already explained the purpose of the Up and Down methods in our InitialMigration file. But all the code in those methods is generated by EF Core. If needed, we can add our custom code, too. We can use the MigrationBuilder parameter to access the broad range of methods that can help us in the process. One of those methods is the Sql method that we can use to add the custom code we like. So, let’s open the InitialMigration class and modify it by adding our custom code:
      public partial class InitialMigration : Migration
      {
          protected override void Up(MigrationBuilder migrationBuilder)
          {
              migrationBuilder.CreateTable(
                  name: "Student",
                  columns: table => new
                  {
                      StudentId = table.Column<Guid>(nullable: false),
                      Name = table.Column<string>(maxLength: 50, nullable: false),
                      Age = table.Column<int>(nullable: true),
                      IsRegularStudent = table.Column<bool>(nullable: false, defaultValue: true)
                  },
                  constraints: table =>
                  {
                      table.PrimaryKey("PK_Student", x => x.StudentId);
                  });
      
              migrationBuilder.Sql(@"CREATE PROCEDURE MyCustomProcedure
                                     AS
                                     SELECT * FROM Student");
          }
      
          protected override void Down(MigrationBuilder migrationBuilder)
          {
              migrationBuilder.DropTable(
                  name: "Student");
      
              migrationBuilder.Sql(@"DROP PROCEDURE MyCustomProcedure");
          }
      }
      
      We should make sure to have the Sql method in the Down method to execute the opposite actions if we decide to remove our migration. Now, we can delete our database (just to simulate the initial state in the SQL server) and only apply our migration again (we don’t need to create it, it is already created).

      Creating Migration if Entities and Dbcontext Files are in a Separate Project

      Right now, our model and context classes are in the main project together with the migration files. But in many real-life projects, models and context classes are in a separate project (repository pattern might be one of the reasons for example). For such projects, executing migrations couldn’t be possible with the setup as we have in our project. Let’s try to demonstrate what we mean. The first thing to do is to create another .NET Core Class Library project and name it Entities and install Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.Relational packages via NuGet Package Manager or PMC window: PM> Install-Package Microsoft.EntityFrameworkCore -Version 3.1.0 PM>Install-Package Microsoft.EntityFrameworkCore.Relational -Version 3.1.0 Then we need to add the reference to the Entities project in our main project. After that, let’s copy the ApplicationContext and the Student classes, paste them in the Entities project and remove the Entities folder from the main project. Our structure should look like this: New Project Structure - Migrations from different project As soon as we do that, we need to change the namespace in the ApplicationContext and Student classes from EFCoreApp.Entities to just Entities. Furthermore, we have to do the same thing for the using directives in the Startup class and in all three migration files. Having done all that, our project should build successfully.

      Adding a New Migration

      Now we can try to add another migration by typing: PM> Add-Migration TestMigrationFromSeparateProject But, as soon as we hit the Enter key, we are going to get an error message which explains that our EFCoreApp project doesn’t match our migrations assembly Entities. This error message is great because it provides us with an explanation of how to solve our problem. All we have to do is to change our migrations assembly, so let’s do exactly that in the Startup class:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext<ApplicationContext>(opts =>
              opts.UseSqlServer(Configuration.GetConnectionString("sqlConnection"),
                  options => options.MigrationsAssembly("EFCoreApp")));
      
          services.AddControllers();
      }
      
      Now, we can run the same command again, but this time it will execute successfully. We have successfully created our new migration along with the migration files in the Migrations folder: Test Migrations folder We can see that the TestMigration file has no code in the Up and Down methods, and this is normal because we didn’t change anything, but we completed our required task. We are going to see how to dispose of this empty migration next.

      Removing a Migration

      We’ve learned how to create migrations from a separate project. But as a result of that, we have created an empty migration which does nothing in our database.  When we create a migration that we’re not satisfied with, we can easily remove it by typing the Remove-Migration [options]command in the PMC window. So, let’s do that: PM> Remove-Migration After a few seconds our previous migration will be removed: Removing migration Excellent, now we can move on.

      Seed Data in Entity Framework Core

      In most of our projects, we want to have some initial data in the created database. So as soon as we execute our migration files to create and configure the database, we want to populate it with some initial data. This action is called Data Seeding. We can create the code for the seeding action in the OnModelCreating method by using the ModelBuilder, as we did for the Fluent API configuration. So, let’s add a few rows into the Student table:
      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
          modelBuilder.Entity<Student>()
              .ToTable("Student");
          modelBuilder.Entity<Student>()
              .Property(s => s.Age)
              .IsRequired(false);
          modelBuilder.Entity<Student>()
              .Property(s => s.IsRegularStudent)
              .HasDefaultValue(true);
      
          modelBuilder.Entity<Student>()
              .HasData(
                  new Student
                  {
                      Id = Guid.NewGuid(),
                      Name = "John Doe",
                      Age = 30
                  },
                  new Student
                  {
                      Id = Guid.NewGuid(),
                      Name = "Jane Doe",
                      Age = 25
                  }
              );
      }
      
      So, we are using the HasData method to inform EF Core about the data it has to seed. The rest of the code is self-explanatory because we are just adding the required data. We are not using the IsRegularStudent property because we created a configuration for that property to have a default value. Now we can create a new migration: PM> Add-Migration SeedInitialData And apply it: PM> Update-Database We can check out our table to inspect the result: Data seed

      A Better Way for Applying Configuration and Data Seed

      We can place all of the configuration code inside the OnModelCreating method, and that will work as it supposed to. As we can see, our OnModelCreating method is readable and easy to maintain. But, what if we had a larger project with more classes and more data to seed? Our method would become hard to read and maintain. EF Core provides a better way for creating a Fluent API configuration by using the IEntityTypeConfiguration<T> interface. By using it, we can divide the configuration for each entity into its own separate configuration class. So, let’s see how to do that. In the Entities project, we are going to create a new folder Configuration and inside a new class StudentConfiguration:
      public class StudentConfiguration : IEntityTypeConfiguration<Student>
      {
          public void Configure(EntityTypeBuilder<Student> builder)
          {
              throw new NotImplementedException();
          }
      }
      
      Of course, we don’t want to throw an exception (it is a default code after VS implements an interface), so, let’s modify this method:
      public void Configure(EntityTypeBuilder<Student> builder)
      {
          builder.ToTable("Student");
          builder.Property(s => s.Age)
              .IsRequired(false);
          builder.Property(s => s.IsRegularStudent)
              .HasDefaultValue(true);
      
          builder.HasData
          (
              new Student
              {
                  Id = Guid.NewGuid(),
                  Name = "John Doe",
                  Age = 30
              },
              new Student
              {
                  Id = Guid.NewGuid(),
                  Name = "Jane Doe",
                  Age = 25
              },
              new Student
              {
                  Id = Guid.NewGuid(),
                  Name = "Mike Miles",
                  Age = 28
              }
          );
      }
      
      This code is a little bit different from the old OnModelCreating code because we don’t have to use .Entity<Student> part anymore. That’s because our builder object is already of type EntityTypeBuilder<Student>. We have added an additional object to insert, just to have something to create a migration for. All we have to do is to modify the OnModelCreating method:
      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
          modelBuilder.ApplyConfiguration(new StudentConfiguration());
      }
      
      And that is all. We can now add a new migration and apply it: PM> Add-Migration AdditionalRowInserted PM> Update-Database Additional row inserted

      Setup the Initial Migration as Soon as Applications Starts

      For every created migration, we had to apply its changes manually. And this is quite okay. But when we deploy our application, it would be nice to have initial data at that moment in the database. What would be even nicer is that we don’t have to do that manually, but to start all the required migrations and seed all the required data as soon as the application starts. Of course, besides being useful on deployment, it helps when sharing or developing our application with other people. We would like them to start the app and execute all the migrations before the app configures. Well, we are going to show you how to do exactly that.

      Creating an Extension Method

      Let’s create a new class MigrationManager in the Entities project. It is going to be a static class because we are going to create an extension method to start all the migrations at the application’s startup:
      public static class MigrationManager
      {
      }
      
      Now, we have to install Microsoft.ASPNetCore.Hosting.Abstractions library (we need this for the IHost type we are going to use in our extension method) and add the MigrateDatabase extension method to this class:
      public static class MigrationManager
      {
           public static IHost MigrateDatabase(this IHost host)
           {
               using (var scope = host.Services.CreateScope())
               {
                   using (var appContext = scope.ServiceProvider.GetRequiredService<ApplicationContext>())
                   {
                       try
                       {
                           appContext.Database.Migrate();
                       }
                       catch (Exception ex)
                       {
                           //Log errors or do anything you think it's needed
                           throw;
                       }
                   }
               }
      
               return host;
          }
      }
      
      We are using the IHost type because this allows us to chain this method in the Program.cs file and of course, as you can see, we need it for the main logic. So, we are creating a service scope and using it with the ServiceProvider to obtain an instance of the ApplicationContext class. In the first article, we have discussed properties contained in the DbContext class, and now, we are using one of them (Database) to call the Migrate method for the migration execution.

      Applying the MigrateDatabase method

      The next step is to call this method in the Program.cs class:
      public static void Main(string[] args)
       {
          CreateWebHostBuilder(args)
              .Build()
              .MigrateDatabase()
              .Run();
       }
      
      Finally let’s remove the Student and _EFMigrationsHistory tables from the database and remove the stored procedure in the Programmability folder, to simulate an empty database (or just drop your database :D). Then, we can start our application. We are going to see logs in a console window which tells us that migrations are executing. After the migrations have finished their work, we can check the database to confirm that all the tables and procedure have been created again.

      Reverting and Scripting Migrations

      In one of the previous sections, we have learned how to remove migration if we haven’t applied it. But in the case we have, we can’t remove it just like that, we need to revert it to the specific migration. So, to show how migration reverting works, we are going to add another row in the StudentConfiguration class, create, apply migration and then revert it back to the previous values. Let’s first add another row to seed:
      new Student
      {
          Id = Guid.NewGuid(),
          Name = "TEST Name",
          Age = 100
      }
      
      Then let’s create: PM> Add-Migration RevertTestMigration and apply migration: PM> Update-Database We can see in the database that a new row has been added. But as we are not satisfied with this migration (hypothetically), let’s revert it: PM> Update-Database AdditionalRowInserted The AdditionalRowInserted migration was the previous one, and if we check our database now, we are going to see that is was reverted to the specific migration values. Finally, if we want to make a SQL script of all our migrations, we can do that by typing: PM> Script-Migration This command will create a script file for us.

      Conclusion

      Excellent, we have learned a lot of different information about data migration and how to use it in various situations within EF Core. So, to summarize, we have covered:
      • How to create and apply migrations
      • The way to add custom code in our migrations
      • Using model and context classes from a different project for our migrations
      • How to seed data and to set up initial seed as soon as the project starts
      • The way to remove, revert migrations and to create script files
      In the next article, we are going to learn more about the configuration of relationships in EF core. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48600 0 0 0 1543 0 0 1544 1543 0 1561 0 0 1562 Guid.NewGuid() which generates a new value every time. If you want to hard code those values, and trust me it is a good approach as well to have initial data with the static guids, you can use new Guid("guid goes here") expression. Thank you very much for reading this article and all the best.]]> 1561 0 1599 in your example? For example, let say I have another table (e.g. 'Exams' with its PK). I want to keep configurations on separate classes. When I seed the data I need to know the primary key from the 'Exams' table before creating a new 'Student' record. Am I right? How could I achieve this?]]> 0 0 1600 builder.HasData ( new Student { Id = new Guid("660ed4cd-1361-4216-9faa-9636e4df681a"), Name = "John Doe", Age = 30 }, . . . And for the Evalutaion table: builder.HasData ( new Evaluation { Id = Guid.NewGuid(), Grade = 5, AdditionalExplanation = "First test...", StudentId = new Guid("660ed4cd-1361-4216-9faa-9636e4df681a") }, Please, read our other articles from the series and check their source code as well, you will see this in the example. Hope this helps you. All the best.]]> 1599 0
      Entity Framework Core Relationships – Convention, Data Annotations and Fluent API https://code-maze.com/efcore-relationships/ Mon, 19 Aug 2019 05:55:44 +0000 https://code-maze.com/?p=48622 the second part of this series, we have learned how to configure non-relational properties in Entity Framework Core. So as a logical continuation, this article will be dedicated to learning about database relationships configuration with Entity Framework Core (EF Core Relationships). We will show you how to create additional entities in the database model and how to create relationships between them. We are going to use all three ways: by Convention, Data Annotations and Fluent API, to create those relationships. You can download the source code for this article on our GitHub repository. To see all the basic instructions and complete navigation for this series, visit Entity Framework Core with ASP.NET Core Tutorial. This is what we are going to learn about:

      EF Core Relationships - Concepts and Navigational Properties

      Right now, we have only one entity (model) class, the Student class, but soon enough we are going to create the rest of the database model in our application. But before we do that, it is quite important to understand some basic concepts when working with relational databases and models. When we create a relationship between two entities, one of them becomes the Principal entity and another one is the Dependent entity. The Principal entity is the main entity in a relationship. It contains a primary key as a property that the dependent entity refers to via the foreign key. The Dependent entity, from the other side, is the entity that holds the foreign key that refers to the principal entity’s primary key. Our entity classes will contain Navigational properties which are the properties containing a single class or a collection of classes that EF Core uses to link entity classes. Additionally, let’s explain the Required and Optional relationships in EF Core. The required relationship is a relationship where a foreign key cannot be null. This means that the principal entity must exist. The optional relationship is a relationship where a foreign key could be null and therefore the principal entity can be missing.

      Configuring One-to-One Relationship

      The one-to-one relationship means that a row in one table can only relate to one row in another table in a relationship. This is not that common relationship because it is usually handled as “all the data in one table”, but sometimes (when we want to separate our entities) it is useful to divide data into two tables. The easiest way to configure this type of relationship is to use by the Convention approach, and that is exactly what we are going to do. So let’s first create another class in the Entities project, named StudentDetails:
      public class StudentDetails
      {
          [Column("StudentDetailsId")]
          public Guid Id { get; set; }
          public string Address { get; set; }
          public string AdditionalInformation { get; set; }
      }
      
      Now, to establish a relationship between the Student and the StudentDetails classes we need to add a reference navigation property at both sides. So, let’s first modify the Student class:
      public class Student
      {
          [Column("StudentId")]
          public Guid Id { get; set; }
      
          [Required]
          [MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
          public string Name { get; set; }
      
          public int? Age { get; set; }
          public bool IsRegularStudent { get; set; }
      
          public StudentDetails StudentDetails { get; set; }
      }
      
      And let’s modify the StudentDetails class:
      public class StudentDetails
      {
          [Column("StudentDetailsId")]
          public Guid Id { get; set; }
          public string Address { get; set; }
          public string AdditionalInformation { get; set; }
      
          public Guid StudentId { get; set; }
          public Student Student { get; set; }
      }
      
      We can see that the Student class has a reference navigation property towards the StudentDetails class and the StudentDetails class has a foreign key and the navigation property Student. As a result, we can create a new migration and apply it:
      PM> Add-Migration OneToOneRelationshipStudent_StudentDetails
      PM> Update-Database
      
      This is the result: One to one - EF Core Relationships Excellent, this works great.

      Additional Explanation

      If we take a look at the first article of this series, we are going to see that we had to create a DbSet<T> property for the Student class in order to be created in the database. But as we can see, we haven’t done the same thing for the StudentDetails class but it is still created in db. Why is that? Well, as we explained in the first article, EF Core searches for all the public DbSet<T> properties in the DbContext class to create tables in the database. Then it searches for all the public properties in the T class to map the columns. But it also searches for all the public navigational properties in the T class and creates additional tables and columns related to the type of the navigation property. So, in our example, in the Student class, EF Core finds the StudentDetails navigation property and creates an additional table with its columns.

      One-to-Many Relationship Configuration

      In this section, we are going to learn how to create One to Many relationships with all three ways. So, before we start, let’s create an additional model class Evaluation in the Entities project:
      public class Evaluation
      {
          [Column("EvaluationId")]
          public Guid Id { get; set; }
          [Required]
          public int Grade { get; set; }
          public string AdditionalExplanation { get; set; }
      }
      
      Let’s continue on.

      Using by Convention Approach to Create One-to-Many Relationship

      Let’s take a look at the different conventions which automatically configure the one-to-many relationship between the Student and Evaluation classes. The first approach includes the navigation property in the principal entity, the Student class:
      public class Student
      {
          [Column("StudentId")]
          public Guid Id { get; set; }
      
          [Required]
          [MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
          public string Name { get; set; }
      
          public int? Age { get; set; }
          public bool IsRegularStudent { get; set; }
      
          public StudentDetails StudentDetails { get; set; }
      
          public ICollection<Evaluation> Evaluations { get; set; }
      }
      
      We have in the ApplicationContext class a DbSet<Student> property and as we explained, EF Core searches through the Student class to find all the navigational properties to create appropriate tables in the database. Another way to create a One-to-Many relationship is by adding a Student property in the Evaluation class without ICollection<Evaluation> property in the Student class:
      public class Evaluation
      {
          [Column("EvaluationId")]
          public Guid Id { get; set; }
          [Required]
          public int Grade { get; set; }
          public string AdditionalExplanation { get; set; }
      
          public Student Student { get; set; }
      }
      
      For this approach to work, we have to add a DbSet<Evaluation> Evaluations property in the ApplicationContext class. The third approach by Convention is to use a combination of the previous ones. So, we can add the ICollection<Evaluation> Evaluations navigational property to the Student class and add the Student Student navigational property in the Evaluation class. Of course, with this approach, we don’t need the DbSet<Evaluation> Evaluations property in the ApplicationContext class. This is the result of any of these three approaches: Optional relationship - EF Core Relationships We can see that the relationship was properly created, but our foreign key is a nullable field. This is because both navigational properties have a default value of null. This relationship is also called an Optional Relationship (we have talked about it in the first part of this article). If we want to create a required relationship between the Student and Evaluation entities, we have to include the foreign key into the Evaluation class:
      public class Student
      {
          [Column("StudentId")]
          public Guid Id { get; set; }
      
          [Required]
          [MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
          public string Name { get; set; }
      
          public int? Age { get; set; }
          public bool IsRegularStudent { get; set; }
          
          public StudentDetails StudentDetails { get; set; }
          
          public ICollection<Evaluation> Evaluations { get; set; }
      }
      
      public class Evaluation
      {
          [Column("EvaluationId")]
          public Guid Id { get; set; }
          [Required]
          public int Grade { get; set; }
          public string AdditionalExplanation { get; set; }
      
          public Guid StudentId { get; set; }
          public Student Student { get; set; }
      }
      
      Now when we execute our migration, we are going to see the following result: Required relationship - EF Core Relationships It is obvious that our relationship is now required.

      Data Annotations Approach

      The Data Annotations approach contains only two attributes related to relationships. The [ForeignKey] and [InverseProperty] attributes. The [ForeignKey] attribute allows us to define a foreign key for a navigational property in the model class. So, let’s modify the Evaluation class by adding this attribute:
      public class Evaluation
      {
          [Column("EvaluationId")]
          public Guid Id { get; set; }
          [Required]
          public int Grade { get; set; }
          public string AdditionalExplanation { get; set; }
      
          [ForeignKey(nameof(Student))]
          public Guid StudentId { get; set; }
          public Student Student { get; set; }
      } 
      
      We have applied the [ForeignKey] attribute on top of the StudentId property(which is a foreign key in this class) giving it a name of the navigational property Student. But it also works the other way around:
      public class Evaluation
      {
          [Column("EvaluationId")]
          public Guid Id { get; set; }
          [Required]
          public int Grade { get; set; }
          public string AdditionalExplanation { get; set; }
         
          public Guid StudentId { get; set; }
          [ForeignKey(nameof(StudentId))]
          public Student Student { get; set; }
      }
      
      The ForeignKey attribute takes one parameter of type string. If the foreign key is a composite key then the ForeignKey attribute should look like this: [ForeignKey(“Property1”, “Property2”)]. Whichever way we choose, the result is going to be the same as with the “by Convention” approach. We are going to have a required relationship created between these two tables: Required relationship - EF Core Relationships

      Fluent API approach for the One-to-Many Configuration

      To create a One-to-Many relationship with this approach, we need to remove the [ForeignKey] attribute from the Evaluation class and to modify the StudentConfiguration class by adding this code:
      builder.HasMany(e => e.Evaluations)
          .WithOne(s => s.Student)
          .HasForeignKey(s => s.StudentId);
      
      With a code like this, we inform EF Core that our Student entity (the builder object is of type EntityTypeBuilder<Student>) can be in a relationship with many Evaluation entities. We also state that the Evaluation is in a relationship with only one Student entity. Finally, we provide information about the foreign key in this relationship. The result is going to be the same: Required relationship - EF Core Relationships We need to mention one thing here. For the database model like we’ve defined, we don’t need to have the HasForeignKey method. That’s because the foreign key property in the Evaluation class has the same type and the same name as the primary key in the Student class. This means that by Convention this relation would still be the required one. But if we had a foreign key with a different name, StudId for example, then the HasForeignKey method would be needed because otherwise, EF core would create an optional relationship between Evaluation and Student classes.

      Many-to-Many Relationship Configuration

      Before we start explaining how to configure this relationship, let’s create the required classes in the Entities project:
      public class Subject
      {
          [Column("SubjectId")]
          public Guid Id { get; set; }
          public string SubjectName { get; set; }
      }
      
      public class StudentSubject
      {
          public Guid StudentId { get; set; }
          public Student Student { get; set; }
      
          public Guid SubjectId { get; set; }
          public Subject Subject { get; set; }
      }
      
      Now, we can modify the Student and Subject classes by providing the navigational property for each class towards the StudentSubject class:
      public class Subject
      {
          [Column("SubjectId")]
          public Guid Id { get; set; }
          public string SubjectName { get; set; }
      
          public ICollection<StudentSubject> StudentSubjects { get; set; }
      }
      
      public class Student
      {
          [Column("StudentId")]
          public Guid Id { get; set; }
      
          [Required]
          [MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
          public string Name { get; set; }
      
          public int? Age { get; set; }
          public bool IsRegularStudent { get; set; }
              
          public StudentDetails StudentDetails { get; set; }
              
          public ICollection<Evaluation> Evaluations { get; set; }
      
          public ICollection<StudentSubject> StudentSubjects { get; set; }
      }
      
      In Entity Framework Core, we have to create a joining entity for a joining table (StudentSubject). This class contains the foreign keys and navigational properties from the Student and Subject classes. Furthermore, the Student and Subject classes both have navigational ICollection properties towards the StudentSubject class. So basically, the Many-to-Many relationship is just two One-To-Many relationships. We have created our entities, and now we have to create the required configuration. For that, let’s create the StudentSubjectConfiguration class in the Entities/Configuration folder:
      public class StudentSubjectConfiguration : IEntityTypeConfiguration<StudentSubject>
      {
          public void Configure(EntityTypeBuilder<StudentSubject> builder)
          {
              builder.HasKey(s => new { s.StudentId, s.SubjectId });
      
              builder.HasOne(ss => ss.Student)
                  .WithMany(s => s.StudentSubjects)
                  .HasForeignKey(ss => ss.StudentId);
      
              builder.HasOne(ss => ss.Subject)
                  .WithMany(s => s.StudentSubjects)
                  .HasForeignKey(ss => ss.SubjectId);
          }
      }
      
      As we said, the Many-to-Many is just two One-to-Many EF Core relationships and that’s exactly what we configure in our code. We create a primary key for the StudentSubject table which is, in this case, a composite key. After the primary key configuration, we use a familiar code to create relationship configurations. Now, we have to modify the OnModelBuilder method in the ApplicationContext class:
      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
          modelBuilder.ApplyConfiguration(new StudentConfiguration());
          modelBuilder.ApplyConfiguration(new StudentSubjectConfiguration());
      }
      
      After these modifications, we can create a migration and apply it: PM> Add-Migration ManyToManyRelationship PM> Update-Database This is the result: Many to many relationship Excellent work. Let’s press on.

      OnDelete Method

      The OnDelete method configures the delete actions between relational entities. We can add this method to the end of the relationship configuration to decide on how delete actions will execute. The values that can be used in the OnDelete method are:
      • Restrict – The delete action isn’t applied to dependent entities. This means that we can’t delete the principal entity if it has related dependent entity.
      • SetNull – The dependent entity isn’t deleted but its foreign key property is set to null.
      • ClientSetNull – If EF Core tracks dependent entity its foreign key is set to null and that entity is not deleted. If it doesn’t track the dependent entity, the database rules apply.
      • Cascade – The dependent entity is deleted with the principal entity.
      If we look at our entities: Student and Evaluation, we are going to see that we have a required relationship between them. For this type of relationship, the Cascade deleting action is configured by default. We can also see that from the code in our migration file: Cascasde delete We can change this type of behavior by modifying the configuration code in the StudentConfiguration class:
      builder.HasMany(e => e.Evaluations)
           .WithOne(s => s.Student)
           .HasForeignKey(s => s.StudentId)
           .OnDelete(DeleteBehavior.Restrict);
      
      Let’s create another migration: PM> Add-Migration StudentEvaluationRestrictDelete And take a look at the migration generated code: Restrict delete Great job.

      Conclusion

      Configuring EF Core Relationships in our database model is a very important part of the modeling process. We have seen that EF Core provides us with several ways to achieve that and to make the process as easy as it can be. Now that we know how to establish relationships in our database, we can continue to the next article where we are going to learn how to access data from the database. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48622 0 0 0
      Differences Between .NET Framework, .NET Core, and .NET Standard https://code-maze.com/differences-between-net-framework-net-core-and-net-standard/ Mon, 21 Oct 2019 06:00:08 +0000 https://code-maze.com/?p=48647
    • The Importance of .NET Framework
    • The Rise of .NET Core
    • What is .NET Standard?
    • Confusion about .NET Standard
    • .NET Framework vs .NET Core
    • .NET Framework and .NET Core vs .NET Standard
    • Deciding Target Platform and Version
    • Conclusion
    • Let's start chronologically, with .NET Framework.

      The Importance of .NET Framework

      .NET Framework is the original implementation of .NET which was developed by Microsoft in the early 2000s to build Web and Desktop applications for Windows. It allows us to write applications in C#, Visual Basic and F#. It is an execution environment that provides a variety of services to its running applications and also an extensive class library to write different kinds of applications. .NET Framework consists of two major components:
      1. Common language runtime (CLR) which handles the execution of applications
      2. Base Class Library (BCL) which provides a library of tested and reusable code that developers can use in their applications
      The code written in any .NET supported language is compiled into a special language called Intermediate Language (IL) and stored in assembly files with a .dll or .exe file extension. When an application runs, the CLR takes the assembly and turns it into machine code through a process called just-in-time (JIT) compilation. The .NET Framework provides many services to its running applications. It provides memory management, common type system and also language interoperability. Going into details of these topics is not in the scope of this article, therefore, we are not going to explain them.

      The Rise of .NET Core

      Due to the rise of different devices and cloud computing, in the past few years, software development trends have changed quite rapidly. Along with Windows, other operating systems usage has also increased. A large number of software and services are now being developed for different platforms. Because of this trend, Microsoft started developing a new framework that can fulfill the requirements of the current as well as future software development needs. Hence, they developed a new framework called .NET Core. It is an open-source and cross-platform implementation of .NET. It shares many of its characteristics with .NET Framework but there are differences as well which we are going to discuss in a while. All aspects of .NET Core are open-source including class libraries, runtime, compilers, languages as well as application frameworks. .NET Core also supports C#, Visual Basic and F#. It can run the application code with the same behavior on multiple architectures, including x64, x86, and ARM. It has a flexible deployment model in which it can be included in the application or installed side-by-side (user-wide or system-wide). .NET Core can also be used with Docker. It also includes command-line tools that can be used during local development and most importantly in continuous integration. Currently, all the innovations and major enhancements are being made in .NET Core. However, the .NET Framework is also being developed but at a much slower pace.

      What is .NET Standard?

      .NET Standard is a specification (not an implementation of .NET) which defines the set of APIs that all .NET implementations must provide. It addresses the code sharing problem for .NET developers across all platforms by bringing APIs across different environments. We can think of it as another .NET Framework, except that we use it to develop class libraries only. .NET Standard is a successor of the portable class library. Ok, so .NET Standard specifies the APIs that need to be implemented. But which APIs does it cover? To answer this question in short - there are multiple versions of .NET Standard. Each version includes a set of APIs which we are going to cover in a while.

      Confusion about .NET Standard

      Newcomers to .NET who try to understand the .NET Standard can easily get confused in the beginning. And here’s one of the reasons why that might be. When we open up Visual Studio 2019 and go to the Create a new project window, we’ll see three types of class library projects; Class Library (.NET Standard), Class Library (.NET Framework) and Class Library (.NET Core). If we carefully read the description which is written under Class Library (.NET Standard) and Class Library (.NET Core), it says that target platforms are .NET Standard and .NET Core respectively. This might give us a feeling that the .NET Standard is another framework or implementation of .NET: Library Project types in Visual Studio 2019 Furthermore, let’s create three projects for each platform:
      1. StandarLib is a Class Library project type that targets the .NET Standard
      2. NetCoreWebApplication is a Web Application type project which targets the .NET Core
      3. WindowFormApp is a Windows Form type project which targets the .NET Framework
      If we take a look at the properties of all three projects, we can see each property window has a field named Target framework; which is right for the .NET Framework and .NET Core projects. But for the .NET Standard project, it might be misleading, because, newcomers might think of it as a framework and not a specification: Target Frameworks The official documentation introduces .NET Standard as a specification and tries to distinguish it from the framework but this distinction doesn’t seem to be reflected in the development tools.

      .NET Framework vs .NET Core

      Now we understand that .NET Framework and .NET Core are two different .NET implementations, therefore, both can be compared. .NET Framework
      1. The .NET Framework is the first implementation of .NET which works on Windows only
      2. Its source code is public but Microsoft doesn’t accept third party contributions for it
      3. It has a very rich desktop top development framework for windows which include Windows Forms and WPF
      4. A huge third-party packages library is also available for it
      5. It doesn’t support the in-app deployment model
      6. Although it can be used with a docker container, its image size is large and can only be deployed on Windows containers
      .NET Core
      1. .NET Core is the latest implementation of .NET which runs on Windows, Linux, and macOS
      2. Its open-source and Microsoft accepts third party contribution to .NET Core
      3. It supports desktop frameworks like Windows Forms and WPF from version 3.0
      4. The .NET Core also has support for a large number of third party packages as well but still, it doesn’t compete with .NET Framework in this area
      5. It does support in-app deployment model
      6. It is the best choice to work with docker containers

      .NET Framework and .NET Core vs .NET Standard

      Because .NET Framework and .NET Core are .NET implementations, therefore, we can compare them together against the .NET Standard. .NET Framework and .NET Core
      1. The .NET Framework and .NET Core are implementations of .NET
      2. Both frameworks have runtime which manages the execution of applications
      3. The base class library is also a part of both frameworks
      4. We can create different types of projects in either framework
      .NET Standard
      1. The .NET Standard is a specification and not a .NET implementation
      2. It specifies a set of APIs that all the .NET implementations have to implement
      3. We can create only class library type projects with it

      Deciding Target Platform and Version

      Let’s discuss what point should we keep in mind while choosing the target platform and their versions. We should use the .NET Core:
      1. While developing applications for cross-platform
      2. For the development of microservices
      3. When we want to use Docker containers
      4. To develop high-performance and scalable systems
      Use the .NET Framework when:
      1. We want to target only Windows
      2. Our application uses some third party packages which are not supported by .NET Core
      3. The application uses .NET technologies that are not available for .NET Core
      .NET Standard should be the choice when:
      1. We want to share our common code across different .NET implementations
      Once we choose the right platform and project type for our application, the next step is to use the correct version. Let’s suppose that we have to target both, the .NET Framework and .NET Core and we also want to share the common code across both of these platforms. Of course, the  .NET Standard class library will be our choice here. Choosing the correct version for this scenario would have been quite challenging, but fortunately, Microsoft has provided us with a nice helpful chart: .NET Standard Chart As shown in this chart, the first row mentions the different versions of .NET Standard and all other rows mention the different versions of various platforms supported by it. For example, if we choose 1.6 version of .NET Standard for our class library project and reference that library in .NET Core and .NET Framework projects then according to this chart we must choose 1.0 version for .NET Core project and 4.6.1 version of .NET Framework project. The same goes for all other versions.

      Conclusion

      We have tried to remove that confusion among the .NET Framework, .NET Core, and .NET Standard by clarifying these concepts and comparing them with each other. We have also shown how to choose the target platform and its version depending on our needs. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48647 0 0 0 1552 0 0 1553 1552 0
      Database Queries in Entity Framework Core https://code-maze.com/queries-in-entity-framework-core/ Mon, 26 Aug 2019 06:00:00 +0000 https://code-maze.com/?p=48669 the source code for this article on our GitHub repository. To see all the basic instructions and complete navigation for this series, visit Entity Framework Core with ASP.NET Core Tutorial. This is what we are going to learn about: Before we start with the next section of this article, we suggest downloading the starting project, adjusting the connection string and running it. By doing so, it will seed all the required data which we need for this article. Feel free to continue with the same project until the end of this article.

      Understanding Queries in Entity Framework Core

      Now, we can start querying data from the database by using the EF Core. Every query consists of three main parts:
      • Connection to the database via the ApplicationContext’s DbSet property
      • Series of LINQ and/or EF Core commands
      • Query execution
      The second part is used often, but sometimes it can be skipped if we want to return all the rows from a table we are connecting to via the DbSet property. So, to explain the query basics, we are going to use the Values controller, as we did in the first part of the series and only the Get action for the sake of simplicity. We are going to focus on the EF Core’s logic, not on Web API overall. If you want to learn more about ASP.NET Core Web API, we strongly recommend reading our ASP.NET Core tutorial. Therefore let’s inject our context object in the Values constructor and write a first query in the Get action:
      [HttpGet]
      public IActionResult Get()
      {
          var students = _context.Students
                 .Where(s => s.Age > 25)
                 .ToList();
                  
           return Ok(students);
       }
      
      From this query, we can see all the mentioned parts. The “_context.Students” is the first part where we access the Student table in the database via the DbSet<Student> Students property. The “.Where(s => s.Age > 25)“ is a second part of the query where we use a LINQ command to select only required rows. Finally, we have ToList() method which executes this query. TIP: When we write only read-only queries in Entity Framework Core(the result of the query won’t be used for any additional database modification), we should always add AsNoTracking method to speed up the execution. In the next article, we are going to talk about how EF Core modifies data in the database and track changes in the loaded entity. For now, just know that EF Core won’t track changes (when we apply AsNoTracking) on the loaded entity which will speed up the query execution:
      [HttpGet]
      public IActionResult Get()
      {
          var students = _context.Students
            .AsNoTracking()
            .Where(s => s.Age > 25)
            .ToList();
                  
           return Ok(students);
       }
      

      Different Ways of Building Relational Queries

      There are different approaches to retrieve our data:
      • Eager loading
      • Explicit Loading
      • Select (Projection) loading
      • Lazy loading
      We are going to talk more about each of them in this article. It is important to know that EF Core will include relationships in the result only if explicitly asked for. So, it doesn’t matter if our Student object has navigational properties because, with the query like the one we’ve written above, they won't be included. As a result of our query, the navigational property values are null: First Query Result - Queries in Entity Framework Core

      Relational Database Queries with Eager Loading in EF Core

      With the Eager Loading approach, EF Core includes the relationships in the query result. It does that by using two different methods Include() and ThenInclude(). In the next example, we are going to return only one student with all the related evaluations, to show how the Include() method works:
      var students = _context.Students
          .Include(e => e.Evaluations)
          .FirstOrDefault();
      
      Before we send request to execute this query, we should install the Microsoft.AspNetCore.Mvc.NewtonsoftJson library and modify the Startup.cs class:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext<ApplicationContext>(opts =>
              opts.UseSqlServer(Configuration.GetConnectionString("sqlConnection"),
                  options => options.MigrationsAssembly("EFCoreApp")));
      
          services.AddControllers()
             .AddNewtonsoftJson(o => o.SerializerSettings.ReferenceLoopHandling =
              Newtonsoft.Json.ReferenceLoopHandling.Ignore);
      }
      
      This is the protection for the “Self-referencing loop” error while returning the result from our API (which does happen in real-world projects). You can use DTO objects, as we did in our ASP.NET Core Web API project, but this is a faster solution and serves the purpose (we are not saying this is a better approach, just as opposite). Now, we should receive a required result after sending the request to http://localhost:5001/api/values: Queries in Entity Framework Core - Eager Loading We can take a look in the console window to see how EF Core translates this query into the SQL command: Translated query We can see that EF core selects the first student from a Student table and then selects all the relational evaluations. The important thing to know is that we can include all the entities in our queries via the Student entity because it has relationships with other entities. That is the reason why we have only one DbSet property of type DbSet<Student> in the ApplicationContext class. But if we want to write a separate query for other entities, Evaluation, for example, we have to add an additional DbSet<Evaluation> property.

      ThenInclude

      To additionally modify our query for including the second-level relationship properties, we can attach the ThenInclude method right after the Include method. So, with the Include method, we are loading the first-level relationship properties and once we attach ThenInclude, we can go even deeper into the relationship graph. Having this in mind, let’s additionally include all the subjects for the selected student:
      var students = _context.Students
          .Include(e => e.Evaluations)
          .Include(ss => ss.StudentSubjects)
          .ThenInclude(s => s.Subject)
          .FirstOrDefault();
      
      The Student entity doesn’t have a direct navigational property to the Subject entity and therefore, we are including the first-level navigational property StudentSubjects and then include the second-level navigational property Subject: ThenInclude Eager Loading - Queries in Entity Framework Core We can go to any depth with the ThenInclude method because if the relationship doesn’t exist, the query doesn’t fail it simply doesn’t return anything. This also applies to the Include method.

      Eager Loading Advantages and Disadvantages and Console Warnings

      The advantage of this approach is that EF Core includes relational data, with Include or ThenInclude, in an efficient manner, using a minimum of database access (database roundtrips). The downside of this approach is that it always loads all the data even though we don’t need some of them. As we’ve seen, when we execute our query, EF Core logs the translated query into the console window. That is a great debugging feature provided by EF Core because we can always decide whether we have created an optimal query in our application by just looking at the translated result.

      Explicit Loading in Entity Framework Core

      With this approach Entity Framework Core explicitly loads relationships into the already loaded entity. So, let’s take a look at different ways to explicitly load relationships:
      var student = _context.Students.FirstOrDefault();
      _context.Entry(student)
          .Collection(e => e.Evaluations)
          .Load();
      
      _context.Entry(student)
          .Collection(ss => ss.StudentSubjects)
          .Load();
      
       foreach (var studentSubject in student.StudentSubjects)
       {
           _context.Entry(studentSubject)
               .Reference(s => s.Subject)
               .Load();
       }
      
      In this example, we’re loading the student entity first. Then we’re including all the evaluations related to the retrieved student. Additionally, we’re including all related subjects via the StudentSubjects navigational property. The important thing to notice is when we want to include a collection into the main entity, we have to use the Collection method, but when we include a single entity as a navigational property, we have to use the Reference method. So, the student object contains ICollection<Evaluation> and ICollection<StudentSubject> properties and both are populated by using the Collection method. On the other hand, the StudentSubject entity contains a single reference towards the Subject entity and therefore we are populating the Subject property with the Reference method.

      Queries in Entity Framework Core with Explicit Loading

      When working with Explicit Loading in Entity Framework Core, we have an additional command available. It allows the query to be applied to the relationship. So, instead of using the Load method, as we did in the previous example, we are going to use the Query method:
      var student = _context.Students.FirstOrDefault();
      
      var evaluationsCount = _context.Entry(student)
          .Collection(e => e.Evaluations)
          .Query()
          .Count();
      
      var gradesPerStudent = _context.Entry(student)
          .Collection(e => e.Evaluations)
          .Query()
          .Select(e => e.Grade)
          .ToList();
      
      The advantage of the Explicit Loading is that we can load a relationship on an entity class later when we really need it. Another benefit is that because we can separately load relationships if we have complex business logic. The relationship loading could be moved to another method or even a class, thus making the code easier to read and maintain. The downside of this approach is that we have more database roundtrips to load all the required relationships. Thus making the query less efficient.

      Select (Projection) Loading

      This approach uses the Select method to pick only the properties we need in our result. Let’s take a look at the following example:
      var student = _context.Students
          .Select(s => new
          {
              s.Name,
              s.Age,
              NumberOfEvaluations = s.Evaluations.Count
          })
          .ToList();
      
      This way we project only the data that we want to return in a response. Of course, we don’t have to return an anonymous object as we did here. We can create our own DTO object and populate it in the projection query. The advantage of this approach is that we can select the data we want to load, but the disadvantage is that we have to write code for every property we want to include in a result.

      Lazy Loading in Entity Framework Core

      Lazy Loading was introduced in EF Core 2.1 and we can use it to postpone the retrieval of data from the database until it is actually needed. This feature can help in some situations but it can degrade our application performance as well and this is the main reason for making it an opt-in feature in EF Core 2.1. You can read more about this feature in the DataModelling section of the  ASP.NET Core Web API with EF Core DB-First Approach article.

      Client vs Server Evaluation

      All the queries we’ve written so far are the ones that EF Core can translate to the SQL commands (as we’ve seen from the console window). But EF Core has a feature called Client vs Server Evaluation which allows us to include methods in our query that can’t be translated into the SQL commands. Those commands will be executed as soon as the data has been retrieved from the database. For example, let’s imagine that we want to show a single student with evaluation explanations as a single string:
      var student = _context.Students
          .Where(s => s.Name.Equals("John Doe"))
          .Select(s => new
          {
              s.Name,
              s.Age,
              Explanations = string.Join(",", s.Evaluations
                  .Select(e => e.AdditionalExplanation))
          })
          .FirstOrDefault();
      
      From EF Core 3.0,  client evaluation is restricted to only happen on the top-level projection (essentially, the last call to Select()). And this is the query result: Client vs Server Validation Result Even though Client vs Server Evaluation allows us to write complex queries, we need to pay attention to the number of rows we return from the database. If we return 20 000 rows our method will run on every row on the client. That can be time-consuming in some cases.

      Raw SQL Commands

      EF Core has methods that we can use to write raw SQL commands to fetch the data from the database. These methods are very useful when:
      • we can’t create our queries with the standard LINQ methods
      • if we want to call a stored procedure
      • if the translated LINQ query is not that efficient as we would like to be

      FromSqlRaw Method

      This method allows us to add raw sql commands to the EF Core queries:
      var student = _context.Students
          .FromSqlRaw(@"SELECT * FROM Student WHERE Name = {0}", "John Doe")
          .FirstOrDefault();
      
      We can also call stored procedures from a database:
      var student = _context.Students
          .FromSqlRaw("EXECUTE dbo.MyCustomProcedure")
          .ToList();
      
      The FromSqlRaw method is a very useful method but it has some limitations:
      • The column names in our result must match the column names that the properties are mapped to
      • Our query must return data for all properties of the entity or query type
      • The SQL query can’t contain relationships, but we can always combine FromSqlRaw with the Include method
      So, if we want to include relationships to our query, we can do it like this:
      var student = _context.Students
          .FromSqlRaw("SELECT * FROM Student WHERE Name = {0}", "John Doe")
          .Include(e => e.Evaluations)
          .FirstOrDefault();
      

      ExecuteSqlRaw Method

      The ExecuteSqlRaw method allows us to execute SQL commands like Update, Insert, Delete. Let’s see how we can use it:
      var rowsAffected = _context.Database
          .ExecuteSqlRaw(
              @"UPDATE Student
                SET Age = {0} 
                WHERE Name = {1}“, 29, "Mike Miles");
      return Ok(new { RowsAffected = rowsAffected});
      
      This command executes required command and returns a number of affected rows. This works the same whether we Update, Insert or Delete rows from the database. In this example, the ExecuteSqlRaw will return 1 as a result because only one row is updated: ExecuteSqlCommand It is quite important to notice that we are using the Database property to call this method, while in a previous example we had to use the Student property for the FromSqlRaw method. Another important thing to notice is that we are using the string interpolation feature for queries in both FromSqlRaw and ExecuteSqlRaw methods because it allows us to place a variable name in the query string, which EF Core then checks and turns into parameters. Those parameters will be checked to prevent SQL injection attacks. We shouldn't use string interpolation outside of the EF Core’s raw query methods because then we will lose the Sql injection attack detections.

      Reload Method

      If we have an entity which is already loaded and then we use the ExecuteSqlRaw method to make some changes to that entity in the database, our loaded entity is going to be outdated for sure. Let’s modify our previous example:
      var studentForUpdate = _context.Students
          .FirstOrDefault(s => s.Name.Equals("Mike Miles"));
      
      var age = 28;
      
      var rowsAffected = _context.Database
          .ExecuteSqlRaw(@"UPDATE Student 
                             SET Age = {0} 
                             WHERE Name = {1}", age, studentForUpdate.Name);
      
      return Ok(new { RowsAffected = rowsAffected});
      
      As soon as we execute  this query, the Age column will change to 28, but let’s see what is going to happen with the studentForUpdate loaded object: ExecuteSqlCommand not changed local entity - Queries in Entity Framework Core There it is, the Age property hasn’t changed even though it has been changed in the database. Of course, this is the expected behavior. So now, the question is: „What if we want it to change after the execution of the ExecuteSqlRaw method?“. Well, to accomplish that, we have to use the Reload method:
      var rowsAffected = _context.Database
          .ExecuteSqlRaw(@"UPDATE Student 
                             SET Age = {0} 
                             WHERE Name = {1}", age, studentForUpdate.Name);
      
      _context.Entry(studentForUpdate).Reload();
      
      Now, when we execute the code again: Reload method - Queries in Entity Framework Core The age property, of the loaded entity, is changed.

      Conclusion

      We did a great job here. We’ve covered a lot of topics and learned a lot about queries in Entity Framework Core. So, to summarize, we have learned:
      • How queries work in EF Core
      • About different query types and how to use each of them
      • The way to use Raw SQL commands with different EF Core methods
      In the next article, we are going to learn about EF Core actions that will modify the data in the database. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48669 0 0 0
      Modifying Data with Entity Famework Core https://code-maze.com/efcore-modifying-data/ Mon, 02 Sep 2019 06:00:52 +0000 https://code-maze.com/?p=48711 the source code for this article on our GitHub repository. To see all the basic instructions and complete navigation for this series, visit Entity Framework Core with ASP.NET Core Tutorial. This is what we are going to learn about:

      ChangeTracker and State of the Entity in Entity Framework Core

      Before we start modifying data with Entity Framework Core, we have to be familiar with some additional EF Core’s features. As we learned, in the first part of the series, DbContext consists of only three properties: ChangeTracker, Database, and Model. We have seen the Model and Database properties in action in previous articles, so in this article, we are going to talk about the ChangeTracker and State properties. The ChangeTracker property provides access to change tracking information and operations about the currently loaded entity. This ability is very important when we want to execute any of the database modification operations. EF Core has this kind of information(about tracking and operations) whether we create, modify it or delete an entity. Furthermore, EF Core won’t execute any operation until we call the SaveChanges method. So knowing what operation we want to execute is crucial to EF Core prior to calling the SaveChanges method. Every tracked entity has the State property attached to it. When we use the context object to load entity without the AsNoTracking method or we change its state via the Update, Remove or Add methods, that entity will be the tracked entity. The value of the State property can be obtained with the _context.Entry(our_entity).State command. Here are the possible states of tracked entities:
      • Detached – The entity isn’t tracked and calling the SaveChanges method won’t have any effect
      • Unchanged – The entity is loaded from the database but has no changes. The SaveChanges method ignores it
      • Added – The entity doesn’t exist in the database and calling the SaveChanges method will add it to the database
      • Modified – The entity exists in the database and has been modified, therefore, calling the SaveChanges method will modify it in the database
      • Deleted – The entity exists in the database and as soon as we call the SaveChanges method it will be removed from the database
      Now, when we know all this stuff, we can continue towards the Create, Update and Delete operations.

      Creating Rows – Modifying Data with Add and AddRange methods

      We can create new rows in the database by using the Add and AddRange methods. The Add method will modify the State of the single entity where the AddRange can do the same but for the multiple entities. Let’s see the Add method in action after we send the student object from Postman:
      [HttpPost]
      public IActionResult Post([FromBody] Student student)
      {
          if (student == null)
              return BadRequest();
      
          if (!ModelState.IsValid)
              return BadRequest();
      
          _context.Add(student);
          _context.SaveChanges();
      
          return Created("URI of the created entity", student);
      }
      
      In this action, we use the HttpPost attribute to mark it as a POST action. Additionally, we load the student entity with the FromBody attribute and add two validation checks in our code. All of these are ASP.NET Core related, so if you are not familiar with these concepts we strongly suggest visiting the ASP.NET Core Web API – Post, Put, Delete article. This article will also help you if you are not familiar with the process of sending the POST requests from Postman. After these checks, we call the Add method to change the state of the entity to Added and call the SaveChanges method to create a new row in the database. The last line of code will return the created entity (with its id) to the client: New Added entity - Modifying Data This is the result in the database: Database result

      Tracking Changes when Adding an Entity

      If we modify our code slightly, we can inspect the way that the entity’s state is changed during this action. Of course, the additional code is not required to save the entity to the database, it is here just for the learning purposes:
      [HttpPost]
      public IActionResult Post([FromBody] Student student)
      {
          if (student == null)
              return BadRequest();
      
          if (!ModelState.IsValid)
              return BadRequest();
      
          var stateBeforeAdd = _context.Entry(student).State;
      
          _context.Add(student);
      
          var stateAfterAdd = _context.Entry(student).State;
      
          _context.SaveChanges();
      
          var stateAfterSaveChanges = _context.Entry(student).State;
      
          return Created("URI of the created entity", student);
      }
      
      state check while adding an entity - Modifying data As we can see, before we add our entity to the db it has the Detached state. As soon as we use the Add method, it has the Added state. Finally, after the SaveChanges method, it has the Unchanged state.

      Using the AddRange Method

      When we want to add multiple rows in the database, we use the AddRange method. It is the same procedure just a different method:
      [HttpPost("postrange")]
      public IActionResult PostRange([FromBody] IEnumerable<Student> students)
      {
          //additional checks
      
          _context.AddRange(students);
          _context.SaveChanges();
      
          return Created("URI is going here", students);
      }
      
      Another important thing to mention here is that the SaveChanges method executes the required operation but it also returns the number of affected rows. So if we need to check how many rows are affected with our operation, we can always return the result of the SaveChanges method: var result = _context.SaveChanges();

      Adding Related Entities to the Database

      Now we are going to learn how to include relationships while adding the main entity to the database. To demonstrate that, we are going to modify our first example a bit:
      [HttpPost]
      public IActionResult Post([FromBody] Student student)
      {
          //validation code goes here
      
          student.StudentDetails = new StudentDetails
          {
              Address = "Added Address",
              AdditionalInformation = "Additional information added"
          };
      
          _context.Add(student);
          _context.SaveChanges();
      
          return Created("URI of the created entity", student);
      }
      
      As we can see from the code above, we are only adding the student object to the application’s context object but the studentDetails object is also added to the database: Add method with relationship - Modifying data And we can check the response from the application: response for the add method It is important to notice that we are not adding Id value manually for any entity, EF Core is in charge of that. You can see in the database that the GUID values have been generated. Excellent. We can do the same thing with one-to-many or many-to-many relationships.

      Updating Rows in the Database

      There are two ways to update rows in the database. With Connected Update and with Disconnected Update. The difference is that with the connected update we use the same context object to load and modify entity. With the disconnected update, this is not the case. Either we use different context objects or we receive an object from a client that has all the properties as the entity in db, so we can update it without loading action first. While working with the connected update there are three main steps to execute:
      • Read data from the database (with or without its relationships)
      • Change one or more properties
      • Call the SaveChanges method
      In the following examples, we are going to leave out the additional validations checks (for the sake of simplicity) because the validation is related to ASP.NET Core and you can read more about that in ASP.NET Core Web API – Post, Put, Delete article. So, let’s see the connected update in action when we send the student object from a client with the Name property updated:
      [HttpPut("{id}")]
      public IActionResult PUT (Guid id, [FromBody] Student student)
      {
          var dbStudent = _context.Students
              .FirstOrDefault(s => s.Id.Equals(id));
      
          dbStudent.Age = student.Age;
          dbStudent.Name = student.Name;
          dbStudent.IsRegularStudent = student.IsRegularStudent;
      
          _context.SaveChanges();
      
          return NoContent();
      }
      
      We can see all three steps in the code above. We load the student object based on its id, then map the properties from the client’s updated student, which sets the state of the entity to the Modified and finally, we call SaveChanges. Even though we have changed only the Name property, we have to map all the properties because we don’t know what has been modified on the client-side exactly. EF Core, on the other hand, has the information on what exactly has been modified. When we load our entity, EF Core starts tracking it and at that point the State is Unchanged. Once we modify any property from a loaded entity it changes the State to Modified. Finally, after the SaveChanges method, the State is reverted to Unchanged. Once we send a request to this PUT action, the student’s name is going to be updated: Update actions connected We can see that only the Name property has been updated. If you can't see both commands in the console window, you can find them in the Output window inside Visual Studio for sure.

      IsModified Property

      When we have an entity that is already in the Modified state, EF Core uses another property that provides information about what really changed. This is the IsModified property and we can examine how it works by slightly modifying our code:
      [HttpPut("{id}")]
      public IActionResult PUT (Guid id, [FromBody] Student student)
      {
          var dbStudent = _context.Students
              .FirstOrDefault(s => s.Id.Equals(id));
      
          dbStudent.Age = student.Age;
          dbStudent.Name = student.Name;
          dbStudent.IsRegularStudent = student.IsRegularStudent;
      
          var isAgeModified = _context.Entry(dbStudent).Property("Age").IsModified;
          var isNameModified = _context.Entry(dbStudent).Property("Name").IsModified;
          var isIsRegularStudentModified = _context.Entry(dbStudent).Property("IsRegularStudent").IsModified;
      
          _context.SaveChanges();
      
          return NoContent()
      }
      
      IsModified property The result is pretty self-explanatory here.

      Updating Relationships in EF Core

      Adding relationships to the update operations in EF Core is pretty easy. We can attach a relational entity to the main entity, modify it and EF Core will do the rest for us as soon as we call the SaveChanges method. The procedure is the same as we did it for the create actions. Let’s see how to update relationship in EF Core:
      [HttpPut("{id}/relationship")]
      public IActionResult UpdateRelationship(Guid id, [FromBody] Student student)
      {
          var dbStudent = _context.Students
              .Include(s => s.StudentDetails)
              .FirstOrDefault(s => s.Id.Equals(id));
      
          dbStudent.Age = student.Age;
          dbStudent.Name = student.Name;
          dbStudent.IsRegularStudent = student.IsRegularStudent;
          dbStudent.StudentDetails.AdditionalInformation = "Additional information updated";
      
          _context.SaveChanges();
      
          return NoContent();
      }
      
      When we send our request form Postman we are going to update the student entity and the studentDetails entity: Update relationships sql result The same process applies to other relationship types. The important thing to know is when we update our main entity by adding a new relationship entity, EF Core is going to create a new row in the relationship table and connects it to the main entity with a foreign key:
      [HttpPut("{id}/relationship")]
      public IActionResult UpdateRelationhip(Guid id, [FromBody] Student student)
      {
          var dbStudent = _context.Students
              .FirstOrDefault(s => s.Id.Equals(id));
      
          dbStudent.StudentDetails = new StudentDetails
          {
              Id = new Guid("e2a3c45d-d19a-4603-b983-7f63e2b86f14"),
              Address = "added address",
              AdditionalInformation = "Additional information for student 2"
          };
      
          _context.SaveChanges();
      
          return NoContent();
      }
      
      Update relationships sql result for insert Since now we know how to update relationships in EF Core, we can continue on to the disconnected update.

      Disconnected Update in EF Core

      There are several ways to execute a disconnected update, and we are going to show you two ways of doing that. In the first example, we are going to attach the object sent from a client, modify its state and then save it to the database:
      [HttpPut("disconnected")]
      public IActionResult UpdateDisconnected([FromBody] Student student)
      {
          _context.Students.Attach(student);
          _context.Entry(student).State = EntityState.Modified;
      
          _context.SaveChanges();
      
          return NoContent();
      }
      
      The student object sent from the client has a Detached state at a beginning. After we use the Attach method the object is going to change state to Unchanged. This also means that as of this moment, EF Core starts tracking the entity. Now, we are going to change the state to Modified and save it to the database. This is the object sent from the client:
      {
          "id": "DF75D335-A0EA-4798-8D8E-D7BF940F4D1D",
          "name": "Student updated 3",
          "age": "22",
          "isRegularStudent": false
      }
      
      And as you can see it has the Id property as well. Additionally, we have changed the Name and IsRegularStudent properties, but EF Core will update the entire object in the database. Another way of doing the same thing is by using the Update or UpdateRange method if we have multiple objects ready for an update. So let’s send the same object with only the IsRegularStudent property modified to true:
      [HttpPut("disconnected")]
      public IActionResult UpdateDisconnected([FromBody] Student student)
      {
          _context.Update(student);
      
          _context.SaveChanges();
      
          return NoContent();
      }
      
      We can see the difference. The Update method will set the entity to tracked and also modify its state from Detached to Modified. So, we don’t have to attach the entity and to modify its state explicitly because the Update method does that for us. This approach is also going to update the entire object even though we changed just one property: Disconnected update command There we go. Now we know how to execute a disconnected update and what is the difference from a connected update.

      Delete Operations in EF Core

      The last way to modify data is by using delete actions. We have two ways of deleting rows and we are going to cover them both. So, let’s start with the Soft Delete approach.

      Soft Delete in Entity Framework Core

      With the Soft Delete approach, the entity is hidden rather than deleted. This is not an unusual way of doing delete actions because in many real-world projects we don’t want to really delete any row because they will be required for some statistics later on. So basically, we are not deleting an entity we are updating it by modifying its property (we are going to name it Deleted) to true. To show how soft delete works, we need to make some changes in our current database model. To start, let’s modify the Student model by adding additional property:
      public bool Deleted { get; set; }
      Now, let’s modify the StudentConfiguration class, by adding an additional method to filter out all the Deleted students from the queries:
      builder.HasQueryFilter(s => !s.Deleted);
      The HasQueryFilter method will include only those Student entities with the Deleted property set to false. Or in other words, it will ignore all the students with Deleted set to true. To apply these changes to the database, we are going to create and apply migration:
      PM> Add-Migration AddedDeletedPropertyToStudent
      PM> Update-Database
      
      After this migration, all the rows in the Student table will have the Deleted column set to zero (false). To continue on, let’s create additional Delete action in the controller and send the Delete request:
      [HttpDelete("{id}")]
      public IActionResult Delete(Guid id)
      {
          var student = _context.Students
              .FirstOrDefault(s => s.Id.Equals(id));
      
          if (student == null)
              return BadRequest();
      
          student.Deleted = true;
          _context.SaveChanges();
      
          return NoContent();
      }
      
      The soft delete consists of three parts:
      • Loading part
      • Modifying part
      • Saving part
      This is the result in the database: Soft delete sql result - Modifying data

      Loading Data with QueryFilter implemented and Ignoring QueryFilter

      With the HasQueryFilter method we’ve made sure that “deleted” entities won’t be included in the query result. Let’s see if it works:
      var studentsWithoudDeleted = _context.Students.ToList();
      EF Core would usually return all the students from the Student table. But with the HasQueryFilter method implemented, the result is different: HasQueryFilterImplemented We can confirm that the “Student updated 3” is missing, which is the only one with the Deleted property set to true. Of course, if we want to ignore our query filter we can do that by applying the IgnoreQueryFilter to the query:
      var studentsWithDeleted = _context.Students
          .IgnoreQueryFilters()
          .ToList();
      
      This query will include the “deleted” entity: IgnoreQueryFIlter Implemented - Modifying data Great job. Let’s continue with the regular Delete.

      Delete a Single Entity with EF Core

      In the regular delete, we are not modifying our entity but actually removing it from the database by using the Remove method or RemoveRange method for multiple entities:
      [HttpDelete("{id}")]
      public IActionResult Delete(Guid id)
      {
          var student = _context.Students
              .FirstOrDefault(s => s.Id.Equals(id));
      
          if (student == null)
              return BadRequest();
      
          _context.Remove(student);
          _context.SaveChanges();
      
          return NoContent();
      }
      
      There are three steps for this action as well:
      • Loading the student entity and set its state to Unchanged
      • Using the Remove method to set the state to Deleted
      • Saving the changes in the database
      We must be careful with Delete actions if our entity has relationships and we haven’t specified them in the delete action. Deleting the main entity could delete the relationships as well (cascade delete), depending on the entity configuration. We have talked about configuring delete actions in the Entity Framework Core Relationships article. This is the translated SQL command: Delete Single Entity We can see that the first Select statement includes the query filter. Right after the filter there is our Delete query.

      Delete Entity with Relationships

      To delete an entity with its relationships, all we have to do is to include that relationship into the main entity:
      [HttpDelete("{id}/relationship")]
      public IActionResult DeleteRelationships(Guid id)
      {
          var student = _context.Students
              .Include(s => s.StudentDetails)
              .FirstOrDefault(s => s.Id.Equals(id));
      
          if (student == null)
              return BadRequest();
      
          _context.Remove(student);
          _context.SaveChanges();
      
          return NoContent();
      }
      
      And that is all we have to do. EF Core will do the rest for us.

      Conclusion

      Excellent. We have covered a lot of topics related to Entity Framework Core and its usage in the ASP.NET Core. In this series, we’ve learned how to integrate EF Core in the ASP.NET Core app and how to set up non-relational and relational configuration. Furthermore, we have learned to use Migrations, Queries, and Database modification operations. We hope that you have enjoyed reading it and that you have found a lot of useful information. Until the next series… [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48711 0 0 0
      Unit Testing with xUnit in ASP.NET Core MVC https://code-maze.com/unit-testing-with-xunit-in-asp-net-core-mvc/ Mon, 16 Sep 2019 06:05:45 +0000 https://code-maze.com/?p=48759 the source code for the starting project on our GitHub repository. We have also provided the source code for the finished project for this article. For the complete navigation of this series, you can visit ASP.NET Core MVC Testing. These are the topics we are going to cover: So, let’s dive right into it.

      Overview of the xUnit Tool

      xUnit is a free, open-source, testing tool for .NET which developers use to write tests for their applications. It is essentially a testing framework which provides a set of attributes and methods we can use to write the test code for our applications. Some of those attributes, we are going to use are:
      • [Fact] – attribute states that the method should be executed by the test runner
      • [Theory] - attribute implies that we are going to send some parameters to our testing code. So, it is similar to the [Fact] attribute, because it states that the method should be executed by the test runner, but additionally implies that we are going to send parameters to the test method
      • [InlineData] – attribute provides those parameters we are sending to the test method. If we are using the [Theory] attribute, we have to use the [InlineData] as well
      As we said, xUnit provides us with a lot of assertion methods we use to validate our production code. As we progress through this series, we are going to use different assertion methods to test different production functionalities. Once we write our test method, we need to run it to be sure whether it works or not. For that purpose, we are going to use the Visual Studio’s Test Explorer, which we can open by opening the Test menu and then Windows > Test Explorer. We can use a keyboard shortcut as well: CTRL+E, T.

      Quick Overview of the Starting Project

      We have created a starting project to start this series off faster. We strongly recommend downloading and using it in the rest of the series. So, let’s just have a quick project overview: Project Overview - Unit Testing with xUnit We can see that we have a repository class for the repository logic with its IEmployeeRepository interface. This will be quite important for us once we start writing tests for our controller in future articles. Our controller contains three actions, one for the GET request and two for the POST request. We can see views for Index and Create actions as well. Finally, we can see the Migrations folder, which contains migration files for our series. So, in order for you to use the prepared data, you have to change a connection string in the appsettings.json file and just run the project. It will automatically create a database and seed all the required data. Now, that we are familiar with the starting project, we can move onto the next phase by adding an additional class with validation logic.

      Adding Validation Functionality in Our Project

      Before we start, let’s take a look at our Employee entity class:
      [Table("Employee")]
      public class Employee
      {
          public Guid Id { get; set; }
      
          [Required(ErrorMessage = "Name is required")]
          public string Name { get; set; }
      
          [Required(ErrorMessage = "Age is required")]
          public int Age { get; set; }
      
          [Required(ErrorMessage = "Account number is required")]
          public string AccountNumber { get; set; }
      }
      
      And the HttpPost action in the controller class:
      [HttpPost]
      [ValidateAntiForgeryToken]
      public IActionResult Create([Bind("Name,AccountNumber,Age")] Employee employee)
      {
          if(!ModelState.IsValid)
          {
              return View(employee);
          }
      
          _repo.CreateEmployee(employee);
          return RedirectToAction(nameof(Index));
      }
      
      In the Create action, we are adding a new employee object to the database if the model is valid. But now, we have decided to add additional validation for the AccountNumber property. Next, we need to create a new validation class and, after that, write tests for each validation rule inside that class. So, let’s start by adding a new folder named Validation and inside it a new class AccountNumberValidation:
      public class AccountNumberValidation
      {
          private const int startingPartLength = 3;
          private const int middlePartLength = 10;
          private const int lastPartLength = 2;
      
          public bool IsValid(string accountNumber)
          {
              var firstDelimiter = accountNumber.IndexOf('-');
              var secondDelimiter = accountNumber.LastIndexOf('-');
      
              if(firstDelimiter == -1 || secondDelimiter == -1) 
                  throw new ArgumentException();
      
              var firstPart = accountNumber.Substring(0, firstDelimiter);
              if (firstPart.Length != startingPartLength)
                  return false;
      
              var tempPart = accountNumber.Remove(0, startingPartLength + 1);
              var middlePart = tempPart.Substring(0, tempPart.IndexOf('-'));
              if (middlePart.Length != middlePartLength)
                  return false;
      
              var lastPart = accountNumber.Substring(secondDelimiter + 1);
              if (lastPart.Length != lastPartLength)
                  return false;
      
              return true;
         } 
      }
      
      So, we want to ensure that the AccountNumber consists of three parts with different lengths (3, 10 and 2). Also, we want to ensure that those parts are divided by the minus sign separator. That being said, we can see that if delimiters are invalid we are throwing an exception. If any of the AccountNumber parts is invalid, we return false. Finally, if everything goes well, we return true. At first glance, this looks great and our validations are up to the task. But, let’s test those validation rules and make sure that everything works as expected.

      Preparing the Testing Project

      Let’s start by creating a new xUnit Test Project and naming it EmployeesApp.Tests: Project creation - Unit Testing with xUnit A new project will prepare a single test class for use, named UnitTest1.cs and will have installed xUnit library and xUnit runner as well: xUnit library installed We can remove UnitTest1 class, add a new folder Validation and create a new class AccountNumberValidationTests in it: xUnit project structure - Unit Testing with xUnit Since we want to test the validation logic from the main project, we have to add its reference to the testing. After we have done these preparations, we are ready to write some tests. If you like, you can add a new test project from the command window as well. All you have to do is to open your cmd window next to the main project’s solution file and type following commands: mkdir EmployeesApp.Tests – to create a new folder cd EmployeesApp.Tests – to navigate to the new folder dotnet new xUnit – to create the xUnit project with the same name as the parent folder

      Unit Testing with xUnit

      So, let’s modify the AccountNumberValidationTests class:
      public class AccountNumberValidationTests
      {
          private readonly AccountNumberValidation _validation;
      
          public AccountNumberValidationTests()
          {
              _validation = new AccountNumberValidation();
          }
      
          [Fact]
          public void IsValid_ValidAccountNumber_ReturnsTrue()
          {
              Assert.True(_validation.IsValid("123-4543234576-23"));
          }
      }
      
      We are going to use the _validation object with all the test methods in this class. Therefore, the best way is to create it in a constructor, and then just use it when we need it. By doing so, we prevent the repetition of instantiating the _validation object. Below the constructor, we can see our first test method decorated with the [Fact] attribute. Pay attention to the naming convention we use for test methods:   [MethodWeTest_StateUnderTest_ExpectedBehavior] The method’s name implies that we are testing a valid account number and that the test method should return true. We can achieve that by using the Assert class and the True method which verifies that the expression inside it returns true. For the expression, we call the IsValid method from the AccountNumberValidation class and pass a valid account number. Now we can run the Test Explorer and verify if our test passes: FIrst test result We can see that this test passes in the class itself: First test result passes in the code Works great. Let’s move on.

      Theory and InlineData

      In the AccountNumberValidation class, the IsValid method contains validations for the first, middle and last part of the account number. Therefore, we are going to write tests for all these situations. Let’s start with the test where the first part is wrong:
      [Fact]
      public void IsValid_AccountNumberFirstPartWrong_ReturnsFalse()
      {
          Assert.False(_validation.IsValid("1234-3454565676-23"));
      }
      
      We expect our test to return false if we have a wrong account number. Therefore we are using the False() method with the provided expression. Of course, to verify this, we have to use the Test Explorer: First account part wrong test passes We can see that the test passes. But now, if we want to test an account number with 2 digits for the first part (we tested just with 4 digits), we would have to write the same method again just with a different account number. Obviously, this is not the best scenario. To improve that, we are going to modify this test method by removing the [Fact] attribute and adding the [Theory] and [InlineData] attributes:
      [Theory]
      [InlineData("1234-3454565676-23")]
      [InlineData("12-3454565676-23")]
      public void IsValid_AccountNumberFirstPartWrong_ReturnsFalse(string accountNumber)
      {
          Assert.False(_validation.IsValid(accountNumber));
      }
      
      Now, let’s check the result: Theory attribute in tests Even though we have only two test methods, the test runner runs three tests. One test for the first test method and two tests for each [InlineData] attribute.

      Additional Tests

      Now when we know how to use the [Theory] and [InlineData] attributes, let’s write additional tests for our account number:
      [Theory]
      [InlineData("123-345456567-23")]
      [InlineData("123-345456567633-23")]
      public void IsValid_AccountNumberMiddlePartWrong_ReturnsFalse(string accNumber)
      {
          Assert.False(_validation.IsValid(accNumber));
      }
      
      [Theory]
      [InlineData("123-3434545656-2")]
      [InlineData("123-3454565676-233")]
      public void IsValid_AccountNumberLastPartWrong_ReturnsFalse(string accNumber)
      {
          Assert.False(_validation.IsValid(accNumber));
      }
      
      There is nothing new in the code above (except different parameters), so we can run the test runner right away: Additional tests Excellent! One more test to go.

      Testing Exceptions

      In the IsValid method, we verify that both delimiters should be minus signs. If this is not the case, we throw an exception. So, let’s write a test for that:
      [Theory]
      [InlineData("123-345456567633=23")]
      [InlineData("123+345456567633-23")]
      [InlineData("123+345456567633=23")]
      public void IsValid_InvalidDelimiters_ThrowsArgumentException(string accNumber)
      {
          Assert.Throws<ArgumentException>(() => _validation.IsValid(accNumber));
      }
      
      We test three different situations here when the second delimiter is wrong, when the first delimiter is wrong, and when both of them are wrong. To test an exception, we have to use the Throws<T> method with the exception type as a T value. Note that, we are using a lambda expression inside the Throws method which is a little different from what we have used before. Having done this, let’s check the result: Last tests fail Well, would you look at that! Our test has failed. To be more precise, two of them have failed and one has passed. So this means that our validation check in the IsValid method is wrong. And now, we see why tests are so important. Even though the code looked like a good one at first glance, now we can see that it is not that good. So, let’s fix it:
      var firstDelimiter = accountNumber.IndexOf('-');
      var secondDelimiter = accountNumber.LastIndexOf('-');
      
      if (firstDelimiter == -1 || (firstDelimiter == secondDelimiter))
          throw new ArgumentException();
      
      Great! Now, let’s run the test again: Last test passes Excellent! Everything is working well.

      Conclusion

      So, this brings us to the end of the first article in the series. We have learned how to create the xUnit project and how to use [Fact], [Theory] and [InlineData] attributes. Also, we have created several tests to test our validation logic from the AccountNumberValidation class. But this is just a beginning. In the next article, we are going to learn how to test our controller class and how to use mocked objects with the testing code. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48759 0 0 0
      Testing MVC Controllers in ASP.NET Core https://code-maze.com/testing-mvc-controllers-asp-net-core/ Mon, 23 Sep 2019 06:00:19 +0000 https://code-maze.com/?p=48796 previous article, we have learned how to write Unit Tests by using the xUnit and different attributes that xUnit provides for us. We have seen how to test validation rules inside a single validation class. But what about controllers and all the actions inside? Can we write tests for them too? Sure we do. In this article, we are going to explain how to do that. You can download the source code on our GitHub repository. For the complete navigation of this series, you can visit ASP.NET Core MVC Testing. These are the topics we are going to cover: So, let’s get going.

      Using Moq Library to Create Mock Objects While Testing MVC Controllers

      Before we start, let’s take a look at the EmployeesController’s constructor code: DI - Testing MVC Controllers As you can see, we are using Dependency Injection to inject the interface in our controller. So basically, our controller has a dependency on the repository logic through that injected interface. And there is no problem with that approach at all, it is even recommended. But when we write tests for our controller or any other class in a project, we should isolate those dependencies. There are several advantages from isolating dependencies in a test code:
      • We don’t have to initialize all dependencies to return correct values, thus making our test code much simplified
      • If our test fails and we didn’t isolate dependency, we can’t be sure whether it fails due to some error in a controller or in that dependency
      • When dependent code communicates with a real database, as our repository does, the test code could take more time to execute due to connection issues or simply the process of data fetching is taking its
      Of course, there are additional reasons to isolate dependencies in test code, but you get the point. That being said, let’s install the Moq library in the EmployeesApp.Tests project: Moq library - Testing MVC Controllers After the installation completes, we are going to create a new Controller folder in the same project and add EmployeesControllerTests class: Controller Structure

      Creating a Mock Object

      Let’s modify the EmployeesControllerTests class:
      public class EmployeesControllerTests
      {
          private readonly Mock<IEmployeeRepository> _mockRepo;
          private readonly EmployeesController _controller;
      
          public EmployeesControllerTests()
          {
              _mockRepo = new Mock<IEmployeeRepository>();
              _controller = new EmployeesController(_mockRepo.Object);
          }
      }
      
      We create a mock object of type IEmployeeRepository inside the constructor, and since we want to test the controller logic, we create an instance of that controller with the mocked object as a required parameter. And there you go. Everything is prepared, a dependency is mocked, so all we have to do is to write some tests.

      Testing Index Action

      If we take a look at the Index action in the EmployeesController class, we can see that we fetch all employees from the database and return a view with those employees:
      public IActionResult Index()
      {
          var employees = _repo.GetAll();
          return View(employees);
      }
      
      As a result, we can write a couple of tests to verify that this action is doing exactly what it is supposed to do. In the first test, we are going to verify that the Index action returns a result of type ViewResult:
      [Fact]
      public void Index_ActionExecutes_ReturnsViewForIndex()
      {
          var result = _controller.Index();
          Assert.IsType<ViewResult>(result);
      }
      
      So, we are executing the Index action from our controller and accept the result inside the result variable. After that, we check the type of the returned result with the IsType method. If the result is of type ViewResult the test will pass, otherwise, it will fail. Let’s run the Test Explorer window and find out the result: Index of type ViewResult test passes We can see that our test passes and that the result is of ViewResult type. Let’s write another test method to verify that our Index action returns an exact number of employees:
      [Fact]
      public void Index_ActionExecutes_ReturnsExactNumberOfEmployees()
      {
          _mockRepo.Setup(repo => repo.GetAll())
              .Returns(new List<Employee>() { new Employee(), new Employee() });
      
          var result = _controller.Index();
      
          var viewResult = Assert.IsType<ViewResult>(result);
          var employees = Assert.IsType<List<Employee>>(viewResult.Model);
          Assert.Equal(2, employees.Count);
      }
      
      In the previous test method, we didn’t use a mocked object because we didn’t use any of our repository methods. We have just checked the type of our result. In this test method, we are fetching the data from the database by using the GetAll repository method. Of course, we don’t want to use the concrete repository but the mocked one and therefore we use the Setup method to specify a setup for the GetAll method. Additionally, we have to use the Returns method to specify the value to return from the mocked GetAll method. After we store the result of the Index action, we check the type of that result, the type of the model object inside that result and finally the number of employees by using the Equal method. All three verifications have to pass in order to pass the test, otherwise, the test will fail. Let’s see this test in action: Index returns exact number of employees The test indeed passes and we have verified that it returns exactly two employees.

      Testing Create Actions

      We have two Create actions in our EmployeesController class, the GET, and the POST action. The first action just loads the Create View and that is something we have to test. So, let’s do it:
      [Fact]
      public void Create_ActionExecutes_ReturnsViewForCreate()
      {
          var result = _controller.Create();
      
          Assert.IsType<ViewResult>(result);
      }
      
      We already had a test like this, just with the Index action, so, there is nothing new about it. If we run Test Explorer we can verify that test passes: Create action returns view - Testing MVC Controllers Let’s move on to the second Create action, the POST one. In that action, we have a model validation and if it is invalid we return a view with the employee object. So let’s test that:
      [Fact]
      public void Create_InvalidModelState_ReturnsView()
      {
          _controller.ModelState.AddModelError("Name", "Name is required");
      
          var employee = new Employee { Age = 25, AccountNumber = "255-8547963214-41" };
      
          var result = _controller.Create(employee);
      
          var viewResult = Assert.IsType<ViewResult>(result);
          var testEmployee = Assert.IsType<Employee>(viewResult.Model);
          Assert.Equal(employee.AccountNumber, testEmployee.AccountNumber);
          Assert.Equal(employee.Age, testEmployee.Age);
      } 
      
      We have to add a model error to the ModelState property in order to test an invalid model state. After that, we create a new employee without the Name property, which makes it invalid as well. Finally, we call the create action and execute a couple of assertions. With assert statements, we verify that the result is of type ViewResult and that the model is of the Employee type. Additionally, we are making sure that we get the same employee back by comparing property values from the testEmployee and the employee objects: Create action Invalid Model State

      Additional Invalid Model Test

      Let’s write one additional test to verify that the CreateEmployee method, from our repository, never executes if the model state is invalid:
      [Fact]
      public void Create_InvalidModelState_CreateEmployeeNeverExecutes()
      {
          _controller.ModelState.AddModelError("Name", "Name is required");
      
          var employee = new Employee { Age = 34 };
      
          _controller.Create(employee);
      
          _mockRepo.Verify(x => x.CreateEmployee(It.IsAny<Employee>()), Times.Never);
      }
      
      The first three code lines are the same as in the previous test method, we add a model error, create an invalid employee object and call the Create action from our controller. Of course, in that action, we have the CreateEmployee method which shouldn’t be executed if the model is invalid. That is exactly what we verify with the Verify method from the mocked object. By using the It.IsAny<Employee> expression, we state that it doesn’t matter which employee is passed as a parameter to the CreateEmployee method. The only important thing is that the parameter is of the Employee type. The last parameter of the Verify method is the number of times our method executes. In this situation we use Times.Never because we don’t want the CreateEmployee method to execute at all if the model is invalid. Back to Test Explorer: Create action Invalid Model State CreateEmployee never executes As you can see, the test passes as expected. If we modify the test code by placing the Times.Once instead of Times.Never the test will fail for sure. You might want to try it yourself: Create action Invalid Model State CreateEmployee never executes test fail The message explains pretty well what is the problem and why the test fails.

      Testing if Model is Valid in the Create Action

      If the Model State is valid the CreateEmployee method should be executed just once:
      public void Create_ModelStateValid_CreateEmployeeCalledOnce()
      {
          Employee emp = null;
          _mockRepo.Setup(r => r.CreateEmployee(It.IsAny<Employee>()))
              .Callback<Employee>(x => emp = x);
      
          var employee = new Employee
          {
              Name = "Test Employee",
              Age = 32,
              AccountNumber = "123-5435789603-21"
          };
      
          _controller.Create(employee);
      
          _mockRepo.Verify(x => x.CreateEmployee(It.IsAny<Employee>()), Times.Once);
      
          Assert.Equal(emp.Name, employee.Name);
          Assert.Equal(emp.Age, employee.Age);
          Assert.Equal(emp.AccountNumber, employee.AccountNumber);
      }
      
      So, we set up the CreateEmployee method with any employee and use the Callback method to populate the emp object with the values from the employee parameter. After that, we create a local employee object, execute the Create action with that employee and Verify if the CreateEmployee method has been executed just once. Additionally, we verify that the emp object is the same as the employee object provided to the Create action. Finally, let’s run that in the Test Explorer: Create action Valid Model State CreateEmployee called once One more test to go:
      [Fact]
      public void Create_ActionExecuted_RedirectsToIndexAction()
      {
          var employee = new Employee
          {
              Name = "Test Employee",
              Age = 45,
              AccountNumber = "123-4356874310-43"
          };
      
          var result = _controller.Create(employee);
      
          var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
          Assert.Equal("Index", redirectToActionResult.ActionName);
      }
      
      If we take a look at the Create action in our controller, we are going to see that after creating a new employee, we redirect a user to the Index action. Well, that is exactly what we’ve successfully tested in our test method.

      Conclusion

      That is it. We have used the Index and Create actions to show you how to test our controller and actions inside it, but all the rules can be applied on other types of Actions (PUT, DELETE...). So, to sum up, we have learned:
      • How to use the Moq library to isolate dependency in the test code
      • To write different tests for our actions inside a controller
      In the next part, we are going to talk about integration tests and how to create an in-memory database. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48796 0 0 0 1501 0 0 1502 1501 0 1923 0 0 1925 1923 0
      Integration Testing in ASP.NET Core MVC https://code-maze.com/integration-testing-asp-net-core-mvc/ Mon, 30 Sep 2019 06:00:15 +0000 https://code-maze.com/?p=48903 the source code on our GitHub repository. For the complete navigation of this series, you can visit ASP.NET Core MVC Testing. These are the topics we are going to cover: Let’s move on.

      Preparing a new Project for Integration Testing

      First, we are going to create a new xUnit project named EmployeesApp.IntegrationTests for integration testing purposes. After the project creation, we are going to rename the UnitTest1.cs class to EmployeesControllerIntegrationTests: Integration tests project Additionally, we are going to add a reference to the main project and install a single NuGet package required for the testing purposes:
      • AspNetCore.Mvc.Testing – this package provides the TestServer and an important class WebApplicationFactory to help us bootstrap our app in-memory
      • Microsoft.EntityFrameworkCore.InMemory - In-memory database provider
      Now we can continue on.

      Creating In-Memory Factory Configuration

      Let’s create a new class TestingWebAppFactory and modify it accordingly:
      public class TestingWebAppFactory<T> : WebApplicationFactory<Startup>
      {
          protected override void ConfigureWebHost(IWebHostBuilder builder)
          {
              builder.ConfigureServices(services =>
              {
      
                  var descriptor = services.SingleOrDefault(
                    d => d.ServiceType ==
                       typeof(DbContextOptions<EmployeeContext>));
      
                  if (descriptor != null)
                  {
                      services.Remove(descriptor);
                  }
      
                  var serviceProvider = new ServiceCollection()
                    .AddEntityFrameworkInMemoryDatabase()
                    .BuildServiceProvider();
      
                  services.AddDbContext<EmployeeContext>(options =>
                  {
                      options.UseInMemoryDatabase("InMemoryEmployeeTest");
                      options.UseInternalServiceProvider(serviceProvider);
                  });
      
                  var sp = services.BuildServiceProvider();
      
                  using (var scope = sp.CreateScope())
                  {
                      using (var appContext = scope.ServiceProvider.GetRequiredService<EmployeeContext>())
                      {
                          try
                          {
                              appContext.Database.EnsureCreated();
                          }
                          catch (Exception ex)
                          {
                              //Log errors or do anything you think it's needed
                              throw;
                          }
                      }
                  }
              });
          }
      }
      
      A couple of things to mention here. Our class implements the WebApplicationFactory<Startup> class and overrides the ConfigureWebHost method. In that method, we remove the EmployeeContext registration from the Startup.cs class. Then, we are adding Entity Framework in-memory database support to the DI container via the ServiceCollection class. After that, we add the database context to the service container and instruct it to use the in-memory database instead of the real database. Finally, we ensure that we seed the data from the EmployeeContext class (The same data you inserted into a real SQL Server database at the beginning of this series). With these preparations in place, we can return to the test class and start writing our tests.

      Integration Testing of the Index Action

      In our test class, we can find a single test method with the default name. But let’s remove it and start from scratch. The first thing we have to do is to implement a previously created TestingWebAppFactory class:
      public class EmployeesControllerIntegrationTests : IClassFixture<TestingWebAppFactory<Startup>>
      {
          private readonly HttpClient _client;
      
          public EmployeesControllerIntegrationTests(TestingWebAppFactory<Startup> factory)
          {
              _client = factory.CreateClient();
          }
      }
      
      So, we implement the TestingWebAppFactory class with the IClassFixture interface and inject it in a constructor, where we create an instance of the HttpClient. The IClassFixture interface is a decorator which indicates that tests in this class rely on a fixture to run. We can see that the fixture is our TestingWebAppFactory class. Now, let’s write our first integration test:
      [Fact]
      public async Task Index_WhenCalled_ReturnsApplicationForm()
      {
          var response = await _client.GetAsync("/Employees");
      
          response.EnsureSuccessStatusCode();
      
          var responseString = await response.Content.ReadAsStringAsync();
      
          Assert.Contains("Mark", responseString);
          Assert.Contains("Evelin", responseString);
      }
      
      We use the GetAsync method to call the action on the /Employees route, which is the Index action and return a result in a response variable. With the EnsureSuccessStatusCode method, we verify that the IsSuccessStatusCode property has the value true: IsSuccessStatusCode - Integration Testing If the value is false, it would mean that the request is not successful, thus the test would fail. Finally, we serialize our HTTP content to a string with the ReadAsStringAsync method and verify that it contains our two employees: Index integration test pass We can see that the test passes and that we successfully return our employees from the in-memory database. If you want to make sure that we are really using the in-memory database and not the real one, you can always stop the SQLServer service in the Services window and run the test again. Excellent! Now, we can continue towards the integration testing of both Create actions.

      Testing the Create (GET) Action

      Before we continue with testing, let’s open the Create.cshtml file, from the Views\Employees folder, and modify it by changing the h4 tag (just to have more than one word to test):
      <h4>Please provide a new employee data</h4>
      Great. Now we are ready to write our test code. We want to verify when the Create (GET) action executes, it returns a create form:
      [Fact]
      public async Task Create_WhenCalled_ReturnsCreateForm()
      {
          var response = await _client.GetAsync("/Employees/Create");
      
          response.EnsureSuccessStatusCode();
      
          var responseString = await response.Content.ReadAsStringAsync();
      
          Assert.Contains("Please provide a new employee data", responseString);
      }
      
      Create integration test pass And it does.

      Additional Tests for the Create (POST) Action

      So, let’s write some integration testing code for the POST action. For the first test method, we are going to verify that our action returns a view with an appropriate error message when the model, sent from the Create page, is invalid. And yes, in a previous article, we had test methods for the invalid model, but without an HTTP request. Having that said, let’s write the test code:
      [Fact]
      public async Task Create_SentWrongModel_ReturnsViewWithErrorMessages()
      {
          var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Employees/Create");
      
          var formModel = new Dictionary<string, string>
          {
              { "Name", "New Employee" },
              { "Age", "25" }
          };
      
          postRequest.Content = new FormUrlEncodedContent(formModel);
      
          var response = await _client.SendAsync(postRequest);
          response.EnsureSuccessStatusCode();
      
          var responseString = await response.Content.ReadAsStringAsync();
      
          Assert.Contains("Account number is required", responseString);
      }
      
      We create a post request and the formModel object as a dictionary, which consists of the elements that we have on the Create page. Of course, we didn’t provide all the elements, the AccountNumber is missing, because we want to send invalid data. After that, we store the formModel as a content in our request, send that request with the SendAsync method and ensure that the response is successful. Finally, we serialize our response and make assertion verification. If we take a look at the Employee model class, we are going to see that if the AccountNumber is not provided the error message should appear on the form:
      [Required(ErrorMessage = "Account number is required")]
      public string AccountNumber { get; set; }
      
      That is exactly what we verify in our test method. Now, we can run the Test Explorer: Post test fail - Integration Testing Well, this test fails. But, there is nothing wrong with the code, the test code is good, just for some reason we are getting the 400 Bad Request message. Why is that?

      Explanation

      Well, if we open our controller and take a look at the Create (POST) action, we can see the ValidateAntiForgeryToken attribute. So, our action expects the anti-forgery token to be provided but we are not doing that, thus the test fails. For now (just as temporary solution) we are going to comment out that attribute and run the test again: Commenting anti-forgery token validation attribute The result: Post test passes Now, the test passes. As we said this is just a temporary solution. There are a couple of steps required to configure Anti-Forgery token in our testing code and in the next article we are going to show you how to do that step by step. For now, let’s just continue with another test while the ValidateAntiForgeryToken is commented out.

      Testing Successful POST Request

      Let’s write the final test in this article, where we verify that the Create action returns the Index view if the POST request is successful:
      [Fact]
      public async Task Create_WhenPOSTExecuted_ReturnsToIndexViewWithCreatedEmployee()
      {
          var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Employees/Create");
      
          var formModel = new Dictionary<string, string>
          {
              { "Name", "New Employee" },
              { "Age", "25" },
              { "AccountNumber", "214-5874986532-21" }
          };
      
          postRequest.Content = new FormUrlEncodedContent(formModel);
      
          var response = await _client.SendAsync(postRequest);
          response.EnsureSuccessStatusCode();
      
          var responseString = await response.Content.ReadAsStringAsync();
      
          Assert.Contains("New Employee", responseString);
          Assert.Contains("214-5874986532-21", responseString);
      }
      
      So, this code is not too much different from the previous one, except we send a valid formModel object with the request and the assertion part. Basically, once the POST request is finished successfully, the Create method should redirect us to the Index method. There, we can find all the employees including the created one. You can always debug your test code and inspect the responseString variable to visually confirm that response is the Index page with a new employee. Finally, let’s run the Test Explorer: The Last Post integration test passes Excellent! It passes.

      Conclusion

      In this article, we have learned how to write integration tests in the ASP.NET Core MVC application. We have created an In-Memory database to use it during tests instead of the real database server. Additionally, we have learned how to test our Index action and how to write integration tests for the Create actions as well. This testing methodology could be applied to other actions as well (PUT, Delete...). Finally, we have seen the problem with the anti-forgery token and in the next article, we are going to learn how to solve that problem by introducing several new functionalities to our code. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48903 0 0 0 1921 0 0
      How to Include AntiForgeryToken for MVC Integration Testing https://code-maze.com/testing-anti-forgery-token-asp-net-core-mvc/ Mon, 07 Oct 2019 06:00:29 +0000 https://code-maze.com/?p=48976 previous article, we have learned how to write integration tests for different actions (Index and Create), but while we were testing the Create (POST) action, we faced a problem with AntiForgeryToken validation. We skipped that problem by commenting out that validation attribute and our test passed, but that was just a temporary solution. In this article, we are going to solve that problem. We will learn how to extract AntiForgeryToken from the HTML response and how to use it in our tests. After fixing our problem, we will be able to test our actions that are protected with the anti-forgery validation attributes. You can download the source code on our GitHub repository. For the complete navigation of this series, you can visit ASP.NET Core MVC Testing. These are the topics that we are going to cover:

      Injecting AntiForgeryToken into the IserviceCollection

      To start, let’s create the AntiForgeryTokenExtractor class in the EmployeesApp.IntegrationTests project, with two properties:
      public static class AntiForgeryTokenExtractor
      {
          public static string AntiForgeryFieldName { get; } = "AntiForgeryTokenField";
          public static string AntiForgeryCookieName { get; } = "AntiForgeryTokenCookie";
      }
      
      In this class, we are going to wrap all the logic required for extracting the anti-forgery field and cookie. For now, we just define the field and the cookie names. In a bit, we are going to add additional methods. But for now, let’s move on to the TestingWebAppFactory class, and inject our token details in IServiceCollection. So, let’s write our code right bellow the services.AddDbContext<EmployeeContext> part:
      services.AddAntiforgery(t =>
      {
          t.Cookie.Name = AntiForgeryTokenExtractor.AntiForgeryCookieName;
          t.FormFieldName = AntiForgeryTokenExtractor.AntiForgeryFieldName;
      });
      
      With this code, we add the anti-forgery service in the specified IServiceCollection with the cookie and the field names. Once we do this, we can extract those properties from the HTML response by using the same names as declared in the AntiForgeryTokenExtractor class.

      Extracting the Field and Cookie from the HTML Response

      With that being said, let’s go back to the AntiForgeryTokenExtractor class and add the required code for extracting the cookie first:
      private static string ExtractAntiForgeryCookieValueFrom(HttpResponseMessage response)
      {
          string antiForgeryCookie = response.Headers.GetValues("Set-Cookie")
              .FirstOrDefault(x => x.Contains(AntiForgeryCookieName));
      
          if (antiForgeryCookie is null)
          {
              throw new ArgumentException($"Cookie '{AntiForgeryCookieName}' not found in HTTP response", nameof(response));
          }
      
          string antiForgeryCookieValue = SetCookieHeaderValue.Parse(antiForgeryCookie).Value.ToString();
      
          return antiForgeryCookieValue;
      }
      
      In our code, we fetch the value of the Set-Cookie property, from the Header of our response that contains the name of the defined cookie. After that, if that cookie doesn’t exist we throw an exception. Otherwise, we just parse its value and return it. Now, we can add another method to extract the field:
      private static string ExtractAntiForgeryToken(string htmlBody)
      {
          var requestVerificationTokenMatch = Regex.Match(htmlBody, $@"\<input name=""{AntiForgeryFieldName}"" type=""hidden"" value=""([^""]+)"" \/\>");
      
          if (requestVerificationTokenMatch.Success)
          {
              return requestVerificationTokenMatch.Groups[1].Captures[0].Value;
          }
      
          throw new ArgumentException($"Anti forgery token '{AntiForgeryFieldName}' not found in HTML", nameof(htmlBody));
      }
      
      With this method, we are using a regex expression to extract the HTML control from the htmlBody string that contains the anti-forgery field value. If the expression is successful, we return its value, otherwise, we throw an exception. Finally, we can create a main method that will return the results of both these methods:
      public static async Task<(string fieldValue, string cookieValue)> ExtractAntiForgeryValues(HttpResponseMessage response)
      {
          var cookie = ExtractAntiForgeryCookieValueFrom(response);
          var token = ExtractAntiForgeryToken(await response.Content.ReadAsStringAsync());
      
          return (token, cookie);
      }
      
      So, we just call both methods, collect their results and return them as a Tuple object. That is it, we can now modify our testing methods and include AntiForgeryToken validation in the controller.

      Modifying Test Methods

      The first action, we are going to do is to remove a comment from the validation attribute on top of the Create action in the EmployeesController class. As soon as we finish that, we can move on to the EmployeesControllerIntegrationTests class. We are going to modify the Create_SentWrongModel_ReturnsViewWithErrorMessages method:
      [Fact]
      public async Task Create_SentWrongModel_ReturnsViewWithErrorMessages()
      {
          var initResponse = await _client.GetAsync("/Employees/Create");
          var antiForgeryValues = await AntiForgeryTokenExtractor.ExtractAntiForgeryValues(initResponse);
      
          var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Employees/Create");
      
          postRequest.Headers.Add("Cookie", new CookieHeaderValue(AntiForgeryTokenExtractor.AntiForgeryCookieName, antiForgeryValues.cookieValue).ToString());
      
          var formModel = new Dictionary<string, string>
          {
              { AntiForgeryTokenExtractor.AntiForgeryFieldName, antiForgeryValues.fieldValue },
              { "Name", "New Employee" },
              { "Age", "25" }
          };
      
          postRequest.Content = new FormUrlEncodedContent(formModel);
      
          var response = await _client.SendAsync(postRequest);
          response.EnsureSuccessStatusCode();
      
          var responseString = await response.Content.ReadAsStringAsync();
      
          Assert.Contains("Account number is required", responseString);
      }
      
      So, we have to send a GET request first in order to get the response which we use to extract our anti-forgery values from. Once extracted, we assign the cookie value to the Header of our POST request and assign the field value in the formModel object. Let’s see how those cookie and field values look like in the response. First the cookie from the response: Cookie value - AntiForgeryToken testing And the field from the HTML body: Field value - AntyForgeryToken integration testing We can see that both the cookie and the field have the same names as we declared in the TestingWebAppFactory class.

      Modifying Additional Test Method

      Let’s add the same modifications to the Create_WhenPOSTExecuted_ReturnsToIndexView method:
      [Fact]
      public async Task Create_WhenPOSTExecuted_ReturnsToIndexView()
      {
          var initResponse = await _client.GetAsync("/Employees/Create");
          var antiForgeryValues = await AntiForgeryTokenExtractor.ExtractAntiForgeryValues(initResponse);
      
          var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Employees/Create");
      
          postRequest.Headers.Add("Cookie", new CookieHeaderValue(AntiForgeryTokenExtractor.AntiForgeryCookieName, antiForgeryValues.cookieValue).ToString());
      
          var modelData = new Dictionary<string, string>
          {
              { AntiForgeryTokenExtractor.AntiForgeryFieldName, antiForgeryValues.fieldValue },
              { "Name", "New Employee" },
              { "Age", "25" },
              { "AccountNumber", "214-5874986532-21" }
          };
      
          postRequest.Content = new FormUrlEncodedContent(modelData);
      
          var response = await _client.SendAsync(postRequest);
          response.EnsureSuccessStatusCode();
      
          var responseString = await response.Content.ReadAsStringAsync();
      
          Assert.Contains("New Employee", responseString);
          Assert.Contains("214-5874986532-21", responseString);
      }
      
      Additionally, we have changed the method name and a single variable in this method, for better readability. All we have to do is to verify that our tests pass: Both Integration POST tests - AntiForgeryToken testing And they do. Excellent.

      Conclusion

      In this article, we have learned:
      • How to inject an anti-forgery service in IServiceCollection
      • The way to extract the anti-forgery cookie and the anti-forgery field values from the response
      • How to modify our test methods to work with AntiForgeryToken validation
      In the next article, we are going to learn about UI testing with the Selenium library. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48976 0 0 0
      Automated UI tests with Selenium and ASP.NET Core MVC https://code-maze.com/automatic-ui-testing-selenium-asp-net-core-mvc/ Mon, 14 Oct 2019 06:00:20 +0000 https://code-maze.com/?p=49032 the source code on our GitHub repository. For the complete navigation of this series, you can visit ASP.NET Core MVC Testing. These are the topics that we are going to cover: So, let’s get down to business.

      Project Preparation and Selenium Installation

      First thing first. We are going to create a new xUnit (.NET Core) test project and name it EmployeesApp.AutomatedUITests. After the creation process, we are going to rename the existing class to AutomatedUITests: Project creation - Selenium Now we can open the NuGet Package Manager window and install two required libraries, Selenium.WebDriver and Selenium.Chrome.WebDriver: Selenium libraries Or you can use the Package Manager window: PM> Install-Package Selenium.WebDriver -Version 3.141.0 PM> Install-Package Selenium.Chrome.WebDriver -Version 79.0.0 That’s all it takes. We are now prepared to write automated UI tests.

      Writing the First UI Test

      Let’s open the AutomatedUITests class and modify it by implementing the IDisposable interface:
      public class AutomatedUITests : IDisposable
      {
          public void Dispose()
          {
              
          }
      }
      
      We are going to use the Dispose method to close a chrome window opened by the ChromeDriver and also to dispose of it. To continue on, let’s create our driver:
      public class AutomatedUITests : IDisposable
      {
          private readonly IWebDriver _driver;
          public AutomatedUITests()
          {
              _driver = new ChromeDriver();
          }
      
          public void Dispose()
          {
              _driver.Quit();
              _driver.Dispose();
          }
      }
      
      So, we instantiate the IWebDriver object by using the ChromeDriver class and in the Dispose method, dispose of it. With that done, everything is ready for the first UI test:
      [Fact]
      public void Create_WhenExecuted_ReturnsCreateView()
      {
          _driver.Navigate()
              .GoToUrl("https://localhost:5001/Employees/Create");
      
          Assert.Equal("Create - EmployeesApp", _driver.Title);
          Assert.Contains("Please provide a new employee data", _driver.PageSource);
      }
      
      We use the Navigate method to instruct the driver to navigate the browser to another location and with the GoToUrl method, we provide that location. Once the browser has navigated to the requested Url, the Title and PageSource properties of the _driver object will be populated. So, we just make assertions on those properties to verify that we have actually navigated to the Create page. Before we start the Test Explorer window, we need to start our application without debugging (CTRL+F5) because a running server is required for UI tests to pass: First UI test pass - Selenium testing As soon as we run our test, we are going to see a new browser window opened and soon after that closed because we call the Quit method in the Dispose method. A little bit later, our test will pass. Excellent! We can shut down our app, and move on to the other tests.

      Using Selenium to Manipulate Input Fields and Executing Additional Tests

      Let’s write another test where we verify that the error message appears on the screen if we populate some input fields, not all of them, and click the Create button:
      [Fact]
      public void Create_WrongModelData_ReturnsErrorMessage()
      {
          _driver.Navigate()
              .GoToUrl("https://localhost:5001/Employees/Create");
      
          _driver.FindElement(By.Id("Name"))
              .SendKeys("Test Employee");
      
          _driver.FindElement(By.Id("Age"))
              .SendKeys("34");
      
          _driver.FindElement(By.Id("Create"))
              .Click();
      
          var errorMessage = _driver.FindElement(By.Id("AccountNumber-error")).Text;
      
          Assert.Equal("Account number is required", errorMessage);
      }
      
      One more time, we navigate to the required location by using the Navigate and the GoToUrl methods. After that, we start populating our input fields. Of course, we have to find an element first. To achieve that we use the FindElement(By.Id(„Name“)) expression. The FindElement method will find an element on the HTML page and it accepts a parameter of type By. The By class consists of the different methods which allow us to search different elements on our page (Id, ClassName, CssSelector, TagName, etc.). Once we find the element, we use the SendKeys method to populate it. The same process is repeated for the Age element and the Create button, just for the Create button we use the Click method to click on it. Finally, we extract the error message from the page and make an assertion. If you want, you can debug this test code to see how our driver opens the page and manipulates the input fields and the button. Let’s start our app without debugging again and run our test: Secont UI test fail We can see that it fails, and the message explains it pretty well. The FindElement method can’t find our button with the Create value for the Id attribute. So, we can inspect our page source and look for a valid attribute or we can change our code a bit. We are going to change the code. Let’s open the Create view in the main project and just add the Id attribute to the button element:
      <input type="submit" id="Create" value="Create" class="btn btn-primary" />
      Now, let’s start the app and run the Test Explorer window: Second UI test passes It passes now.

      Additional UI Test

      Let’s write one additional test where we populate all the fields, click the Create button and then verify that the Index page is loaded with a new employee:
      [Fact]
      public void Create_WhenSuccessfullyExecuted_ReturnsIndexViewWithNewEmployee()
      {
          _driver.Navigate()
              .GoToUrl("https://localhost:5001/Employees/Create");
      
          _driver.FindElement(By.Id("Name"))
              .SendKeys("Another Test Employee ");
      
          _driver.FindElement(By.Id("Age"))
              .SendKeys("34");
      
          _driver.FindElement(By.Id("AccountNumber"))
              .SendKeys("123-9384613085-58");
      
          _driver.FindElement(By.Id("Create"))
              .Click();
      
          Assert.Equal("Index - EmployeesApp", _driver.Title);
          Assert.Contains("Another Test Employee ", _driver.PageSource);
          Assert.Contains("34", _driver.PageSource);
          Assert.Contains("123-9384613085-58", _driver.PageSource);
      }
      
      We can see that there are small differences between this code and the previous one. Here we just populate all the fields and making assertions on the page’s title and newly created data. So, let’s try if this passes: Last selenium UI test And it does. Great job. Now, let’s refactor our code to eliminate repetitions and make it more readable.

      Making the Code Even Better

      We can see that we have a lot of redundant code in our testing methods when we navigate to the URI or find different elements on the HTML page. This is something we want to avoid. This pattern, that we are going to use, is called Page Object Model Design Pattern. But, we are not going to use the PageFactory class (as you can see in many different examples) because it is not supported in .NET Core and it is getting obsolete in .NET Framework. So, let’s start by creating a new EmployeePage class in the EmployeesApp.UITests project and modifying it:
       public class EmployeePage
      {
          private readonly IWebDriver _driver;
          private const string URI = "https://localhost:5001/Employees/Create";
      
          private IWebElement NameElement => _driver.FindElement(By.Id("Name"));
          private IWebElement AgeElement => _driver.FindElement(By.Id("Age"));
          private IWebElement AccountNumberElement => _driver.FindElement(By.Id("AccountNumber"));
          private IWebElement CreateElement => _driver.FindElement(By.Id("Create"));
      
          public string Title => _driver.Title;
          public string Source => _driver.PageSource;
          public string AccountNumberErrorMessage => _driver.FindElement(By.Id("AccountNumber-error")).Text;
      
          public EmployeePage(IWebDriver driver)
          {
              _driver = driver;
          }
      
          public void Navigate() => _driver.Navigate()
                 .GoToUrl(URI);
      
          public void PopulateName(string name) => NameElement.SendKeys(name);
          public void PopulateAge(string age) => AgeElement.SendKeys(age);
          public void PopulateAccountNumber(string accountNumber) => AccountNumberElement.SendKeys(accountNumber);
          public void ClickCreate() => CreateElement.Click();
      }
      
      This code is pretty easy to understand because we only extract the logic for finding HTML elements and add some methods to populate them and click the Create button. We additionally extract the logic for the Title, Source and error message. After these changes, we can modify the AutomatedUITests class:
      public class AutomatedUITests : IDisposable
      {
          private readonly IWebDriver _driver;
          private readonly EmployeePage _page;
          public AutomatedUITests()
          {
              _driver = new ChromeDriver();
              _page = new EmployeePage(_driver);
              _page.Navigate();
          }
      
          [Fact]
          public void Create_WhenExecuted_ReturnsCreateView()
          {
              Assert.Equal("Create - EmployeesApp", _page.Title);
              Assert.Contains("Please provide a new employee data", _page.Source);
          }
      
          [Fact]
          public void Create_WrongModelData_ReturnsErrorMessage()
          {
              _page.PopulateName("New Name");
              _page.PopulateAge("34");
              _page.ClickCreate();
      
              Assert.Equal("Account number is required", _page.AccountNumberErrorMessage);
          }
      
          [Fact]
          public void Create_WhenSuccessfullyExecuted_ReturnsIndexViewWithNewEmployee()
          {
              _page.PopulateName("New Name");
              _page.PopulateAge("34");
              _page.PopulateAccountNumber("123-9384613085-58");
              _page.ClickCreate();
      
              Assert.Equal("Index - EmployeesApp", _page.Title);
              Assert.Contains("New Name", _page.Source);
              Assert.Contains("34", _page.Source);
              Assert.Contains("123-9384613085-58", _page.Source);
          }
      
          public void Dispose()
          {
               _driver.Quit();
               _driver.Dispose();
          }
      }
      
      And that is it. It is pretty obvious that this code is much cleaner and easier to read. You can run the test explorer to verify that all tests pass as they did before.

      Conclusion

      And there we go. With this article, we have finished our Testing series. Now you should have enough knowledge to write your own tests and to deeply explore testing features in ASP.NET Core MVC. We hope you have enjoyed this series as much as we did. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      49032 0 0 0 1573 0 0 1574 1573 0 1575 1574 0 1577 1575 0
      HTTP Reference: status code classification https://code-maze.com/?post_type=tablepress_table&p=383 Sat, 17 Jun 2017 10:06:34 +0000 http://www.code-maze.com/?post_type=tablepress_table&p=383 383 0 0 0 _tablepress_export_table_id Getting Started with AutoMapper in ASP.NET Core https://code-maze.com/automapper-net-core/ Mon, 28 Oct 2019 06:00:44 +0000 https://code-maze.com/?p=48816 In this article, we are going to learn how to use AutoMapper in an ASP.NET Core application. We are going to start by looking into what the AutoMapper is and what problem it solves. Then, we are going to explain how we can use AutoMapper in our application. After that, we’ll learn about the usage guidelines and best practices. We’ll also take a look at what’s happening behind the scenes and how to flatten complex object models. You can find the source code on our GitHub repository. We have divided this article into the following sections: Let's learn what problems we can solve with AutoMapper.

      What is AutoMapper?

      AutoMapper is a simple library that helps us to transform one object type to another. It is a convention-based object-to-object mapper that requires very little configuration.  The object-to-object mapping works by transforming an input object of one type into an output object of a different type.

      One Use Case

      AutoMapper was built to solve a complex problem that most developers face in their day-to-day life - writing code that maps one object type to another. This type of code is rather tedious and boring to write, so why not leave that job to this little tool? What makes AutoMapper interesting is that it provides some easy to use conventions to take the dirty work out of figuring out how to map Type A to Type B. As long as Type B follows AutoMapper’s established conventions, almost no configuration is needed to map two types. Here’s one common scenario. We’ve created an application and we want to keep the separation between our domain models and our view models.  In order to accomplish this, we need to write the code to adapt our domain model to our view model. Then, as we add more views and domain models, we end up writing more adapters. Later on, we'll have to write even more adapters to map our data transfer objects from the database layer into our domain objects.  This is mundane and repetitive. And this is where AutoMapper comes in.

      How to Use AutoMapper in Our Application

      Let’s have a look at how to add Automapper into our .NET Core application.

      Installation

      The first step is to install the corresponding NuGet package:
      Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
      If we install the AutoMapper.Extensions.Microsoft.DependencyInjection package, it will automatically install the AutoMapper package for us since it references it.

      Configuration

      After installing the required package, the next step is to configure the services. Let’s do it in the Startup.cs class:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddAutoMapper(typeof(Startup));
          services.AddControllersWithViews();
      }
      That’s it. AutoMapper is installed and configured in our project. Now, let’s see how to use it with our objects.

      Usage

      Let’s say we have a domain object named User:
      public class User
      {
          public int Id { get; set; }
      
          public string FirstName { get; set; }
      
          public string LastName { get; set; }
      
          public string Email { get; set; }
      
          public string Address { get; set; }
      }
      In the UI layer, we would have a ViewModel to display the user information:
      public class UserViewModel
      {
          public string FirstName { get; set; }
      
          public string LastName { get; set; }
      
          public string Email { get; set; }
      }
      Now, let’s see how we are going to convert our domain object to a view model.

      Profiles

      A good way to organize our mapping configurations is with Profiles. We need to create classes that inherit from Profile class and put the configuration in the constructor:
      public UserProfile()
      {
          CreateMap<User, UserViewModel>();
      }
      UserProfile class creates the mapping between our User domain object and UserViewModel. As soon as our application starts and initializes AutoMapper, AutoMapper will scan our application and look for classes that inherit from the Profile class and load their mapping configurations. Now, let’s define a Controller and use the Auto-Mapping capabilities that we just added:
      public class UserController : Controller
      {
          private readonly IMapper _mapper;
      
          public UserController(IMapper mapper)
          {
              _mapper = mapper;
          }
      
          public IActionResult Index()
          {
              // Populate the user details from DB
              var user = GetUserDetails();
      
              UserViewModel userViewModel = _mapper.Map<UserViewModel>(user);
      
              return View(userViewModel);
          }
      }
      First, we inject the mapper object into the controller. Then, we call the Map() method, which maps the User object to the UserViewModel object. Furthermore, pay attention to a local method GetUserDetails that we use for the local data storage. You can find its implementation in our source code. Let’s create a View for the Index action method as explained in the article’s section: Creating Views. Now let’s run the application: run the app We can see our User domain object is properly mapped to the UserViewModel object.

      Mapping to Properties with different Names

      Well, that was quite simple, wasn’t it?  But what if we have different property names in our source and destination objects. Let’s take a look at how to do the mapping in these cases. Let’s modify the property names in UserViewModel class:
      public class UserViewModel
      {
          public string FName { get; set; }
      
          public string LName { get; set; }
      
          public string Email { get; set; }
      
      }
      Here we need to map the below properties from User domain object to our UserViewModel: User.FirstName -> UserViewModel.FName User.LastName -> UserViewModel.LName User.EMail -> UserViewModel.Email So, let’s change the mapping in the UserProfile class:
      public UserProfile()
      {
          CreateMap<User, UserViewModel>()
              .ForMember(dest =>
                  dest.FName,
                  opt => opt.MapFrom(src => src.FirstName))
              .ForMember(dest =>
                  dest.LName,
                  opt => opt.MapFrom(src => src.LastName))
      }
      We use the CreateMap() method to create a mapping by providing the source and destination properties.  If we want to customize the configuration for individual members, we can use the ForMember() method which has the parameters destinationMember, which is of type Expression and memberOptions, which is of type Action. For example, the above code maps FirstName and LastName properties of the User object to the FName and the LName property of UserViewModel respectively. After making these changes, let’s run the application once again. We can see that these properties are mapped correctly.

      Reverse Mapping

      So far, we have only looked at one-directional mapping, which means if we have two types Type A and Type B, then we only map Type A to Type B. But, by using Automapper’s Reverse mapping capability, it is possible to achieve bi-directional mapping:
      public UserProfile()
      {
          CreateMap<User, UserViewModel>()
              .ForMember(dest =>
                  dest.FName,
                  opt => opt.MapFrom(src => src.FirstName))
              .ForMember(dest =>
                  dest.LName,
                  opt => opt.MapFrom(src => src.LastName))
              .ReverseMap();
      }
      Once Reverse Mapping is configured, we can map back from destination to source type:
      var mappedUser = _mapper.Map<User>(userViewModel);
      This way, we can easily achieve bi-directional mapping between types using AutoMapper’s Reverse Mapping capabilities.

      Behind the Scenes

      We have seen the magic of AutoMapper in action. So what happens in the background?  AutoMapper uses a programming concept called Reflection to retrieve the type metadata of objects. We can use reflection to dynamically get the type from an existing object and invoke its methods or access its fields and properties.  Then, based on the conventions and configurations defined, we can easily map the properties of the two types. AutoMapper was built around this concept.

      Usage Guidelines and Best practices

      As with every other component that we use in our application, there are certain usage guidelines and best practices that we need to follow while using AutoMapper.

      Do’s 

      • Always use the AutoMapper.Extensions.Microsoft.DependencyInjection package in ASP.NET Core with services.AddAutoMapper(assembly[]). This package will perform all the scanning and dependency injection registration. We only need to declare the Profile configurations.
      • Always organize configuration into Profiles. Profiles allow us to group common configuration and organize mappings by usage. This lets us put mapping configuration closer to where it is used, instead of a single file of configuration that becomes difficult to edit/maintain.
      • Always use configuration options supported by LINQ over their counterparts as LINQ query extensions have the best performance of any mapping strategy.
      • Always flatten DTOs. AutoMapper can handle mapping properties A.B.C into ABC. By flattening our model, we create a more simplified object that won't require a lot of navigation to get at data.
      • Always put common simple computed properties into the source model. Similarly, we need to place computed properties specific to the destination model in the destination model.

      Don'ts

      • Do not call CreateMap() on each request. It is not a good practice to create the configuration for each mapping request. Mapping configuration should be done once at startup.
      • Do not use inline maps. Inline maps may seem easier for very simple scenarios, but we lose the ease of configuration.
      • If we have to write a complex mapping behavior, it might be better to avoid using AutoMapper for that scenario.
      • Do not put any logic that is not strictly mapping behavior into the configuration. AutoMapper should not perform any business logic, it should only handle the mapping.
      • Avoid sharing DTOs across multiple maps. Model your DTOs around individual actions, and if you need to change it, you only affect that action.
      • Do not create DTOs with circular associations. AutoMapper does support it, but it's confusing and can result in quite bad performance. Instead, we can create separate DTOs for each level of a hierarchy we want.

      Flattening Complex Object Models

      AutoMapper supports flattening complex object models into DTO or another simple object model. For example, Domain Objects usually have a complex object model with many associations between them, but ViewModels generally have a flat object model.  We can map Domain Objects to ViewModels with AutoMapper’s Flattening If we follow proper naming conventions for our object models, then there is no need to provide any additional configuration code. AutoMapper works with conventions and maps our object model from complex to flat/simple ones. AutoMapper uses the following conventions:
      • It will automatically map properties with the same names.
      • If the source object has some association with other objects, then it will try to map with properties on the destination object whose name is a combination of the source class name and property name in the Pascal case
      • It will try to map methods on source object which has a Get prefix with a property on destination object with the name excluding the Get prefix.
      If we follow these conventions, AutoMapper will automatically map our objects. Otherwise, we’ll need to configure AutoMapper using Fluent API. Let’s modify our User object by adding a child object Address:
      public class User
      {
          public int Id { get; set; }
      
          public string FirstName { get; set; }
      
          public string LastName { get; set; }
      
          public string Email { get; set; }
      
          public Address Address { get; set; }
      
          public string GetFullName()
          {
              return $"{this.LastName}, {this.FirstName}";
          }
      }
      And here's how the Address class looks like:
      public class Address
      {
          public int Id { get; set; }
      
          public string Door { get; set; }
      
          public string Street1 { get; set; }
      
          public string Street2 { get; set; }
      
          public string City { get; set; }
      
          public string State { get; set; }
      
          public string Country { get; set; }
      
          public string ZipCode { get; set; }
      }
      Also, note that we have added a method GetFullName() to get the user’s full name. Let’s modify the UserViewModel class:
      public class UserViewModel
      {
          [Display(Name = "Full Name")]
          public string FullName { get; set; }
      
          [Display(Name = "Country")]
          public string AddressCountry { get; set; }
      
          public string Email { get; set; }
      }
      Now, Let’s modify the profile class to use the default conventions:
      public UserProfile()
      {
          CreateMap<User, UserViewModel>();
      }
      Let’s run the application once again: run the app with complex type mapping This time, we can see that the GetFullName() method on the source object is correctly mapped to FullName property on the destination object. Similarly, User.Address.Country property is automatically mapped to UserViewModel.AddressCountry These mappings are correctly handled by the AutoMapper using its default conventions.

      Conclusion

      In this article, we have learned the following concepts:
      • The AutoMapper component - what is it and when to use it?
      • Installing, Configuring and using the AutoMapper component in our ASP.NET Core application
      • How AutoMapper works behind the scenes
      • Usage Guidelines and best practices while using AutoMapper.
      • Flattening a complex object model using AutoMapper’s default conventions.
      That’s all for now. Hope you enjoyed the article. We’ll cover more advanced topics related to AutoMapper in one of the following articles. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48816 0 0 0
      Paging in ASP.NET Core Web API https://code-maze.com/paging-aspnet-core-webapi/ Mon, 18 Nov 2019 06:00:08 +0000 https://code-maze.com/?p=48879 GitHub repo. If you want to follow along with the article, you can use the start branch and if you want to get the final solution or if you get stuck, switch to the end branch. NOTE: Some degree of previous knowledge is needed to follow this article. It relies heavily on the ASP.NET Core Web API series on Code Maze, so if you are not sure how to set up the database or how the underlying architecture works, we strongly suggest you go through the series. We'll discuss what paging is, the easiest way to implement it, and then improve on that solution some more to create a more readable and flexible codebase. So let's see what we're going to talk about in this article exactly: Let's start.

      What is Paging?

      Paging refers to getting partial results from an API. Imagine having millions of results in the database and having your application try to return all of them at once. Not only that would be an extremely ineffective way of returning the results, but it could also possibly have devastating effects on the application itself or the hardware it runs on. Moreover, every client has limited memory resources and it needs to restrict the number of shown results. Thus, we need a way to return a set number of results to the client in order to avoid these consequences. Let's see how we can do that.

      Initial Implementation

      Before we do any changes to the source code, let's inspect how it looks right now, and how you would probably begin with any project. In our case, we have the OwnerController which does all the necessary actions on the Owner entity. One particular action that stands out, and that we need to change is the GetOwners() action:
      [HttpGet]
      public IActionResult GetOwners()
      {
      	var owners = _repository.Owner.GetAllOwners();
      
      	_logger.LogInfo($"Returned all owners from database.");
      
      	return Ok(owners);
      }
      Which calls GetOwners() from OwnerRepository:
      public IEnumerable<Owner> GetOwners()
      {
      	return FindAll()
      		.OrderBy(ow => ow.Name);
      }
      FindAll() method is just a method from a Base Repository class that return the whole set of owners.
      public IQueryable<T> FindAll()
      {
      	return this.RepositoryContext.Set<T>();
      }
      As you can see it's a straightforward action, meant to return all the owners from the database ordered by name. And it does just that. But, in our case, that's just a few account owners (five). What if there were thousands or even millions of people in the database (you wish, but still, imagine another kind of entity). End then add to that, a few thousand of API consumers. We would end up with a very long query that returns A LOT of data. The best-case scenario would be that you started out with a small number of owners that increased slowly over time so you can notice the slow decline in performance. Other scenarios are far less benign for your application and machines (imagine hosting it in the cloud and not having proper caching in place). So, having that in mind, let's modify this method to support paging.

      Paging Implementation

      Mind you, we don't want to change the base repository logic or implement any business logic in the controller. What we want to achieve is something like this: https://localhost:5001/api/owners?pageNumber=2&pageSize=2. This should return the second set of two owners we have in our database. We also want to constraint our API not to return all the owners even if someone calls https://localhost:5001/api/owners. Let's start by changing the controller:
      [HttpGet]
      public IActionResult GetOwners([FromQuery] OwnerParameters ownerParameters)
      {
      	var owners = _repository.Owner.GetOwners(ownerParameters);
      
      	_logger.LogInfo($"Returned {owners.Count()} owners from database.");
      
      	return Ok(owners);
      }
      A few things to take note here:
      • We're calling the GetOwners method from the OwnerRepository, which doesn't exist yet, but we'll implement it soon
      • We're using [FromQuery] to point out that we'll be using query parameters to define which page and how many owners we are requesting
      • OwnerParameters class is the container for the actual parameters
      We also need to actually create OwnerParameters class since we are passing it as an argument to our controller. Let's create it in the Models folder of the Entities project:
      public class OwnerParameters
      {
      	const int maxPageSize = 50;
      	public int PageNumber { get; set; } = 1;
      
      	private int _pageSize = 10;
      	public int PageSize
      	{
      		get
      		{
      			return _pageSize;
      		}
      		set
      		{
      			_pageSize = (value > maxPageSize) ? maxPageSize : value;
      		}
      	}
      }
      We are using constant maxPageSize to restrict our API to a maximum of 50 owners. We have two public properties - PageNumber and PageSize. If not set by the caller, PageNumber will be set to 1, and PageSize to 10. Now, let's implement the most important part, the repository logic. We need to extend the GetOwners() method in the IOwnerRepository interface and in the OwnerRepository class:
      public interface IOwnerRepository : IRepositoryBase<Owner>
      {
      	IEnumerable<Owner> GetOwners(OwnerParameters ownerParameters);
      	Owner GetOwnerById(Guid ownerId);
      	OwnerExtended GetOwnerWithDetails(Guid ownerId);
      	void CreateOwner(Owner owner);
      	void UpdateOwner(Owner dbOwner, Owner owner);
      	void DeleteOwner(Owner owner);
      }
      And the logic:
      public IEnumerable<Owner> GetOwners(OwnerParameters ownerParameters)
      {
      	return FindAll()
      		.OrderBy(on => on.Name)
      		.Skip((ownerParameters.PageNumber - 1) * ownerParameters.PageSize)
      		.Take(ownerParameters.PageSize)
      		.ToList();
      }
      Ok, the easiest way to explain this is by example. Say we need to get the results for the third page of our website, counting 20 as the number of results we want. That would mean we want to skip the first ((3 - 1) * 20) = 40 results, and then take the next 20 and return them to the caller. Does that make sense?

      Testing the Solution

      Now, in our database we only have only a few owners, so let's try something like this: https://localhost:5001/api/owners?pageNumber=2&pageSize=2 This should return the next subset of owners:
      [
          {
              "id": "66774006-2371-4d5b-8518-2177bcf3f73e",
              "name": "Nick Somion",
              "dateOfBirth": "1998-12-15T00:00:00",
              "address": "North sunny address 102"
          },
          {
              "id": "a3c1880c-674c-4d18-8f91-5d3608a2c937",
              "name": "Sam Query",
              "dateOfBirth": "1990-04-22T00:00:00",
              "address": "91 Western Roads"
          }
      ]
      If that's what you got, you're on the right track. Now, what can we do to improve this solution?

      Improving the Solution

      Since we're returning just a subset of results to the caller, we might as well have a PagedList instead of List. PagedList will inherit from the List class and will add some more to it. We can also, move the skip/take logic to the PagedList since it makes more sense. Let's implement it.

      Implementing PagedList Class

      We don't want our skip/take logic implemented inside our repository. Let's create a class for it:
      public class PagedList<T> : List<T>
      {
      	public int CurrentPage { get; private set; }
      	public int TotalPages { get; private set; }
      	public int PageSize { get; private set; }
      	public int TotalCount { get; private set; }
      
      	public bool HasPrevious => CurrentPage > 1;
      	public bool HasNext => CurrentPage < TotalPages;
      
      	public PagedList(List<T> items, int count, int pageNumber, int pageSize)
      	{
      		TotalCount = count;
      		PageSize = pageSize;
      		CurrentPage = pageNumber;
      		TotalPages = (int)Math.Ceiling(count / (double)pageSize);
      
      		AddRange(items);
      	}
      
      	public static PagedList<T> ToPagedList(IEnumerable<T> source, int pageNumber, int pageSize)
      	{
      		var count = source.Count();
      		var items = source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
      
      		return new PagedList<T>(items, count, pageNumber, pageSize);
      	}
      }
      As you can see, we've transferred the skip/take logic to the static method inside of the PagedList class. We've added a few more properties, that will come in handy as metadata for our response. HasPrevious is true if CurrentPage is larger than 1, and HasNext is calculated if CurrentPage is smaller than the number of total pages. TotalPages is calculated too, by dividing the number of items by the page size and then rounding it to the larger number since a page needs to exist even if there is one item on it. Now that we've cleared that out, let's change our OwnerRepository and OwnerController accordingly. First, we need to change our repo (don't forget to change the interface too):
      public PagedList<Owner> GetOwners(OwnerParameters ownerParameters)
      {
      	return PagedList<Owner>.ToPagedList(FindAll().OrderBy(on => on.Name),
      		ownerParameters.PageNumber,
      		ownerParameters.PageSize);
      }
      And then the controller:
      [HttpGet]
      public IActionResult GetOwners([FromQuery] OwnerParameters ownerParameters)
      {
      	var owners = _repository.Owner.GetOwners(ownerParameters);
      
      	var metadata = new
      	{
      		owners.TotalCount,
      		owners.PageSize,
      		owners.CurrentPage,
      		owners.TotalPages,
      		owners.HasNext,
      		owners.HasPrevious
      	};
      
      	Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(metadata));
      
      	_logger.LogInfo($"Returned {owners.TotalCount} owners from database.");
      
      	return Ok(owners);
      }
      Now, if we send the same request as we did earlier https://localhost:5001/api/owners?pageNumber=2&pageSize=2, we get the same exact result:
      [
          {
              "id": "f98e4d74-0f68-4aac-89fd-047f1aaca6b6",
              "name": "Martin Miller",
              "dateOfBirth": "1983-05-21T00:00:00",
              "address": "3 Edgar Buildings"
          },
          {
              "id": "66774006-2371-4d5b-8518-2177bcf3f73e",
              "name": "Nick Somion",
              "dateOfBirth": "1998-12-15T00:00:00",
              "address": "North sunny address 102"
          }
      ]
      But now we have some additional useful information in X-Pagination response header: [caption id="attachment_48926" align="aligncenter" width="765"]postman response headers paging Response headers[/caption] As you can see, all of our metadata is here. We can use this information when building any kind of frontend pagination functionality. You can play around with different requests to see how it works in other scenarios. There is one more thing we can do to make our solution even more generic. We have the OwnerParameters class, but what if we want to use it in our AccountController? Parameters that we send to the Account controller might be different. Maybe not for paging, but we'll send a bunch of different parameters later on and we need to separate the parameter classes. Let's see how to improve it.

      Creating a Parent Parameters Class

      First, let's create an abstract class QueryStringParameters. We'll use this class to implement mutually used functionalities for every parameter class we will implement. And since we have OwnerController and AccountController, that means we need to create OwnerParameters and AccountParameters classes. Let's start by defining QueryStringParameters class inside the Models folder of the Entities project:
      public abstract class QueryStringParameters
      {
      	const int maxPageSize = 50;
      	public int PageNumber { get; set; } = 1;
      
      	private int _pageSize = 10;
      	public int PageSize
      	{
      		get
      		{
      			return _pageSize;
      		}
      		set
      		{
      			_pageSize = (value > maxPageSize) ? maxPageSize : value;
      		}
      	}
      }
      We've also moved our paging logic inside the class since it will be valid for any entity we might want to return through the repository. Now, we need to create AccountParameters class, and then inherit the QueryStringParameters class in both the OwnerParameters and the AccountParameters classes. Remove the logic from OwnerParameters and inherit QueryStringParameters:
      public class OwnerParameters : QueryStringParameters
      {
      		
      }
      And create AccountParameters class inside the Models folder too:
      public class AccountParameters : QueryStringParameters
      {
      		
      }
      Now, these classes look a bit empty now, but soon we'll be populating them with other useful parameters and we'll see what the real benefit is. For now, it's important that we have a way to send a  different set of parameters for AccountController and OwnerController. Now we can do something like this too, inside our AccountController:
      [HttpGet]
      public IActionResult GetAccountsForOwner(Guid ownerId, [FromQuery] AccountParameters parameters)
      {
      	var accounts = _repository.Account.GetAccountsByOwner(ownerId, parameters);
      
      	var metadata = new
      	{
      		accounts.TotalCount,
      		accounts.PageSize,
      		accounts.CurrentPage,
      		accounts.TotalPages,
      		accounts.HasNext,
      		accounts.HasPrevious
      	};
      
      	Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(metadata));
      
      	_logger.LogInfo($"Returned {accounts.TotalCount} owners from database.");
      
      	return Ok(accounts);
      }
      And due to the inheritance of the paging parameters through the QueryStringParameters class, we get the same behavior. Neat. That's about it, let's summarize.

      Conclusion

      Paging is a useful and important concept in building any API out there. Without it, our application would probably slow down considerably or just plain drop dead. The solution we've implemented is not perfect, far from it, but you got the point. We've isolated different parts of the paging mechanism and we can go even further and make it a bit more generic. But you can do it as an exercise and implement it in your own project. You can also find one frontend application of paging in our Angular Material paging article. In this article we've covered:
      • The easiest way to implement pagination in ASP.NET Core Web API
      • Tested the solution in a real-world scenario
      • Improved that solution by introducing PagedList entity and separated our parameters for different controllers
      Hope you liked this article and that you've learned something new or useful from it. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48879 0 0 0 1661 var owner = await Task.Run(() => { return _repository.Owner.GetOwners(ownerParameters);});. Thank you!]]> 0 0 1664 https://code-maze.com/ 1661 0 1670 RepositoryBase.cs, where this can be write if following repository pattern with modelview, and how can use it if I have other lists like List of Student, List of Owner, List of Teacher.]]> 0 0 1671 https://code-maze.com/ 1670 0 1863 Vladimir Pecanac for help.. In your blog the scenario defined as Generic repository, wrapper classes. Where as in my case it is not generic classes for repository. Here is example given below: public interface ISchoolsRepository { Task<ICollection> GetAllSchoolAsync(); } and Repository Class: public class SchoolsRepository : ISchoolsRepository { public readonly learning_gpsContext _GpsContext; public SchoolsRepository(learning_gpsContext GpsContext) { _GpsContext = GpsContext; } public async Task<ICollection> GetAllSchoolAsync() { List results = null; var result = PagdeList.Create(_GpsContext.School, 1, 10); results = await _GpsContext.School.AsNoTracking().ToListAsync(); return results; } } and my controller is look like this: [HttpGet] public async Task GetSchoolAll() { var schools = await _schoolsRepository.GetAllSchoolAsync(); List schoolsVms = new List(); foreach (Schools school in schools) { schoolsVms.Add(new SchoolsVm { Id = school.ID.ToString(), Name = school.Name, creatDate = school.CreatedAt.ToString() }); } return Ok(schoolsVms); } Any suggestion to do pagination in the given situation. Although your tutorial is very clear and well articulated but if I will follow your blog, I will have to do lots of changes in my projects.]]> 1671 0
      Filtering in ASP.NET Core Web API https://code-maze.com/filtering-aspnet-core-webapi/ Mon, 02 Dec 2019 07:05:17 +0000 https://code-maze.com/?p=48937 paging, filtering is still an important part of a flexible REST API, so we need to know how to implement it in our API projects. Filtering helps us get the exact result set we want instead of all the results without any criteria. The starting point source code for this article can be found on the GitHub repo. For the finished project, switch to the filtering final branch. NOTE: Some degree of previous knowledge is needed to follow this article. It relies heavily on the ASP.NET Core Web API series on Code Maze, so if you are not sure how to set up the database or how the underlying architecture works, we strongly suggest you go through the series. In this article we are going to learn: So let's get right into it.

      What is Filtering?

      Filtering is a mechanism to retrieve results by providing some kind of criterium. We can write many kinds of filters to get the results by type of class property, value range, date range or anything else. When implementing filtering, you are always restricted by the predefined set of options you can set in your request. For example, you can send a date value to request an owner account type, but you won't have much success. On the front end, filtering is usually implemented as checkboxes, radio buttons or dropdowns. This kind of implementation limits you to only those options that are available to create a valid filter. Take for example a car-selling website. When filtering the cars you want, you would ideally want to select:
      • Car manufacturer as a category from a list or a dropdown
      • Car model from a list or a dropdown
      • Is it new or used with radio buttons
      • The city where the seller is as a dropdown
      • The price of the car is an input field (numeric)
      • ....
      You get the point. So the request would look something like this:
      https://bestcarswebsite.com/sale?manufacturer=ford&model=expedition&state=used&city=washington&price_from=30000&price_to=50000
      Or even like this:
      https://bestcarswebsite.com/sale/filter?data[manufacturer]=ford&[model]=expedition&[state]=used&[city]=washington&[price_from]=30000&[price_to]=50000
      Or anything else that makes sense to you the most. The API needs to parse the filter, so we don't need to get too crazy with it :). Now that we know what filtering is, let's see how it's different from searching.

      How is Filtering Different from Searching

      When searching for results you usually have only one input and that's the one you use to search for anything within a website. So in other words, you send a string to the API, and API is responsible for using that string to find any results that match it. On our car website, we would use the search field to find the "Ford Expedition" car model, and we would get all the results that match the car name "Ford Expedition". This would return every "Ford Expedition" car available. We can also improve the search by implementing search terms like Google does it for example. If the user enters Ford Expedition without quotes in the search field, we would return both what's relevant to Ford and Expedition. But, if the user puts the quotes around it, we would search the entire term "Ford Expedition" in our database. It makes for a better user experience. Example: https://bestcarswebsite.com/sale/search?name=ford focus Using search doesn't mean we can't use filters together with it. It makes perfect sense to use the filtering and searching together, so we need to take that into account when writing our source code. But enough of the theory. Let's implement some filters.

      How to Implement Filtering in ASP.NET Core Web API

      We have a DateOfBirth property in our Owner class. Let' say we want to find out which owners are born between the year 1975 and 1997. We also want to be able to enter just the starting year, and not the ending one, and vice versa. We would need a query like this one: https://localhost:5001/api/owner?minYearOfBirth=1975&maxYearOfBirth=1997 But, we want to be able to do this too: https://localhost:5001/api/owner?minYearOfBirth=1975 Or like this: https://localhost:5001/api/owner?maxYearOfBirth=1997 Ok, we have a specification. Let's see how to implement it. We've already implemented paging in our controller so we have the necessary infrastructure to extend it with the filtering functionality. We've used the OwnerParameters class to define the query parameters for our paging request. Let's extend our OwnerParameters class to support filtering too:
      public class OwnerParameters : QueryStringParameters
      {
      	public uint MinYearOfBirth { get; set; }
      	public uint MaxYearOfBirth { get; set; } = (uint)DateTime.Now.Year;
      
      	public bool ValidYearRange => MaxYearOfBirth > MinYearOfBirth;
      }
      We've added two unsigned int properties (to avoid negative year values), MinYearOfBirth and MaxYearOfBirth. Since the default uint value is 0, we don't need to explicitly define it, 0 is okay in this case. For the MaxYearOfBirthproperty, we want to set it to the current year. If we don't get it through the query params, we have something to work with. It doesn't matter if someone sets the year to 3053 through the params, it won't affect the results. We've also added a simple validation property - ValidYearRange. Its purpose is to tell us if the max year is indeed greater then min year. If it's not, we want to let the API user know that he/she is doing something wrong. Okay, now that we have our parameters ready, we can extend the controller:
      [HttpGet]
      public IActionResult GetOwners([FromQuery] OwnerParameters ownerParameters)
      {
      	if (!ownerParameters.ValidYearRange)
      	{
      		return BadRequest("Max year of birth cannot be less than min year of birth");
      	}
      
      	var owners = _repository.Owner.GetOwners(ownerParameters);
      
      	var metadata = new
      	{
      		owners.TotalCount,
      		owners.PageSize,
      		owners.CurrentPage,
      		owners.TotalPages,
      		owners.HasNext,
      		owners.HasPrevious
      	};
      
      	Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(metadata));
      
      	_logger.LogInfo($"Returned {owners.TotalCount} owners from database.");
      
      	return Ok(owners);
      }
      As you can see, there's not much to it, we've added our validation check and a BadRequest response with a short message to the API user. That should do it for the controller. Let's get to the implementation in our OwnerRepository class:
      public PagedList<Owner> GetOwners(OwnerParameters ownerParameters)
      {
      	var owners = FindByCondition(o => o.DateOfBirth.Year >= ownerParameters.MinYearOfBirth &&
      								o.DateOfBirth.Year <= ownerParameters.MaxYearOfBirth)
      							.OrderBy(on => on.Name);
      
      	return PagedList<Owner>.ToPagedList(owners,
      		ownerParameters.PageNumber,
      		ownerParameters.PageSize);
      }
      Actually, at this point, the implementation is rather simple too. We are using FindByCondition method to find all the owners with the DateOfBirth between MaxYearOfBirth and MinYearOfBirth. Pretty simple hah? Let's try it out.

      Sending and Testing Some Requests

      Like the specification states, we have 3 use cases to test. For the reference, these are the owners in our database: database owners First, let's test just the minYearOfBirth parameter. https://localhost:5001/api/owner?minYearOfBirth=1975 In this case, we shouldn't see Anna Bosh in our results. The second test should be just the maxYearOf Birth. https://localhost:5001/api/owner?maxYearOfBirth=1997 Now, Nick Somion should not appear amongst our owners. And the final test is to include both minYearOfBirth and maxYearOfBirth. https://localhost:5001/api/owner?minYearOfBirth=1975&maxYearOfBirth=1997 In this case, neither Anna or Nick should be in the results. We should also check if our validation works: https://localhost:5001/api/owner?minYearOfBirth=1975&maxYearOfBirth=1974 We should get a bad request with the message that the range is invalid. To top this off we can combine filtering with our current paging solution. https://localhost:5001/api/owner?minYearOfBirth=1975&maxYearOfBirth=1997&pageSize=2&pageNumber=2 Can you guess what the result is (hint: it's a single person)? If you've guessed, let us know in the comments. That's it, we've tested all the relevant cases.

      Conclusion

      We've covered another important concept when building RESTful APIs. Certainly not important as paging is, but filtering is often needed in APIs because we can restrict the results to only the ones we are interested in. The solution is rather simple when you know what to do. It was also easy to implement since we had an infrastructure set up already in our paging article. In this article we've learned:
      • What filtering is
      • How it's different from searching
      • How to implement filtering in ASP.NET Core
      • Tested our implementation
      Hopefully, you've learned something new and useful. In the next article, we'll cover searching. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48937 0 0 0 1636 0 0 1640 OData as an option which handles paging, filtering, sorting and more fairly seamlessly.]]> 0 0 1648 1640 0 1660 1648 0 1662 https://code-maze.com/ 1640 0 1663 https://code-maze.com/ 1636 0
      Searching in ASP.NET Core Web API https://code-maze.com/searching-aspnet-core-webapi/ Mon, 09 Dec 2019 07:00:23 +0000 https://code-maze.com/?p=48963 Lucene.NET which are already optimized and proven. You can find the source code on the GitHub repo. If you want to follow along with the article, you can use the start branch and if you want to get the final solution or if you get stuck, switch to the final branch. NOTE: Some degree of previous knowledge is needed to follow this article. It relies heavily on the ASP.NET Core Web API series on Code Maze, so if you are not sure how to set up the database or how the underlying architecture works, we strongly suggest you go through the series. In this article we are going to learn: Let's dive right into it.

      What is Searching?

      There is no doubt in our minds that you've seen a search field on almost every website on the internet. It's easy to find something when we are familiar with the website structure or when a website is not that large. But, if we want to find the most relevant topic for us, or if we don't know what we're going to find, or maybe we're first-time visitors of a large website, we're probably going to use a search field. Basic search is not that hard to implement, but if taken lightly, the search function can be useless. Sometimes it's better to just revert back to Google, or use a google dork to search the website. If you are not familiar with Google dorks, we highly recommend you try them out. In our simple project, one use case of a search would be to find an owner by his/her name. Let's see how we can achieve that.

      How to Implement Searching in ASP.NET Core Web API

      Since we're going to implement the most basic search in our project, the implementation won't be complex at all. We have all we need infrastructure-wise since we already covered paging and filtering. We'll just extend our implementation a bit. What we want to achieve is something like this: https://localhost:5001/api/owners?name=Anna Bosh This should return just one result: Anna Bosh. Of course, the search needs to work together with filtering and paging, so that's one of the things that we need to keep in mind too. Like we did with filtering, we're going to extend our OwnerParameters class first since we're going to send our search query as a query parameter:
      public class OwnerParameters : QueryStringParameters
      {
      	public uint MinYearOfBirth { get; set; }
      	public uint MaxYearOfBirth { get; set; } = (uint)DateTime.Now.Year;
      
      	public bool ValidYearRange => MaxYearOfBirth > MinYearOfBirth;
      
      	public string Name { get; set; }
      }
      We've added just one new property - Name. Simple as that. Now we can write queries with name="term" in them. Next thing we need to do is to actually implement the search functionality in our OwnerRepository class:
      public PagedList<Owner> GetOwners(OwnerParameters ownerParameters)
      {
      	var owners = FindByCondition(o => o.DateOfBirth.Year >= ownerParameters.MinYearOfBirth &&
      								o.DateOfBirth.Year <= ownerParameters.MaxYearOfBirth);
      
      	SearchByName(ref owners, ownerParameters.Name);
      
      	return PagedList<Owner>.ToPagedList(owners.ToList().OrderBy(on => on.Name),
      		ownerParameters.PageNumber,
      		ownerParameters.PageSize);
      }
      
      private void SearchByName(ref IQueryable<Owner> owners, string ownerName)
      {
      	if (!owners.Any() || string.IsNullOrWhiteSpace(ownerName))
      		return;
      
      	owners = owners.Where(o => o.Name.ToLower().Contains(ownerName.Trim().ToLower()));
      }
      First, we need to check if the name parameter is actually sent, by doing a simple IsNullOrWhiteSpace check on the Name property. If it's not, there's no point in searching for anything really. After that, we are using the Where clause with trimmed Name string (just in case). We are doing this after the filtering is done to search through fewer results. Everything else stays the same. That's it for our implementation. As you can see it really isn't that hard since it is the most basic search, and we already had an infrastructure set.

      Testing Our Implementation

      The only thing that remains is to test our solution. First, let's recall how our database table looks. database owners Now, let's try to find Anna: https://localhost:5001/api/owners?name=Anna Bosh Sure enough, we get our result. search result If we entered her full name, Anna Bosh, the result would be the same. That's because we are looking for the entire term by default with .Contains(). For an additional example, let's try to find all the owners that contain the letter "o": https://localhost:5001/api/owner?name=o Now, we should get three results instead of one:
      [
        {
          "id": "261e1685-cf26-494c-b17c-3546e65f5620",
          "name": "Anna Bosh",
          "dateOfBirth": "1974-11-14T00:00:00",
          "address": "27 Colored Row"
        },
        {
          "id": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
          "name": "John Keen",
          "dateOfBirth": "1980-12-05T00:00:00",
          "address": "61 Wellfield Road"
        },
        {
          "id": "66774006-2371-4d5b-8518-2177bcf3f73e",
          "name": "Nick Somion",
          "dateOfBirth": "1998-12-15T00:00:00",
          "address": "North sunny address 102"
        }
      ]
      Now let's combine that with filtering and paging: https://localhost:5001/api/owner?name=o&minYearOfBirth=1974&maxYearOfBith=1985&pageSize=1&pageNumber=2 Can you guess which result we should get (hint, it's a single result)? If you've guessed, leave us a comment in the comments section. That's it, we've successfully implemented and tested our search functionality. Let's sum that up.

      Conclusion

      In this article we've covered:
      • What searching is and how it's different from filtering
      • The way to implement searching in ASP.NET Core Web API
      • How to test searching in our API
      • Combining search with our existing solutions for paging and filtering
      Hope this short article was useful to you. If you have any questions or suggestions, please leave us a comment and we'll get to you as quickly as possible. Next up, we'll cover sorting. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48963 0 0 0
      How to Implement Sorting in ASP.NET Core Web API https://code-maze.com/sorting-aspnet-core-webapi/ Mon, 16 Dec 2019 07:00:34 +0000 https://code-maze.com/?p=48970 start branch and if you want to get the final solution or if you get stuck, switch to the final branch. NOTE: Some degree of previous knowledge is needed to follow this article. It relies heavily on the ASP.NET Core Web API series on Code Maze, so if you are not sure how to set up the database or how the underlying architecture works, we strongly suggest you go through the series. In this article we are going to learn: Let's begin.

      What is Sorting?

      Sorting, in this case, refers to ordering our results in a preferred way, using our query string parameters. We are not talking about sorting algorithms nor we are going into how's of implementing a sorting algorithm. What we're interested in, however, is how do we make our API sort our results the way we want it to. Let's say we want our API to sort owners by their name in ascending order, and then by their date of birth. To do that, our API call needs to look something like this: https://localhost:5001/api/owner?orderBy=name,dateOfBirth desc Our API needs to take all the parameters into consideration and sort our results accordingly. In our case, this means sorting results by their name, and then, if there are owners with the same name, sorting them by the DateOfBirth property. Let's recall how our owner data looks like in the database: database owners For the sake of demonstrating this example, we are going to add one more Anna Bosh to our database. You can add whatever you want besides that to test the results. So, let's add another Anna Bosh: database owners extended The new Anna is 10 years older and lives on a different address. Great, now we have the required data to test our functionality properly. And of course, like with all other functionalities we've implemented so far (paging, filtering, and searching), we need to implement this to work well with everything else. We should be able to get the paginated, filtered and sorted data for example. Let's see one way to go around implementing this.

      How to Implement Sorting in ASP.NET Core Web API

      First, since we want every Entity to be sortable by some criterium, we are going to add OrderBy property to our base class QueryStringParameters:
      public abstract class QueryStringParameters
      {
      	const int maxPageSize = 50;
      	public int PageNumber { get; set; } = 1;
      
      	private int _pageSize = 10;
      	public int PageSize
      	{
      		get
      		{
      			return _pageSize;
      		}
      		set
      		{
      			_pageSize = (value > maxPageSize) ? maxPageSize : value;
      		}
      	}
      
      	public string OrderBy { get; set; }
      }
      We won't set any default values since different classes can have different default values. For example, we want OwnerParameters to order our results by the property "name" by default:
      public class OwnerParameters : QueryStringParameters
      {
      	public OwnerParameters()
      	{
      		OrderBy = "name";
      	}
      
      	public uint MinYearOfBirth { get; set; }
      	public uint MaxYearOfBirth { get; set; } = (uint)DateTime.Now.Year;
      
      	public bool ValidYearRange => MaxYearOfBirth > MinYearOfBirth;
      
      	public string Name { get; set; }
      }
      And we want our Accounts to be ordered by DateCreated:
      public class AccountParameters : QueryStringParameters
      {
      	public AccountParameters()
      	{
      		OrderBy = "DateCreated";
      	}
      }
      Next, we're going to dive right into the implementation of our sorting mechanism, or rather, our ordering mechanism. One thing to note is that we'll be using System.Linq.Dynamic.Core NuGet package to dynamically create our OrderBy query on the fly. So, feel free to install it in the Repository project and add a using directive in the OwnerRepository class. Let's add a new private method ApplySort in our OwnerRepository class:
      private void ApplySort(ref IQueryable<Owner> owners, string orderByQueryString)
      {
      	if (!owners.Any())
      		return;
      
      	if (string.IsNullOrWhiteSpace(orderByQueryString))
      	{
      		owners = owners.OrderBy(x => x.Name);
      		return;
      	}
      
      	var orderParams = orderByQueryString.Trim().Split(',');
      	var propertyInfos = typeof(Owner).GetProperties(BindingFlags.Public | BindingFlags.Instance);
      	var orderQueryBuilder = new StringBuilder();
      
      	foreach (var param in orderParams)
      	{
      		if (string.IsNullOrWhiteSpace(param))
      			continue;
      
      		var propertyFromQueryName = param.Split(" ")[0];
      		var objectProperty = propertyInfos.FirstOrDefault(pi => pi.Name.Equals(propertyFromQueryName, StringComparison.InvariantCultureIgnoreCase));
      
      		if (objectProperty == null)
      			continue;
      
      		var sortingOrder = param.EndsWith(" desc") ? "descending" : "ascending";
      
      		orderQueryBuilder.Append($"{objectProperty.Name.ToString()} {sortingOrder}, ");
      	}
      
      	var orderQuery = orderQueryBuilder.ToString().TrimEnd(',', ' ');
      
      	if (string.IsNullOrWhiteSpace(orderQuery))
      	{
      		owners = owners.OrderBy(x => x.Name);
      		return;
      	}
      
      	owners = owners.OrderBy(orderQuery);
      }
      Okay, that's one big ass method. We're actually doing multiple different things here to get our results sorted, so let's take it step-by-step and see what've done exactly. Let's dissect our method.

      Implementation - Step by Step

      First, let start with the method definition. It has two arguments, one for the list of owners as IQueryable<Owner>, and other for the ordering query. If we send a request like this one https://localhost:5001/api/owner?orderBy=name,dateOfBirth desc, our orderByQueryString will be name,dateOfBirth desc. We begin our method implementation with some mandatory checks for owners and queryString. If there are no owners, we exit the method immediately. If we've got some owners, but the query string is empty, we want to order the owners by name:
      if (!owners.Any())
      	return;
      
      if (string.IsNullOrWhiteSpace(orderByQueryString))
      {
      	owners = owners.OrderBy(x => x.Name);
      	return;
      }
      Next, we're splitting our query string to get the individual fields:
      var orderParams = orderByQueryString.Trim().Split(',');
      We're also using a bit of reflection to prepare the list of PropertyInfo objects that represent the properties of our Owner class. We need them to be able to check if the field received through the query string really exists in the Owner class:
      var propertyInfos = typeof(Owner).GetProperties(BindingFlags.Public | BindingFlags.Instance);
      Having that prepared, we can actually run through all the parameters and check for their existence:
      if (string.IsNullOrWhiteSpace(param))
      	continue;
      
      var propertyFromQueryName = param.Split(" ")[0];
      var objectProperty = propertyInfos.FirstOrDefault(pi => pi.Name.Equals(propertyFromQueryName, StringComparison.InvariantCultureIgnoreCase));
      If we don't find such a property we skip the step in the foreach loop and go to the next parameter in the list:
      if (objectProperty == null)
      	continue;
      If we do find the property, we return it and additionally check if our parameter contains "desc" at the end of the string. We use that to decide how we should order our property:
      var sortingOrder = param.EndsWith(" desc") ? "descending" : "ascending";
      We use the StringBuilder to build our query with each loop:
      orderQueryBuilder.Append($"{objectProperty.Name.ToString()} {sortingOrder}, ");
      Now that we've looped through all the fields, we are removing excess commas and doing one last check to see if our query indeed has something in it:
      var orderQuery = orderQueryBuilder.ToString().TrimEnd(',', ' ');
      
      if (string.IsNullOrWhiteSpace(orderQuery))
      {
      	owners = owners.OrderBy(x => x.Name);
      	return;
      }
      Finally, we can order our query:
      owners = owners.OrderBy(orderQuery);
      At this point, our orderQuery variable should contain "Name ascending, DateOfBirth descending" string. That means it will order our results first by Name in ascending order, and then by DateOfBirth in descending order. The standard LINQ query for this would be:
      owners.OrderBy(x => x.Name).ThenByDescending(o => o.DateOfBirth);
      A neat little trick to form a query when you don't know how you should sort in advance.

      Calling the Method

      Now that we've seen how the method works up close, we should just call it in our GetOwners method:
      public PagedList<Owner> GetOwners(OwnerParameters ownerParameters)
      {
      	var owners = FindByCondition(o => o.DateOfBirth.Year >= ownerParameters.MinYearOfBirth &&
      								o.DateOfBirth.Year <= ownerParameters.MaxYearOfBirth);
      
      	SearchByName(ref owners, ownerParameters.Name);
      
      	ApplySort(ref owners, ownerParameters.OrderBy);
      
      	return PagedList<Owner>.ToPagedList(owners,
      		ownerParameters.PageNumber,
      		ownerParameters.PageSize);
      }
      We are providing the list of owners and the OrderBy query string. That's it for our implementation. Or maybe not? What if we want to use ApplySort in the AccountRepository? Should we actually keep this logic in our repository class? The answer to both our questions is of course NO! Let's see how to make our method more generic, and implement it in such a way that it can be used by both Account and Owner repositories (or any other repository that comes later).

      Improving the Solution

      There are two main things we want to improve on. Kick the private ApplySort method out of the OwnerRepository, and make the ApplySort generic. So let's start by defining the SortHelper class and the ISortHelper interface in the Helpers folder of the Entities project. ISortHelper should declare one method - ApplySort:
      public interface ISortHelper<T>
      {
      	IQueryable<T> ApplySort(IQueryable<T> entities, string orderByQueryString);
      }
      As you can see, ISortHelper is a generic interface and it can be applied to any type we want. We need to provide a collection of entities, and a sorting string. Now let's see the actual implementation:
      public class SortHelper<T> : ISortHelper<T>
      {
      	public IQueryable<T> ApplySort(IQueryable<T> entities, QueryStringParameters queryString)
      	{
      		if (!entities.Any())
      			return entities;
      
      		if (string.IsNullOrWhiteSpace(queryString.OrderBy))
      		{
      			return entities;
      		}
      
      		var orderParams = queryString.OrderBy.Trim().Split(',');
      		var propertyInfos = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
      		var orderQueryBuilder = new StringBuilder();
      
      		foreach (var param in orderParams)
      		{
      			if (string.IsNullOrWhiteSpace(param))
      				continue;
      
      			var propertyFromQueryName = param.Split(" ")[0];
      			var objectProperty = propertyInfos.FirstOrDefault(pi => pi.Name.Equals(propertyFromQueryName, StringComparison.InvariantCultureIgnoreCase));
      
      			if (objectProperty == null)
      				continue;
      
      			var sortingOrder = param.EndsWith(" desc") ? "descending" : "ascending";
      
      			orderQueryBuilder.Append($"{objectProperty.Name.ToString()} {sortingOrder}, ");
      		}
      
      		var orderQuery = orderQueryBuilder.ToString().TrimEnd(',', ' ');
      
      		return entities.OrderBy(orderQuery);
      	}
      }
      This implementation gives us the ability to inject this class to our repositories and call ApplySort wherever we need it:
      private ISortHelper<Owner> _sortHelper;
      
      public OwnerRepository(RepositoryContext repositoryContext, ISortHelper<Owner> sortHelper)
      	: base(repositoryContext)
      {
      	_sortHelper = sortHelper;
      }
      We also need to extend our RepoWrapper since our repository classes are instantiated there:
      public class RepositoryWrapper : IRepositoryWrapper
      {
      	private RepositoryContext _repoContext;
      	private IOwnerRepository _owner;
      	private IAccountRepository _account;
      	private ISortHelper<Owner> _ownerSortHelper;
      	private ISortHelper<Account> _accountSortHelper;
      
      	public IOwnerRepository Owner
      	{
      		get
      		{
      			if (_owner == null)
      			{
      				_owner = new OwnerRepository(_repoContext, _ownerSortHelper);
      			}
      
      			return _owner;
      		}
      	}
      
      	public IAccountRepository Account
      	{
      		get
      		{
      			if (_account == null)
      			{
      				_account = new AccountRepository(_repoContext, _accountSortHelper);
      			}
      
      			return _account;
      		}
      	}
      
      	public RepositoryWrapper(RepositoryContext repositoryContext,
      		ISortHelper<Owner> ownerSortHelper,
      		ISortHelper<Account> accountSortHelper)
      	{
      		_repoContext = repositoryContext;
      		_ownerSortHelper = ownerSortHelper;
      		_accountSortHelper = accountSortHelper;
      	}
      
      	public void Save()
      	{
      		_repoContext.SaveChanges();
      	}
      }
      And now we can call it in our GetOwners method:
      public PagedList<Owner> GetOwners(OwnerParameters ownerParameters)
      {
      	var owners = FindByCondition(o => o.DateOfBirth.Year >= ownerParameters.MinYearOfBirth &&
      								o.DateOfBirth.Year <= ownerParameters.MaxYearOfBirth);
      
      	SearchByName(ref owners, ownerParameters.Name);
      
      	_sortHelper.ApplySort(owners, ownerParameters.OrderBy);
      
      	return PagedList<Owner>.ToPagedList(owners,
      		ownerParameters.PageNumber,
      		ownerParameters.PageSize);
      }
      Same thing with the AccountRepository. If you are having trouble implementing it, refer to the finished project on GitHub at the top of the article. And since we used dependency injection to inject our SortHelper, we need to not forget to register it in our ServiceExtensions class:
      public static void ConfigureRepositoryWrapper(this IServiceCollection services)
      {
      	services.AddScoped<ISortHelper<Owner>, SortHelper<Owner>>();
      	services.AddScoped<ISortHelper<Account>, SortHelper<Account>>();
      
      	services.AddScoped<IRepositoryWrapper, RepositoryWrapper>();
      }
      A lot of work, but it's worth it, now we can apply sorting to any entity in our project, even if we add new ones in the future. Let's test it out.

      Testing Our Implementation

      Now the fun part starts, let's put the result of all our efforts to test. First, let's try out the query we've been using as an example: https://localhost:5001/api/owner?orderBy=name,dateOfBirth desc The response should be:
      [
          {
              "id": "261e1685-cf26-494c-b17c-3546e65f5620",
              "name": "Anna Bosh",
              "dateOfBirth": "1974-11-14T00:00:00",
              "address": "27 Colored Row"
          },
          {
              "id": "9c362f85-5581-4182-ac96-b7b88a74dda7",
              "name": "Anna Bosh",
              "dateOfBirth": "1964-11-14T00:00:00",
              "address": "24 Crescent Street"
          },
          {
              "id": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
              "name": "John Keen",
              "dateOfBirth": "1980-12-05T00:00:00",
              "address": "61 Wellfield Road"
          },
          {
              "id": "f98e4d74-0f68-4aac-89fd-047f1aaca6b6",
              "name": "Martin Miller",
              "dateOfBirth": "1983-05-21T00:00:00",
              "address": "3 Edgar Buildings"
          },
          {
              "id": "66774006-2371-4d5b-8518-2177bcf3f73e",
              "name": "Nick Somion",
              "dateOfBirth": "1998-12-15T00:00:00",
              "address": "North sunny address 102"
          },
          {
              "id": "a3c1880c-674c-4d18-8f91-5d3608a2c937",
              "name": "Sam Query",
              "dateOfBirth": "1990-04-22T00:00:00",
              "address": "91 Western Roads"
          }
      ]
      As you can see, the list is sorted by Name ascending, and since we have two Anna's, they were sorted by DateOfBirth, descending. Now try reversing the ordering in the query: https://localhost:5001/api/owner?orderBy=name desc,dateOfBirth Now the result should be:
      [
          {
              "id": "a3c1880c-674c-4d18-8f91-5d3608a2c937",
              "name": "Sam Query",
              "dateOfBirth": "1990-04-22T00:00:00",
              "address": "91 Western Roads"
          },
          {
              "id": "66774006-2371-4d5b-8518-2177bcf3f73e",
              "name": "Nick Somion",
              "dateOfBirth": "1998-12-15T00:00:00",
              "address": "North sunny address 102"
          },
          {
              "id": "f98e4d74-0f68-4aac-89fd-047f1aaca6b6",
              "name": "Martin Miller",
              "dateOfBirth": "1983-05-21T00:00:00",
              "address": "3 Edgar Buildings"
          },
          {
              "id": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
              "name": "John Keen",
              "dateOfBirth": "1980-12-05T00:00:00",
              "address": "61 Wellfield Road"
          },
          {
              "id": "9c362f85-5581-4182-ac96-b7b88a74dda7",
              "name": "Anna Bosh",
              "dateOfBirth": "1964-11-14T00:00:00",
              "address": "24 Crescent Street"
          },
          {
              "id": "261e1685-cf26-494c-b17c-3546e65f5620",
              "name": "Anna Bosh",
              "dateOfBirth": "1974-11-14T00:00:00",
              "address": "27 Colored Row"
          }
      ]
      Works like a charm! Now, you can try different invalid queries like: https://localhost:5001/api/owner?orderBy=age https://localhost:5001/api/owner?orderBy=  https://localhost:5001/api/owner?orderBy=name desc&dateOfBirth and see the results for yourself. One more thing we should try is if the solution works when combined with paging, filtering and searching. https://localhost:5001/api/owner?pageNumber=1&pageSize=5&orderBy=dateOfBirth asc&minYearOfBirth=1960&maxYearOfBirth=1980&name=Anna Can you guess what's the result of this query? If you've guessed, leave us a comment with what you think the result is. That's it, let's summarize.

      Conclusion

      As you have seen, even the trivial sort like this requires a certain amount of business logic, some reflection, validation and even a bit of dynamic query building. But once you implement it, your API becomes really versatile and flexible. This implementation is as simple as it gets, but there are certain things we can do to improve it. We can implement a service layer (we don't have one to keep things simple), we can make our code generic, and not just owner-specific, and we can, of course, create an extension method ApplySort for IQueryable which would make this solution even more flexible. In this article we've covered:
      • The definition of sorting and how it works
      • One way to implement sorting in ASP.NET Core Web API
      • Testing our solution for valid and invalid queries
      • Testing if our solution works together with paging, filtering and searching
      That's it for sorting. If you have any questions or suggestions, please leave us a comment. Next up, we'll cover a very nice topic of data shaping. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      48970 0 0 0 1906 0 0
      Data Shaping in ASP.NET Core Web API https://code-maze.com/data-shaping-aspnet-core-webapi/ Mon, 23 Dec 2019 07:00:22 +0000 https://code-maze.com/?p=49017 sorting article. Data shaping is not something that every API needs, but it can be very useful in some cases. If you want to follow along with the article, you can use the start branch and if you want to get the final solution or if you get stuck, switch to the final branch. NOTE: Some degree of previous knowledge is needed to follow this article. It relies heavily on the ASP.NET Core Web API series on Code Maze, so if you are not sure how to set up the database or how the underlying architecture works, we strongly suggest you go through the series. In this article we are going to learn: Let's start by learning what data shaping is exactly.

      What is Data Shaping

      Data shaping is a great way to reduce the amount of traffic sent from the API to the client. It enables the consumer of the API to select (shape) the data by choosing the fields through the query string. What we mean by this is something like: https://localhost:5001/api/owner?fields=name,dateOfBirth This would tell the API to return a list of owners with ONLY the Name and DateOfBirth fields of the owner entity. In our case, the owner entity doesn't have too many fields to choose from, but you can see how this can be very helpful when an entity has a lot of fields in it. It's not that uncommon. By giving the consumer a way to select just the fields it needs, we can potentially reduce the stress on the API. On the other hand, this is not something that every API needs, so we need to think carefully and decide whether we should implement its implementation has a bit of reflection going on. And we know for a fact that reflection takes its toll and slows our application down. Finally, as always, data-shaping should work well together with the concepts we've covered so far - paging, filtering, searching, and sorting. Let's get to work.

      How to Implement Data Shaping in ASP.NET Core Web API

      First things first, we need to extend our QueryStringParameters class since we are going to add a new feature to our query string and we want it to be available for any entity:
      public abstract class QueryStringParameters
      {
      	const int maxPageSize = 50;
      	public int PageNumber { get; set; } = 1;
      
      	private int _pageSize = 10;
      	public int PageSize
      	{
      		get
      		{
      			return _pageSize;
      		}
      		set
      		{
      			_pageSize = (value > maxPageSize) ? maxPageSize : value;
      		}
      	}
      
      	public string OrderBy { get; set; }
      
      	public string Fields { get; set; }
      }
      We've added the Fields property and now we can use fields as a query string parameter. Next on, similarly to what we did with sorting, we are going to do here. But, we'll make this generic to start with. We'll make the IDataShaper.cs and DataShaper.cs in the Helpers folder of the Entities project: First, let's create the IDataShaper interface:
      public interface IDataShaper<T>
      {
      	IEnumerable<ExpandoObject> ShapeData(IEnumerable<T> entities, string fieldsString);
      	ExpandoObject ShapeData(T entity, string fieldsString);
      }
      The IDataShaper defines two methods that should be implemented, one for the single entity, and one for the collection of entities. Both are named ShapeData but they have different signatures. Notice how we use the ExpandoObjecttype as a return type. We need to do that in order to shape our data how we want it. And now, let's see the actual implementation:
      public class DataShaper<T> : IDataShaper<T>
      {
      	public PropertyInfo[] Properties { get; set; }
      
      	public DataShaper()
      	{
      		Properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
      	}
      
      	public IEnumerable<ExpandoObject> ShapeData(IEnumerable<T> entities, string fieldsString)
      	{
      		var requiredProperties = GetRequiredProperties(fieldsString);
      
      		return FetchData(entities, requiredProperties);
      	}
      
      	public ExpandoObject ShapeData(T entity, string fieldsString)
      	{
      		var requiredProperties = GetRequiredProperties(fieldsString);
      
      		return FetchDataForEntity(entity, requiredProperties);
      	}
      
      	private IEnumerable<PropertyInfo> GetRequiredProperties(string fieldsString)
      	{
      		var requiredProperties = new List<PropertyInfo>();
      
      		if (!string.IsNullOrWhiteSpace(fieldsString))
      		{
      			var fields = fieldsString.Split(',', StringSplitOptions.RemoveEmptyEntries);
      
      			foreach (var field in fields)
      			{
      				var property = Properties.FirstOrDefault(pi => pi.Name.Equals(field.Trim(), StringComparison.InvariantCultureIgnoreCase));
      
      				if (property == null)
      					continue;
      
      				requiredProperties.Add(property);
      			}
      		}
      		else
      		{
      			requiredProperties = Properties.ToList();
      		}
      
      		return requiredProperties;
      	}
      
      	private IEnumerable<ExpandoObject> FetchData(IEnumerable<T> entities, IEnumerable<PropertyInfo> requiredProperties)
      	{
      		var shapedData = new List<ExpandoObject>();
      
      		foreach (var entity in entities)
      		{
      			var shapedObject = FetchDataForEntity(entity, requiredProperties);
      			shapedData.Add(shapedObject);
      		}
      
      		return shapedData;
      	}
      
      	private ExpandoObject FetchDataForEntity(T entity, IEnumerable<PropertyInfo> requiredProperties)
      	{
      		var shapedObject = new ExpandoObject();
      
      		foreach (var property in requiredProperties)
      		{
      			var objectPropertyValue = property.GetValue(entity);
      			shapedObject.TryAdd(property.Name, objectPropertyValue);
      		}
      
      		return shapedObject;
      	}
      }
      Let's break this class down.

      Implementation - Step by Step

      We have one public property in this class - Properties. It's an array of PropertyInfo's that we're going to pull out of the input type, whatever it is, Account or Owner in our case.
      public PropertyInfo[] Properties { get; set; }
      
      public DataShaper()
      {
      	Properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
      }
      So here it is, on class instantiation, we get all the properties of an input class. Next on, we have the implementation of our two public methods ShapeData:
      public IEnumerable<ExpandoObject> ShapeData(IEnumerable<T> entities, string fieldsString)
      {
      	var requiredProperties = GetRequiredProperties(fieldsString);
      
      	return FetchData(entities, requiredProperties);
      }
      
      public ExpandoObject ShapeData(T entity, string fieldsString)
      {
      	var requiredProperties = GetRequiredProperties(fieldsString);
      
      	return FetchDataForEntity(entity, requiredProperties);
      }
      They both look similar and rely on the GetRequiredProperties method to parse the input string that contains the fields we want to fetch. The GetRequiredProperties method is a method that does the magic. It parses the input string and returns just the properties we need to return to the controller:
      private IEnumerable<PropertyInfo> GetRequiredProperties(string fieldsString)
      {
      	var requiredProperties = new List<PropertyInfo>();
      
      	if (!string.IsNullOrWhiteSpace(fieldsString))
      	{
      		var fields = fieldsString.Split(',', StringSplitOptions.RemoveEmptyEntries);
      
      		foreach (var field in fields)
      		{
      			var property = Properties.FirstOrDefault(pi => pi.Name.Equals(field.Trim(), StringComparison.InvariantCultureIgnoreCase));
      
      			if (property == null)
      				continue;
      
      			requiredProperties.Add(property);
      		}
      	}
      	else
      	{
      		requiredProperties = Properties.ToList();
      	}
      
      	return requiredProperties;
      }
      As you can see, there's nothing special about it. If the fieldsString is not empty, we split it and check if the fields match the properties in our entity. If they do, we add them to the list of required properties. On the other hand, if the fieldsString is empty, we consider all properties are required. Now, FetchData and FetchDataForEntity are the private methods to extract the values from these required properties we've prepared: FetchDataForEntity does it for a single entity:
      private ExpandoObject FetchDataForEntity(T entity, IEnumerable<PropertyInfo> requiredProperties)
      {
      	var shapedObject = new ExpandoObject();
      
      	foreach (var property in requiredProperties)
      	{
      		var objectPropertyValue = property.GetValue(entity);
      		shapedObject.TryAdd(property.Name, objectPropertyValue);
      	}
      
      	return shapedObject;
      }
      As you can see, we loop through the requiredProperties and then using a bit of reflection, we extract the values and add them to our ExpandoObject. ExpandoObject implements IDictionary<string,object> so we can use the TryAdd method to add our property using its name as a key, and the value as a value for the dictionary. This way we dynamically add just the properties we need to our dynamic object. The FetchData method is just an implementation for multiple objects. It utilizes the FetchDataForEntity method we've just implemented:
      private IEnumerable<ExpandoObject> FetchData(IEnumerable<T> entities, IEnumerable<PropertyInfo> requiredProperties)
      {
      	var shapedData = new List<ExpandoObject>();
      
      	foreach (var entity in entities)
      	{
      		var shapedObject = FetchDataForEntity(entity, requiredProperties);
      		shapedData.Add(shapedObject);
      	}
      
      	return shapedData;
      }
      Nothing special here, just looping through the list of entities and returning a collection of shaped entities as a result. That's it for the implementation, let's see how do we connect all this into our existing solution.

      Connecting the Dots

      Now that we've implemented the logic we can inject our data shaper in the repositories as we did with ISortHelper:
      private IDataShaper<Owner> _dataShaper;
      
      public OwnerRepository(RepositoryContext repositoryContext, 
      	ISortHelper<Owner> sortHelper,
      	IDataShaper<Owner> dataShaper)
      	: base(repositoryContext)
      {
      	_sortHelper = sortHelper;
      	_dataShaper = dataShaper;
      }
      And then apply data shaping in the GetOwners method:
      public PagedList<ExpandoObject> GetOwners(OwnerParameters ownerParameters)
      {
      	var owners = FindByCondition(o => o.DateOfBirth.Year >= ownerParameters.MinYearOfBirth &&
      								o.DateOfBirth.Year <= ownerParameters.MaxYearOfBirth);
      
      	SearchByName(ref owners, ownerParameters.Name);
      
      	_sortHelper.ApplySort(owners, ownerParameters.OrderBy);
      	var shapedOwners = _dataShaper.ShapeData(owners, ownerParameters.Fields);
      
      	return PagedList<ExpandoObject>.ToPagedList(shapedOwners,
      		ownerParameters.PageNumber,
      		ownerParameters.PageSize);
      }
      Create another GetOwnerById method that shapes the data since we still need our regular method for validation checks in the controller actions:
      public SerializableExpando GetOwnerById(Guid ownerId, string fields)
      {
      	var owner = FindByCondition(owner => owner.Id.Equals(ownerId))
      		.DefaultIfEmpty(new Owner())
      		.FirstOrDefault();
      
      	return _dataShaper.ShapeData(owner, fields);
      }
      And don't forget to modify the IOwnerRepository interface to reflect these changes:
      public interface IOwnerRepository : IRepositoryBase<Owner>
      {
      	PagedList<ExpandoObject> GetOwners(OwnerParameters ownerParameters);
      	ExpandoObject GetOwnerById(Guid ownerId, string fields);
      	Owner GetOwnerById(Guid ownerId);
      	void CreateOwner(Owner owner);
      	void UpdateOwner(Owner dbOwner, Owner owner);
      	void DeleteOwner(Owner owner);
      }
      You can try changing AccountRepository on your own for practice. If you get stuck, check out the finished project. And of course, since we've modified the repository classes constructors, we need to modify our RepositoryWrapper too:
      public class RepositoryWrapper : IRepositoryWrapper
      {
      	private RepositoryContext _repoContext;
      	private IOwnerRepository _owner;
      	private IAccountRepository _account;
      	private ISortHelper<Owner> _ownerSortHelper;
      	private ISortHelper<Account> _accountSortHelper;
      	private IDataShaper<Owner> _ownerDataShaper;
      	private IDataShaper<Account> _accountDataShaper;
      
      	public IOwnerRepository Owner
      	{
      		get
      		{
      			if (_owner == null)
      			{
      				_owner = new OwnerRepository(_repoContext, _ownerSortHelper, _ownerDataShaper);
      			}
      
      			return _owner;
      		}
      	}
      
      	public IAccountRepository Account
      	{
      		get
      		{
      			if (_account == null)
      			{
      				_account = new AccountRepository(_repoContext, _accountSortHelper, _accountDataShaper);
      			}
      
      			return _account;
      		}
      	}
      
      	public RepositoryWrapper(RepositoryContext repositoryContext,
      		ISortHelper<Owner> ownerSortHelper,
      		ISortHelper<Account> accountSortHelper,
      		IDataShaper<Owner> ownerDataShaper,
      		IDataShaper<Account> accountDataShaper)
      	{
      		_repoContext = repositoryContext;
      		_ownerSortHelper = ownerSortHelper;
      		_accountSortHelper = accountSortHelper;
      		_ownerDataShaper = ownerDataShaper;
      		_accountDataShaper = accountDataShaper;
      	}
      
      	public void Save()
      	{
      		_repoContext.SaveChanges();
      	}
      }
      Using dependency injection means we need to register our data shapers in the ConfigureRepositoryWrapper method of the ServiceExtensions class order to resolve them:
      public static void ConfigureRepositoryWrapper(this IServiceCollection services)
      {
      	services.AddScoped<ISortHelper<Owner>, SortHelper<Owner>>();
      	services.AddScoped<ISortHelper<Account>, SortHelper<Account>>();
      
      	services.AddScoped<IDataShaper<Owner>, DataShaper<Owner>>();
      	services.AddScoped<IDataShaper<Account>, DataShaper<Account>>();
      
      	services.AddScoped<IRepositoryWrapper, RepositoryWrapper>();
      }
      And because we've done such an awesome job, we don't need to change our GetOwners action in the OwnerController, but we do need to make a slight change to validation in the GetOwnerById action:
      [HttpGet("{id}", Name = "OwnerById")]
      public IActionResult GetOwnerById(Guid id, [FromQuery] string fields)
      {
      	var owner = _repository.Owner.GetOwnerById(id, fields);
      
      	if (owner == default(ExpandoObject))
      	{
      		_logger.LogError($"Owner with id: {id}, hasn't been found in db.");
      		return NotFound();
      	}
      
      	return Ok(owner);
      }
      As you can see the changes aren't that drastic. Everything is set up now. We just need to run this and it will work for sure. Or will it? As a matter of fact, it won't :)

      Resolving Json Serialization Problems

      Now, if we try to run the application as it is right now, we will get a big fat InvalidCastException. The reason for that is that System.Text doesn't support the casting of ExpandoObject to IDictionary, which is what we need to happen when we return the result from the controller. To avoid this, we need to add Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package and then in the ConfigureServices method of our Startup.cs class find the services.AddControllers() line and add .AddNewtonsoftJson()method to the end of it:
      public void ConfigureServices(IServiceCollection services)
      {
      	services.ConfigureCors();
      
      	services.ConfigureIISIntegration();
      
      	services.ConfigureLoggerService();
      
      	services.ConfigureMySqlContext(Configuration);
      
      	services.ConfigureRepositoryWrapper();
      
      	services.AddControllers(config =>
      	{
      		config.RespectBrowserAcceptHeader = true;
      		config.ReturnHttpNotAcceptable = true;
      	}).AddXmlDataContractSerializerFormatters()
      	.AddNewtonsoftJson();
      }
      What this does is replace the default System.Text.Json serializer with Newtonsoft.Json one. The exception should be gone now and the application should work normally again. While we are at it, we are going to configure the application to respect browser headers, return 406 Not Acceptable if an unknown media type is requested, and use a data contract XML serializer because we want to support content negotiation. This solves the problem with the json serialization. But let's test our solution, to see if it actually does what we specified.

      Testing Our Solution

      Before we test our solution, let's quickly take a look at how our Owner table looks like right now: database owners extended Great, these entries should do it. First, let's send a plain GET request to our owners' endpoint: https://localhost:5001/api/owner We should get a full response back:
      [
          {
              "Id": "261e1685-cf26-494c-b17c-3546e65f5620",
              "Name": "Anna Bosh",
              "DateOfBirth": "1974-11-14T00:00:00",
              "Address": "27 Colored Row"
          },
          {
              "Id": "9c362f85-5581-4182-ac96-b7b88a74dda7",
              "Name": "Anna Bosh",
              "DateOfBirth": "1964-11-14T00:00:00",
              "Address": "24 Crescent Street"
          },
          {
              "Id": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
              "Name": "John Keen",
              "DateOfBirth": "1980-12-05T00:00:00",
              "Address": "61 Wellfield Road"
          },
          {
              "Id": "f98e4d74-0f68-4aac-89fd-047f1aaca6b6",
              "Name": "Martin Miller",
              "DateOfBirth": "1983-05-21T00:00:00",
              "Address": "3 Edgar Buildings"
          },
          {
              "Id": "66774006-2371-4d5b-8518-2177bcf3f73e",
              "Name": "Nick Somion",
              "DateOfBirth": "1998-12-15T00:00:00",
              "Address": "North sunny address 102"
          },
          {
              "Id": "a3c1880c-674c-4d18-8f91-5d3608a2c937",
              "Name": "Sam Query",
              "DateOfBirth": "1990-04-22T00:00:00",
              "Address": "91 Western Roads"
          }
      ]
      So our data shaping functionality hasn't changed the default application behavior. Now, let's see data shaping in action: https://localhost:5001/api/owner?fields=name,dateOfBirth The API should return the shaped data:
      [
          {
              "Name": "Sam Query",
              "DateOfBirth": "1990-04-22T00:00:00"
          },
          {
              "Name": "Nick Somion",
              "DateOfBirth": "1998-12-15T00:00:00"
          },
          {
              "Name": "Martin Miller",
              "DateOfBirth": "1983-05-21T00:00:00"
          },
          {
              "Name": "John Keen",
              "DateOfBirth": "1980-12-05T00:00:00"
          },
          {
              "Name": "Anna Bosh",
              "DateOfBirth": "1974-11-14T00:00:00"
          },
          {
              "Name": "Anna Bosh",
              "DateOfBirth": "1964-11-14T00:00:00"
          }
      ]
      And now to top it off, let's see if it works with paging, filtering, searching and sorting: https://localhost:5001/api/owner?fields=name,dateOfBirth&pageSize=2&pageNumber=1&orderBy=name asc,dateOfBirth desc&maxYearOfBirth=1970 This query should return only one result. Can you guess which one? If you've guessed, leave us a comment with what you think the result is. That's it, we've tested our API successfully. One more thing to test. Let's try to request an XML result.

      Resolving XML Serialization Problems

      Let's change the Accept header to application/xml and send a request. We want to test out if our content negotiation works. We are going to send a simple request this time: https://localhost:5001/api/owner/a3c1880c-674c-4d18-8f91-5d3608a2c937 And the response looks like this:
      <ArrayOfKeyValueOfstringanyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
          <KeyValueOfstringanyType>
              <Key>Id</Key>
              <Value xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/" i:type="d3p1:guid">a3c1880c-674c-4d18-8f91-5d3608a2c937</Value>
          </KeyValueOfstringanyType>
          <KeyValueOfstringanyType>
              <Key>Name</Key>
              <Value xmlns:d3p1="http://www.w3.org/2001/XMLSchema" i:type="d3p1:string">Sam Query</Value>
          </KeyValueOfstringanyType>
          <KeyValueOfstringanyType>
              <Key>DateOfBirth</Key>
              <Value xmlns:d3p1="http://www.w3.org/2001/XMLSchema" i:type="d3p1:dateTime">1990-04-22T00:00:00</Value>
          </KeyValueOfstringanyType>
          <KeyValueOfstringanyType>
              <Key>Address</Key>
              <Value xmlns:d3p1="http://www.w3.org/2001/XMLSchema" i:type="d3p1:string">91 Western Roads</Value>
          </KeyValueOfstringanyType>
      </ArrayOfKeyValueOfstringanyType>
      As you can see that looks pretty ugly and unreadable. But that's how our XmlDataContractSerializerOutputFormatter serializes our ExpandoObject by default. So do we want to fix this and how do we do it? This is a topic that's quite outside of the scope of this article, but you can find the solution in the finished project source code. A simple explanation is that we need to create our own dynamic object and define XML serialization rules for it. So we need to create something like this:
      public class Entity : DynamicObject, IXmlSerializable, IDictionary<string, object>
      {
      	//...
      	//implementation
      	//...
      }
      The main thing to notice here is that we inherit from DynamicObject that will make our object dynamic, IXmlSerializable interface, which we need to implement custom serialization rules and IDictionary<string, object> because of the Add method that is needed for XML serialization. All that's left is to replace the ExpandoObject type to the Entity type throughout our project. Now, we should get a response like this one:
      <Entity xmlns="http://schemas.datacontract.org/2004/07/Entities.Models">
          <Id>a3c1880c-674c-4d18-8f91-5d3608a2c937</Id>
          <Name>Sam Query</Name>
          <DateOfBirth>4/22/1990 12:00:00 AM</DateOfBirth>
          <Address>91 Western Roads</Address>
      </Entity>
      That looks much nicer, doesn't it? If the XML serialization is not important to you, you can keep using ExpandoObject, but if you want a nicely formatted XML response, this is a way to go. Ok, let's summarize what we've done so far.

      Conclusion

      Data shaping is an exciting and neat little feature that can really make our APIs flexible and reduce our network traffic. If we have a high volume traffic API, data shaping should work just fine. On the other hand, it's not a feature that we should use lightly because it utilizes reflection and dynamic typing to get things done. As with all other functionalities, we need to be careful when and if we should implement data shaping. Performance tests might come in handy even if we do implement it. In this article we've covered:
      • What data shaping is
      • How to implement a generic data shaping solution in ASP.NET Core Web API
      • How to fix our json serialization problems with ExpandoObject
      • Testing of our solution by sending some simple requests
      • Formatting our XML responses
      If you found some parts unclear, we suggest taking a quick look at the other parts of this mini-series: paging, filtering, searching, and sorting. Paging article is especially important since we set up the infrastructure for the whole series in that article. Hopefully, you've learned something new and interesting this time. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      49017 0 0 0
      Configuring and Using Swagger UI in ASP.NET Core Web API https://code-maze.com/swagger-ui-asp-net-core-web-api/ Mon, 04 Nov 2019 06:30:14 +0000 https://code-maze.com/?p=49098 In this article, we are going to learn how to integrate Swagger UI in an ASP.NET Core Web API application.  The source code for this article is provided on our GitHub repository. We have divided this article into the following sections:

      The Need for Documenting our API

      Developers who consume our API might be trying to solve important business problems with it. Hence it is very important for them to understand how to use our API effectively. This is where API documentation comes into the picture. The API documentation is the process of giving instructions about how to effectively use and integrate an API. Hence it can be thought of as a concise reference manual containing all the information required to work with the API, with details about the functions, classes, return types, arguments and more, supported by tutorials and examples. So having proper documentation for our API enables the consumers to integrate our APIs as quickly as possible and move forward with their development. Furthermore, this also helps them to understand the value and usage of our API, improve the chances for our API adoption and make our APIs easier to maintain and support.

      Swagger/Open API

      Swagger is a language-agnostic specification for describing REST APIs. The Swagger is also referred to as OpenAPI. It allows us to understand the capabilities of a service without looking at the actual implementation code.  Swagger minimizes the amount of work needed while integrating an API. Similarly, it also helps API developers to document their APIs quickly and accurately.

      Swagger Specification

      Swagger Specification is an important part of the Swagger flow. By default, a document named swagger.json is generated by the Swagger tool which is based on our API. It describes the capabilities of our API and how to access it via HTTP.

      Swagger UI

      Swagger UI offers a web-based UI that provides information about the service. This is built using the Swagger Specification and embedded inside the Swashbuckle package and hence it can be hosted in our ASP.NET Core app using middlewares.

      Integrating Swagger UI into our Applications

      We can use the Swashbuckle package to easily integrate Swagger into our .NET Core Web API projects. It will generate the Swagger specification for our project. Additionally, the Swagger UI is also contained within Swashbuckle. There are three main components in the Swashbuckle package: Swashbuckle.AspNetCore.Swagger: This contains the Swagger object model and the middleware to expose SwaggerDocument objects as JSON. Swashbuckle.AspNetCore.SwaggerGen: A Swagger generator that builds SwaggerDocument objects directly from our routes, controllers, and models.  Swashbuckle.AspNetCore.SwaggerUI: An embedded version of the Swagger UI tool. It interprets Swagger JSON to build a rich, customizable experience for describing the web API functionality.

      Installing the Package

      The first step is to install the Swashbuckle package. We can execute the following command in the Package Manager Console window: Install-Package Swashbuckle.AspNetCore -version 5.0.0-rc4 This will install the Swashbuckle package in our application.

      Configuring the Swagger Middleware

      The next step is to configure the Swagger Middleware. Let’s make the following changes in the ConfigureServices() method of the Startup.cs class:
      public void ConfigureServices(IServiceCollection services)
      {
          // Register the Swagger generator, defining 1 or more Swagger documents
          services.AddSwaggerGen(c =>
          {
              c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });                
          });
      
          services.AddControllers();
      }
      This adds the Swagger generator to the services collection. In the Configure() method, let's enable the middleware for serving the generated JSON document and the Swagger UI:
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
          // Enable middleware to serve generated Swagger as a JSON endpoint.
          app.UseSwagger();
      
          // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
          // specifying the Swagger JSON endpoint.
          app.UseSwaggerUI(c =>
          {
              c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
          });
      }
      By executing these steps, The Swagger is configured and ready to use in our project.

      Exploring the Swagger UI

      First, we are going to create an Employee class:
      public class Employee
      {
          public int Id { get; set; }
      
          public string FirstName { get; set; }
      
          public string LastName { get; set; }
      
          public string EmailId { get; set; }
      }
      Then, let's create an API controller with action methods. We are going to use some mock data and keep the focus on understanding Swagger’s capabilities:
      [Route("api/[controller]")]
      [ApiController]
      public class EmployeeController : ControllerBase
      {
          // GET: api/Employee
          [HttpGet]
          public IEnumerable<Employee> Get()
          {
              return GetEmployees();
          }
      
          // GET: api/Employee/5
          [HttpGet("{id}", Name = "Get")]
          public Employee Get(int id)
          {
              return GetEmployees().Find(e => e.Id == id);
          }
      
          // POST: api/Employee
          [HttpPost]
          [Produces("application/json")]
          public Employee Post([FromBody] Employee employee)
          {
              // Logic to create new Employee
              return new Employee();
          }
      
          // PUT: api/Employee/5
          [HttpPut("{id}")]
          public void Put(int id, [FromBody] Employee employee)
          {
              // Logic to update an Employee
          }
      
          // DELETE: api/Employee/5
          [HttpDelete("{id}")]
          public void Delete(int id)
          {
          }
      
      
          private List<Employee> GetEmployees()
          {
              return new List<Employee>()
              {
                  new Employee()
                  {
                      Id = 1,
                      FirstName= "John",
                      LastName = "Smith",
                      EmailId ="John.Smith@gmail.com"
                  },
                  new Employee()
                  {
                      Id = 2,
                      FirstName= "Jane",
                      LastName = "Doe",
                      EmailId ="Jane.Doe@gmail.com"
                  }
              };
          }
      }
      Now, let’s run the app and navigate to https://localhost:<port>/swagger/v1/swagger.json. We can see that a document describing the endpoints is generated: swagger-json The Swagger UI can be found at http://localhost:<port>/swagger: swagger-ui Now we can explore the API via the Swagger UI and it will be easier to incorporate it into other applications. We can see each controller and its action methods listed here. Once we click on an action method, we can see detailed information like parameters, response, and example values. There is also an option to try out each of those action methods: swagger-ui-action-method By clicking on the Try it out button, we can test the endpoint and see the response: swagger-ui-try-it-out In this section, we’ve explored the Swagger UI in detail. Now we’re going to look at options for extending and customizing it.

      Extending and Customizing 

      Swagger provides options for extending the documentation and customizing the UI.

      Extending the documentation

      Let’s look at the various options to extend the documentation.

      API Info & Description

      First, let’s see how we can specify the API info and description. The configuration action passed to the AddSwaggerGen() method adds information such as Contact, License, and Description. Let's provide some values for those: 
      // This method gets called by the runtime. Use this method to add services to the container.
      public void ConfigureServices(IServiceCollection services)
      {
          // Register the Swagger generator, defining 1 or more Swagger documents
          services.AddSwaggerGen(c =>
          {
              c.SwaggerDoc("v1", new OpenApiInfo
              {
                  Title = "Employee API",
                  Version = "v1",
                  Description = "An API to perform Employee operations",
                  TermsOfService = new Uri("https://example.com/terms"),
                  Contact = new OpenApiContact
                  {
                      Name = "John Walkner",
                      Email = "John.Walkner@gmail.com",
                      Url = new Uri("https://twitter.com/jwalkner"),
                  },
                  License = new OpenApiLicense
                  {
                      Name = "Employee API LICX",
                      Url = new Uri("https://example.com/license"),
                  }
              });
          });
      }
      Now let’s run the application once again and explore the Swagger UI: swagger-ui-api-info We can see that the Swagger document is now updated with API Info.

      XML Comments

      For enabling XML comments, we need to do the following steps:
      1. In the Build tab of the project properties, check the box labeled XML documentation file. Let’s keep the auto-generated file path.
      2. Suppress warning 1591, which will now give warnings about any method, class, or field that doesn't have triple-slash comments.
      generate-xml-documentation-setting In the ConfigureServices() method, configure Swagger to use the XML file that's generated in the above step:
      public void ConfigureServices(IServiceCollection services)
      {
          // Register the Swagger generator, defining 1 or more Swagger documents
          services.AddSwaggerGen(c =>
          {
              c.SwaggerDoc("v1", new OpenApiInfo
              {
                  Title = "Employee API",
                  Version = "v1",
                  Description = "An API to perform Employee operations",
                  TermsOfService = new Uri("https://example.com/terms"),
                  Contact = new OpenApiContact
                  {
                      Name = "John Walkner",
                      Email = "John.Walkner@gmail.com",
                      Url = new Uri("https://twitter.com/jwalkner"),
                  },
                  License = new OpenApiLicense
                  {
                      Name = "Employee API LICX",
                      Url = new Uri("https://example.com/license"),
                  }
              });
      
      // Set the comments path for the Swagger JSON and UI.
              var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
              var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
              c.IncludeXmlComments(xmlPath);
          });
      
          services.AddControllers();
      }
      Now, adding triple-slash comments to the action method enhances the Swagger UI by adding a description to the section header. Let’s add a summary:
      /// <summary>
      /// Gets the list of all Employees.
      /// </summary>
      /// <returns>The list of Employees.</returns>
      // GET: api/Employee
      [HttpGet]
      public IEnumerable<Employee> Get()
      {
          return GetEmployees();
      }
      This displays the summary against the action method: action-method-with-summary We can additionally add <remarks> element to the documentation. It supplements information specified in the <summary> element and provides a more robust Swagger UI. The <remarks> element content can consist of text, JSON, or XML:
      /// <summary>
      /// Creates an Employee.
      /// </summary>
      /// <remarks>
      /// Sample request:
      /// 
      ///     POST api/Employee
      ///     {        
      ///       "firstName": "Mike",
      ///       "lastName": "Andrew",
      ///       "emailId": "Mike.Andrew@gmail.com"        
      ///     }
      /// </remarks>
      /// <param name="employee"></param>        
      [HttpPost]
      [Produces("application/json")]
      public Employee Post([FromBody] Employee employee)
      {
          // Logic to create new Employee
          return new Employee();
      }
      This will enhance the UI with additional info: action-method-with-summary-and-remarks We have looked at how to enhance the documentation using XML comments.

      Using Data Annotations

      We can decorate a model with attributes to enhance the documentation. Let’s add a [Required] attribute to the EmailId field of the Employee model:
      public class Employee
      {
          public int Id { get; set; }
      
          public string FirstName { get; set; }
      
          public string LastName { get; set; }
      
          [Required]
          public string EmailId { get; set; }
      }
      This gets reflected in the Swagger UI: required-attribute-in-swagger-ui

      Describing Response Types

      The developers who consume our APIs are usually more interested in what it returns- specifically the response types and error codes. Hence it is very important to describe our response types.  These are denoted using XML comments & data annotations. Let's enhance the response types a little bit:
      /// <summary>
      /// Creates an Employee.
      /// </summary>
      /// <remarks>
      /// Sample request:
      /// 
      ///     POST api/Employee
      ///     {        
      ///       "firstName": "Mike",
      ///       "lastName": "Andrew",
      ///       "emailId": "Mike.Andrew@gmail.com"        
      ///     }
      /// </remarks>
      /// <param name="employee"></param>
      /// <returns>A newly created employee</returns>
      /// <response code="201">Returns the newly created item</response>
      /// <response code="400">If the item is null</response>          
      [HttpPost]
      [ProducesResponseType(201)]
      [ProducesResponseType(400)]
      [Produces("application/json")]
      public Employee Post([FromBody] Employee employee)
      {
          // Logic to create new Employee
          return new Employee();
      }
      This will reflect in the Responses section: repsonse-types In this section, we’ve looked at various options for extending the documentation.

      Customizing the UI

      The default UI of Swagger is pretty good. But we can customize it If we wish to do so. We may change the documentation pages to represent our brand or theme. Branding the Swagger components requires adding the resources to serve static files and building the folder structure to host those files. First of all, we are going to enable static file middleware in the Configure() method in Startup.cs file:
      app.UseStaticFiles();
      After that, let’s acquire the contents of the dist folder from the Swagger UI GitHub repository. This folder contains the necessary assets for the Swagger UI page. Let’s create a wwwroot/swagger/ui folder, and copy the contents of the dist folder into it. Additionally, let’s create a custom.css file in wwwroot/swagger/ui with the following CSS to customize the page header:
      .swagger-ui .topbar {
          background-color: grey;
          border-bottom: 3px solid black;
      }
      We have to reference custom.css in the index.html file inside ui folder, after any other CSS files:
      <link rel="stylesheet" type="text/css" href="./swagger-ui.css">
      <link rel="stylesheet" type="text/css" href="custom.css">
      Finally, let’s browse to the index.html page at https://localhost:<port>/swagger/ui/index.html, enter https://localhost:<port>/swagger/v1/swagger.json in the header's textbox, and click the Explore button.  We can see that the UI is now customized with the changes we made: customized-ui In this section, we have looked at options for customizing the Swagger UI.

      Conclusion

      We have looked at the following topics in this article:
      • The need for documenting our APIs
      • Swagger/Open API - Swagger Specification & Swagger UI
      • Integrating Swagger UI into an ASP.NET Core Web API
      • Extending the Swagger Documentation
      • Customizing the Swagger UI
      [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      49098 0 0 0 1612 0 0 1613 1612 0 1646 { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); }); app.UseHttpsRedirection(); app.UseMvc(); }]]> 0 0 1647 1646 0 1649 0 0 1650 1647 0 1651 1647 0 1652 1650 0 1653 1651 0 1654 1649 0 1655 1652 0 1656 options.InjectStylesheet("/custom.css");, this way I don't need to use different url for swagger ui. Thank you!]]> 0 0 1657 1656 0 1665 1654 0 1666 1657 0 1667 1666 0
      System Testing a REST API using C# and DalSoft Rest Client https://code-maze.com/system-testing-rest-api-dalsoft-restclient/ Mon, 11 Nov 2019 06:00:38 +0000 https://code-maze.com/?p=49432 unit test series. The main focus point of this article will be how to perform system testing on your REST API. If you haven’t used DalSoft RestClient before you can learn about it by reading this post. The code examples can found in this GitHub repository. In this article we will learn: Let’s get into it.

      About System Testing

      There are four types of testing: system, integration, unit, and acceptance testing. The type of testing we're going to cover in this post is System Testing. System Testing is a form of "Black Box" testing which means testing without knowing anything about the internals of the System Under Test (SUT). It’s usually the last gate before deploying changes into production. System Testing is usually done on an environment that is as close to production as possible such as a staging or UAT (User Acceptance Testing) environment. Some people even system test using their production environment as part of a continual QA process. For our example, we are going to test the JSONPlaceholder API. Let's start by installing the DalSoft RestClient Package first.

      Install the DalSoft RestClient Package

      There are two easy ways to do this: Install via .NET CLI: dotnet add package DalSoft.RestClient Or Install via NuGet through the Package Management Console: Install-Package DalSoft.RestClient That is all we need in terms of the required libraries.

      Optionally Create an "SDK" to Test your REST API

      For our example, we are going to create an "SDK" to system test the JSONPlaceholder API. This is optional because we could just use Rest Client’s dynamic features instead. To create the SDK we are going to make use of Rest Client’s Resource method. To do this all we need to do is create a Resource class. A Resource class has properties and methods returning strings that represent our resources. We then can use the methods and properties in the Resource method as an expression. Here is an example of our simple SDK:
      public class UserResource
      {
         public string GetUsers() => "users";
      
         public string GetUser(int userId) => $"{GetUsers()}/{userId}";
              
         public string GetUserPosts(int userId) => $"{GetUser(userId)}/posts";
      }

      Use Verify in our System Tests

      Now that we have a Resource class, all we need to do to call the JSONPlaceholder API is pass it as a generic parameter to the Resource method along with an expression parameter representing the Resource, and finally, call the HTTP method we want. This sounds harder than it is and it can all be wrapped up easily in a couple of lines of code:
      var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
      // Get all users from jsonplaceholder API
      var response = await client.Resource<UserResource>(api => api.GetUsers()).Get();
      Now we know we can call the JSONPlaceholder API easily it’s time to write some system tests. This is easily done using the Verify method. To use the Verify method you provide a generic parameter to cast your response to (usually either your Model, a string or HttpResponseMessage) and an expression returning a boolean representing the condition you want to verify. Let’s write some tests. Verify that the resource /users/1 returns a 200 OK status code:
      [Fact]
      public async Task Get_UserWithId1_ReturnsOkStatusCode()
      {
         var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
         await client
            .Resource<UserResource>(api => api.GetUser(1)).Get()
            .Verify<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.OK);
      }
      
      Verify that the response body of the JSON returned is what we were expecting:
      [Fact]
      public async Task Get_UserWithId1_ReturnsJsonStringWithUsernameBret()
      {
        var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
         await client
            .Resource<UserResource>(api => api.GetUser(1)).Get()
            .Verify<string>(s => s.Contains("\"username\": \"Bret\""));
      }
      
      We can do one better. We can verify that the response body can be cast to a User object, and the User object returned is what we expect:
      [Fact]
      public async Task Get_UserWithId1_ReturnsUserModelWithUsernameBret()
      {
         var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
         await client
            .Resource<UserResource>(api => api.GetUser(1)).Get()
            .Verify<User>(user => user.Username == "Bret");
      }
      
      You can also chain the Verify methods together to be nice and "Fluent":
      [Fact]
      public async Task Get_UserWithId1_ReturnsUserWithUsernameBretAndOkStatusCode()
      {
        var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
        await client
           .Resource<UserResource>(api => api.GetUser(1)).Get()
           .Verify<User>(user => user.Username == "Bret")
           .Verify<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.OK);
      }
      

      What Happens if a Verify Test Condition Fails?

      So what happens when a Verify test condition fails? Simple AggregateException is thrown and the InnerExceptions contain a VerifiedFailed exception for each failure because an exception is thrown our test fails. The VerifiedFailed exception contains a descriptive message of exactly what condition failed. Let's make our test fail and see what it looks like. Take the last test we did and make it fail:
      [Fact]
      public async Task Get_UserWithId1_ReturnsUserWithUsernameBretAndOkStatusCode()
      {
        var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
        await client
           .Resource<UserResource>(api => api.GetUser(1)).Get()
           .Verify<User>(user => user.Username == "This will Fail")
           .Verify<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.BadRequest);
      }
      
      View live example Running this test will give you the following output: System.AggregateException: One or more errors occurred. (user => (user.Username == "This will Fail") was not verified) (response => (Convert(response.StatusCode, Int32) == 400) was not verified) There are more advanced features for handling Verify failures that we will cover later on in the post.

      Use DalSoft Rest Client’s Dynamic Features

      We mentioned creating the Resource class or "SDK" was optional. This is because if you don’t pass the generic parameter a dynamic object is returned, which you can work with right away! Here’s how it looks, you can start testing a REST API right away with a few lines of code:
      [Fact]
      public async Task Get_UserWithId1_ReturnsDynamicWithUsernameBretAndOkStatusCode()
      {
         var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
         await client
            .Resource("users/1").Get()
            .Verify(userIsBret => userIsBret.username == "Bret")
            .Verify(httpResponseMessageIsOk => httpResponseMessageIsOk.HttpResponseMessage.StatusCode == HttpStatusCode.OK);
      }
      
      View live example You may have noticed the strange variable naming in this test, this is because when using a dynamic object you can’t get the full expression in the VerifyFailed exception message. Instead, we only get the parameter name, so naming it like this tells us what failed. For example, if the test above failed it would look like: System.AggregateException: One or more errors occurred. (dynamic userIsBret => ... was not verified) (dynamic httpResponseMessageIsOk => ... was not verified) You can also mix and match Generic and Dynamic Verify methods:
      [Fact]
      public async Task Get_UserWithId1_ReturnsDynamicWithUsernameBretAndOkStatusCode()
      {
         var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
         await client
            .Resource("users/1").Get()
            .Verify(userIsBret => userIsBret.username == "Bret")
            .Verify(httpResponseMessageIsOk => httpResponseMessageIsOk.HttpResponseMessage.StatusCode == HttpStatusCode.OK)
            .Verify<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.OK);
      }
      

      Fine-Grained Control Using OnVerifyFailed

      We've covered what happens if a Verify test condition fails earlier and for most testing scenarios this should work fine. There are advanced options to handle failed verifications, but it’s worth noting these are designed more for validation than testing. For advanced scenarios, we can use the OnVerifyFailed callback to handle failed verifications. By default instead of throwing the OnVerifyFailed callback just passes an AggregateException (containing VerifiedFailed exceptions) and a response object to an Action you provide. Here’s what it looks like:
      [Fact]
      public async Task Get_UserWithId1Example_TestingUsingOnVerifyFailed()
      {
          var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
           await client
              .Resource<UserResource>(api => api.GetUser(1)).Get()
              .Verify<User>(user => user.Username == "Fail")
              .Verify<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.BadRequest)
              .OnVerifyFailed((aggregateException, response) =>
              {
                          // Handle the failure here
              });
      }
      
      View live example Running the above code your notice that the OnVerifyFailed callback is called and no exceptions are thrown. Like Verify you can use a generic parameter to cast the response:
      [Fact]
      public async Task Get_UserWithId1Example_TestingUsingOnVerifyFailed()
      {
          var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
           await client
              .Resource<UserResource>(api => api.GetUser(1)).Get()
              .Verify<User>(user => user.Username == "Fail")
              .Verify<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.BadRequest)
              .OnVerifyFailed<HttpResponseMessage>((aggregateException, response) =>
              {
                 if (response.StatusCode == HttpStatusCode.BadRequest)
                    // ...
                 else
                    // ...     
              });
      }
      
      You can use the OnVerifyFailed callback to handle and throw by setting the throwOnVerifyFailed parameter to true:
      [Fact]
      public async Task Get_UserWithId1Example_TestingUsingOnVerifyFailed()
      {
          var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
           await client
              .Resource<UserResource>(api => api.GetUser(1)).Get()
              .Verify<User>(user => user.Username == "Fail")
              .Verify<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.BadRequest)
              .OnVerifyFailed((aggregateException, response) =>
              {
                  // Handle the failure here
              }, throwOnVerifyFailed: true);
      }
      
      Running the above test will cause OnVerifyFailed to be called and an exception to be thrown. Where in the chain OnVerifyFailed appears is important. OnVerifyFailed will only handle Verifications above itself in the chain. By default (throwOnVerifyFailed: false) only the first OnVerifyFailed callback will "handle" the failures, and subsequent OnVerifyFailed callbacks in the chain won’t be called. However, we can use the throwOnVerifyFailed parameter to change this behavior.
      [Fact]
      public async Task Get_UserWithId1Example_TestingUsingOnVerifyFailed()
      {
          var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
           await client
              .Resource<UserResource>(api => api.GetUser(1)).Get()
              .Verify<User>(user => user.Username == "Fail")
              .Verify<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.BadRequest)
              .OnVerifyFailed((aggregateException, response) =>
              {
                  // I will be called on Verify failures 
              })
              .OnVerifyFailed((aggregateException, response) =>
              {
                  // I will NOT be called on Verify failures 
              });
      }
      
      In the example below the last invoked OnVerifyFailed callback in the chain decides whether an exception should be thrown. This simply means if the last OnVerifyFailed callback in the chain has Verify failures and throwOnVerifyFailed is true an exception is thrown.
      [Fact]
      public async Task Get_UserWithId1Example_TestingUsingOnVerifyFailed()
      {
          var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
           await client
              .Resource<UserResource>(api => api.GetUser(1)).Get()
              .Verify<User>(user => user.Username == "Fail")
              .Verify<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.BadRequest)
              .OnVerifyFailed((aggregateException, response) =>
              {
                  // I will be called on Verify failures 
              }, throwOnVerifyFailed:true) // Throw exceptions to next handler
              .OnVerifyFailed((aggregateException, response) =>
              {
                  // I WILL be called on Verify failures and will throw an exception
              }, throwOnVerifyFailed:true);
      }
      

      Putting it All Together

      Putting all this together gives us fine-grained control over what to do with the Verify failures. This contrived example shows how to handle individual groups of Verify failures:
      [Fact(DisplayName = "This test is meant to fail")]
      public async Task Get_UserWithId1Example_TestingUsingOnVerifyFailed()
      {
         var client = new RestClient("https://jsonplaceholder.typicode.com", new Config()
                      .SetJsonSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
      
         await client
            .Resource<UserResource>(api => api.GetUser(1)).Get()
            .Verify<User>(user => user.Username == "Fail")
            .OnVerifyFailed<HttpResponseMessage>((aggregateException, response) =>
            {   // Callback invoked if the Verify above fails
                Assert.Equal(1, aggregateException.InnerExceptions.Count);
            }, throwOnVerifyFailed: false)
            .Verify<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.BadRequest)
            .OnVerifyFailed<HttpResponseMessage>((aggregateException, response) =>
            {
               // Because the previous OnVerifyFailed set throwOnVerifyFailed to false callback only invoked if above Verify failure
               Assert.Equal(1, aggregateException.InnerExceptions.Count);
            }, throwOnVerifyFailed: true)
            .Verify<User>(response => response.Username == "Peter")
            .OnVerifyFailed<User>((aggregateException, response) =>
            {
               // Because the previous OnVerifyFailed set throwOnVerifyFailed to true callback invoked if the above Verify and previous Verify fails
               // aggregateException contains above Verify and previous Verify failures
               Assert.Equal(2, aggregateException.InnerExceptions.Count);
            }, throwOnVerifyFailed: true);
               // Last OnVerifyFailed set throwOnVerifyFailed to true so an exception will be thrown 
               // this will contain both the above and previous Verify failures, but not the first failure as throwOnVerifyFailed was set to false 
      }
      
      View live example The first OnVerifyFailed callback only handles the Verify failure directly above it in the chain. The next OnVerifyFailed callback in the chain only handles the Verify failure directly above but sets throwOnVerifyFailed to true. This means the last OnVerifyFailed is called for the Verify failure directly above as well as the previous failure. An exception is thrown because the last OnVerifyFailed callback has throwOnVerifyFailed set to true.

      Use Rest Client Factory in Your Test Fixture

      Creating HttpClient (which Rest Client extends) is expensive. In most cases for system tests, you could use a Static field with a one-time initialized Rest Client. However, it would be better to use HttpClientFactory / RestClientFactory, in a future post, we will explore how to use a Test Fixture with RestClientFactory.

      Conclusion

      In this post, you've learned how to use DalSoft Rest Client to system test a REST API. We built a simple SDK and learned how easy it is to call a REST API right away using Rest Client’s dynamic features. All the source code for this post is in this GitHub repository. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      49432 0 0 0
      Implementing HATEOAS in ASP.NET Core Web API https://code-maze.com/hateoas-aspnet-core-web-api/ Mon, 30 Dec 2019 07:41:35 +0000 https://code-maze.com/?p=49442 paging, filtering, searching, sorting articles, and especially data-shaping and builds upon foundations we've put down in these articles. This article contains some advanced concepts of REST and Web API. If you find yourself lost or like you're missing part of the whole picture, take a step back and take your time to go over these articles first. We'll also link the relevant resource throughout the article if you want to explore some topics further. You can find the source code on the GitHub repo. If you want to follow along with the article, you can use the start branch and if you want to get the final solution or if you get stuck, switch to the finished project. NOTE: Some degree of previous knowledge is needed to follow this article. It relies heavily on the ASP.NET Core Web API series on Code Maze, so if you are not sure how to set up the database or how the underlying architecture works, we strongly suggest you go through the series. In this article we are going to learn: Let's get into it.

      What is HATEOAS and Why Is It so Important?

      HATEOAS (Hypermedia as the Engine of Application State) is a very important REST constraint. Without it, a REST API cannot be considered RESTful and many of the benefits we get by implementing a REST architecture are unavailable. Hypermedia refers to any kind of content that contains links to media types such as documents, images, videos... REST architecture allows us to generate hypermedia links in our responses dynamically and thus make navigation much easier. To put this into perspective, think about a website that uses hyperlinks to help you navigate to different parts of it. You can achieve the same effect with HATEOAS in your REST API. Imagine a website that has a home page and you land on it, but there are no links anywhere. You need to scrape the website or find some other way to navigate it to get to the content you want. We're not saying that the website is the same as a REST API, but you get the point. The power of being able to explore an API on your own can be very useful. Let's see how that actually works.

      Typical Response with HATEOAS Implemented

      Let's say we want to get some Owners from our API. But how do we do that? We don't even know how to get to the owners endpoint. Well first we would go to the only thing we know how to request and that's the root of the application: https://localhost:50001/api Our root endpoint should tell us more about our API, or rather where to start exploring it:
      [
          {
              "href": "http://localhost:5001/api",
              "rel": "self",
              "method": "GET"
          },
          {
              "href": "http://localhost:5001/api/owner",
              "rel": "owner",
              "method": "GET"
          },
          {
              "href": "http://localhost:5000/api/owner",
              "rel": "create_owner",
              "method": "POST"
          }
      ]
      Ok, now we know what is available to us immediately and we can proceed to get existing owners: http://localhost:5001/api/owner And indeed we get the owners:
      [
      	{
      		"Id": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
      		"Name": "John Keen",
      		"DateOfBirth": "1980-12-05T00:00:00",
      		"Address": "61 Wellfield Road",
      		"Links": [
      			{
      				"href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
      				"rel": "self",
      				"method": "GET"
      			},
      			{
      				"href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
      				"rel": "delete_owner",
      				"method": "DELETE"
      			},
      			{
      				"href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
      				"rel": "update_owner",
      				"method": "PUT"
      			}
      		]
      	},
      	{
      		"Id": "261e1685-cf26-494c-b17c-3546e65f5620",
      		"Name": "Anna Bosh",
      		"DateOfBirth": "1974-11-14T00:00:00",
      		"Address": "27 Colored Row",
      		"Links": [
      			{
      				"href": "https://localhost:5001/api/owner/261e1685-cf26-494c-b17c-3546e65f5620",
      				"rel": "self",
      				"method": "GET"
      			},
      			{
      				"href": "https://localhost:5001/api/owner/261e1685-cf26-494c-b17c-3546e65f5620",
      				"rel": "delete_owner",
      				"method": "DELETE"
      			},
      			{
      				"href": "https://localhost:5001/api/owner/261e1685-cf26-494c-b17c-3546e65f5620",
      				"rel": "update_owner",
      				"method": "PUT"
      			}
      		]
      	},
      	"..."
      ]
      As you can see we got the list of our owners, and for each owner all the actions we can perform on them. And so on... So, it's a nice way to make an API self-discoverable and evolvable.

      What is a Link?

      According to RFC5988, a link is "a typed connection between two resources that are identified by Internationalised Resource Identifiers (IRIs)". Simply put we use links to traverse the internet or rather the resources on the internet. Our responses contain an array of Links, which consist of a few properties according to the RFC:
      • href - represents a target URI
      • rel - represents a link relation type, which means it describes how the current context is related to the target resource
      • method - we need an HTTP method to know how to distinguish the same target URIs

      Pros/Cons of Implementing HATEOAS

      So what are all the benefits we can expect when implementing HATEOAS? HATEOAS is not trivial to implement, but the rewards we reap are worth it. Some of the things we can expect to get when we implement HATEOAS:
      • API becomes self-discoverable and explorable
      • A client can use the links to implement its logic, it becomes much easier, and any changes that happen in the API structure are directly reflected onto the client
      • The server drives the application state and URL structure and not vice versa
      • The link relations can be used to point to developer documentation
      • Versioning through hyperlinks becomes easier
      • Reduced invalid state transaction calls
      • API is evolvable without breaking all the clients
      We can do so much with HATEOAS. But since it's not easy to implement all these features, we should keep in mind the scope of our API and if we actually need all this. There is a great difference between a high volume public API and some internal API that is needed to communicate between parts of the same system. As with everything else, the context is everything. Keep it simple. That is more than enough theory for now. Let's get to work and see how the concrete implementation of HATEOAS looks like.

      Extending the Model in Preparation For HATEOAS Implementation

      Let's begin with the concept we know so far, and that's the Link. First, we are going to create the Link entity in the Models folder of the Entities project:
      public class Link
      {
      	public string Href { get; set; }
      	public string Rel { get; set; }
      	public string Method { get; set; }
      
      	public Link()
      	{
      
      	}
      
      	public Link(string href, string rel, string method)
      	{
      		Href = href;
      		Rel = rel;
      		Method = method;
      	}
      }
      Note that we have an empty constructor too. We'll need that for XML serialization purposes, so keep it that way. Next on, we need to create a class that will contain all of our links - LinkResourceBase:
      public class LinkResourceBase
      {
      	public LinkResourceBase()
      	{
      
      	}
      
      	public List<Link> Links { get; set; } = new List<Link>();
      }
      And finally, since our response needs to describe the root of the controller, we need a wrapper for our Links:
      public class LinkCollectionWrapper<T> : LinkResourceBase
      {
      	public List<T> Value { get; set; } = new List<T>();
      
      	public LinkCollectionWrapper()
      	{
      
      	}
      
      	public LinkCollectionWrapper(List<T> value)
      	{
      		Value = value;
      	}
      }
      This class might not make too much sense right now, but stay with us and it will become clear later down the road. For now, let's just assume we wrapped our links in another class for the response representation purposes. Since our response will contain links too now, we need to extend the XML serialization rules, so that our XML response returns the properly formatted links. Without this we would get something like: <Links>System.Collections.Generic.List`1[Entites.Models.Link]<Links>. So, we need to extend the WriteXmlElement method to support Links:
      public void WriteXml(XmlWriter writer)
      {
      	foreach (var key in expando.Keys)
      	{
      		var value = expando[key];
      		WriteLinksToXml(key, value, writer);
      	}
      }
      
      private void WriteLinksToXml(string key, object value, XmlWriter writer)
      {
      	writer.WriteStartElement(key);
      
      	if (value.GetType() == typeof(List<Link>))
      	{
      		foreach (var val in value as List<Link>)
      		{
      			writer.WriteStartElement(nameof(Link));
      			WriteLinksToXml(nameof(val.Href), val.Href, writer);
      			WriteLinksToXml(nameof(val.Method), val.Method, writer);
      			WriteLinksToXml(nameof(val.Rel), val.Rel, writer);
      			writer.WriteEndElement();
      		}
      	}
      	else
      	{
      		writer.WriteString(value.ToString());
      	}
      
      	writer.WriteEndElement();
      }
      As we did in the data shaping article, we won't go into too much detail here since it is out of the scope of the article, but the logic isn't too complicated either. In short, we check if the type is List<Link>, and if it is, we iterate through all the links and call the method recursively to for each of the properties: href, method, and rel. That's all we need for now. We have a solid foundation to implement HATEOAS in our controllers.

      Implementing HATEOAS in ASP.NET Core Web API

      Now, let's head towards our OwnerController and actually implement HATEOAS. First, we need to extend our controller with the LinkGenerator class, which will help us build the links we want:
      private ILoggerManager _logger;
      private IRepositoryWrapper _repository;
      private LinkGenerator _linkGenerator;
      
      public OwnerController(ILoggerManager logger,
      	IRepositoryWrapper repository,
      	LinkGenerator linkGenerator)
      {
      	_logger = logger;
      	_repository = repository;
      	_linkGenerator = linkGenerator;
      }
      Next on, we need to extend our GetOwners method to add the relevant links to the returned owners:
      [HttpGet]
      public IActionResult GetOwners([FromQuery] OwnerParameters ownerParameters)
      {
      	if (!ownerParameters.ValidYearRange)
      	{
      		return BadRequest("Max year of birth cannot be less than min year of birth");
      	}
      
      	var owners = _repository.Owner.GetOwners(ownerParameters);
      
      	var metadata = new
      	{
      		owners.TotalCount,
      		owners.PageSize,
      		owners.CurrentPage,
      		owners.TotalPages,
      		owners.HasNext,
      		owners.HasPrevious
      	};
      
      	Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(metadata));
      
      	_logger.LogInfo($"Returned {owners.TotalCount} owners from database.");
      
      	for (var index = 0; index < owners.Count(); index++)
      	{
      		var ownerLinks = CreateLinksForOwner(owners[index].Id, ownerParameters.Fields);
      		owners[index].Add("Links", ownerLinks);
      	}
      
      	var ownersWrapper = new LinkCollectionWrapper<Entity>(owners);
      
      	return Ok(CreateLinksForOwners(ownersWrapper));
      }
      We iterate through all the owners we've returned and added the relevant links to them. After that, we wrap the owners collection and create links that are important for the entire collection. Now we just need the implementation for CreateLinksForOwner and CreateLinksForOwners methods:
      private IEnumerable<Link> CreateLinksForOwner(Guid id, string fields = "")
      {
      	var links = new List<Link>
      	{
      		new Link(_linkGenerator.GetUriByAction(HttpContext, nameof(GetOwnerById), values: new { id, fields }),
      		"self",
      		"GET"),
      
      		new Link(_linkGenerator.GetUriByAction(HttpContext, nameof(DeleteOwner), values: new { id }),
      		"delete_owner",
      		"DELETE"),
      
      		new Link(_linkGenerator.GetUriByAction(HttpContext, nameof(UpdateOwner), values: new { id }),
      		"update_owner",
      		"PUT")
      	};
      
      	return links;
      }
      
      private LinkCollectionWrapper<Entity> CreateLinksForOwners(LinkCollectionWrapper<Entity> ownersWrapper)
      {
      	ownersWrapper.Links.Add(new Link(_linkGenerator.GetUriByAction(HttpContext, nameof(GetOwners), values: new { }),
      			"self",
      			"GET"));
      
      	return ownersWrapper;
      }
      There are a few things to note here. We need to take the fields into consideration while creating the links since we might be using it in our requests. We are creating the links by using the LinkGenerator's GetUriByAction method which accepts HttpContext, the name of the action, and the values that need to be used to make the URL valid. In the case of the OwnersController, we send the owner id and fields. The response which we expect is exactly as the one we've used as an example. We are doing something similar to our GetOwnerById method:
      [HttpGet("{id}", Name = "OwnerById")]
      public IActionResult GetOwnerById(Guid id, [FromQuery] string fields)
      {
      	var owner = _repository.Owner.GetOwnerById(id, fields);
      
      	if (owner.Id == Guid.Empty)
      	{
      		_logger.LogError($"Owner with id: {id}, hasn't been found in db.");
      		return NotFound();
      	}
      
      	owner.Add("Links", CreateLinksForOwner(owner.Id, fields));
      
      	return Ok(owner);
      }
      The logic is much easier to implement for a single owner since we don't need to wrap it. We are going to do the same for the AccountController, but with a slight difference. As you know the endpoint to the AccountController is a bit different: /api/owner/{ownerId}/account /api/owner/{ownerId}/account/{accountId} We need to take this into consideration while creating our HATEOAS links:
      private List<Link> CreateLinksForAccount(Guid ownerId, Guid id, string fields = "")
      {
      	var links = new List<Link>
      	{
      		new Link(_linkGenerator.GetUriByAction(HttpContext, nameof(GetAccountForOwner), values: new { ownerId, id, fields }),
      		"self",
      		"GET"),
      	};
      
      	return links;
      }
      
      private LinkCollectionWrapper<Entity> CreateLinksForAccounts(LinkCollectionWrapper<Entity> accountsWrapper)
      {
      	accountsWrapper.Links.Add(new Link(_linkGenerator.GetUriByAction(HttpContext, nameof(GetAccountsForOwner), values: new { }),
      			"self",
      			"GET"));
      
      	return accountsWrapper;
      }
      Our AccountController implementation doesn't contain Create, Update or Delete methods so it's much simpler, but, as you can see, in addition to the account id, we need to provide the owner id too, in order to properly generate links. We need to take fields into consideration in this case too. So let's quickly test our implementation and see how it works.

      Testing Our Solution

      Let's begin with the simple query to our owners endpoint: GET https://localhost:5001/api/owner This should return the list of owners with the links attached:
      {
          "value": [
              {
                  "Id": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                  "Name": "John Keen",
                  "DateOfBirth": "1980-12-05T00:00:00",
                  "Address": "61 Wellfield Road",
                  "Links": [
                      {
                          "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                          "rel": "self",
                          "method": "GET"
                      },
                      {
                          "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                          "rel": "delete_owner",
                          "method": "DELETE"
                      },
                      {
                          "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                          "rel": "update_owner",
                          "method": "PUT"
                      }
                  ]
              },
      		...
      		...
      		...
          "links": [
              {
                  "href": "https://localhost:5001/api/owner",
                  "rel": "self",
                  "method": "GET"
              }
          ]
      }
      As you can see, there are all the links we've defined for our owner. The whole collection of owners is wrapped in the "value" entity because right at the end we define a link that describes "self" or rather how to get to the controller. We can extend it anytime we want with other relevant links that are important to the controller. That was the whole point of the wrapper we implemented. Now let's proceed by testing the single owner: GET https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906 This should result in:
      {
          "Id": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
          "Name": "John Keen",
          "DateOfBirth": "1980-12-05T00:00:00",
          "Address": "61 Wellfield Road",
          "Links": [
              {
                  "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                  "rel": "self",
                  "method": "GET"
              },
              {
                  "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                  "rel": "delete_owner",
                  "method": "DELETE"
              },
              {
                  "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                  "rel": "update_owner",
                  "method": "PUT"
              }
          ]
      }
      Just one owner, no wrappers whatsoever. Now, let's try to get the accounts for that owner: GET https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906/account We get the list of that owner's accounts and links to them:
      {
          "value": [
              {
                  "Id": "371b93f2-f8c5-4a32-894a-fc672741aa5b",
                  "DateCreated": "1999-05-04T00:00:00",
                  "AccountType": "Domestic",
                  "OwnerId": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                  "Links": [
                      {
                          "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906/account/371b93f2-f8c5-4a32-894a-fc672741aa5b",
                          "rel": "self",
                          "method": "GET"
                      }
                  ]
              }
      		...
          ],
          "links": [
              {
                  "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906/account",
                  "rel": "self",
                  "method": "GET"
              }
          ]
      }
      And finally let's test a single account: GET https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906/account/371b93f2-f8c5-4a32-894a-fc672741aa5b The result should be pretty obvious at this point:
      {
          "Id": "371b93f2-f8c5-4a32-894a-fc672741aa5b",
          "DateCreated": "1999-05-04T00:00:00",
          "AccountType": "Domestic",
          "OwnerId": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
          "Links": [
              {
                  "href": "https://localhost:5001/api/owner/371b93f2-f8c5-4a32-894a-fc672741aa5b/account/371b93f2-f8c5-4a32-894a-fc672741aa5b",
                  "rel": "self",
                  "method": "GET"
              }
          ]
      }
      One link, describing how to get to that account. As you can see, there are two ids in that link, so we've done a great job in our implementation. Now, the only thing remaining to test is how it works when we select the fields we want: GET https://localhost:5001/api/owner?fields=name And much to our surprise, this doesn't work. Why is that?

      Improving the Solution

      Well, as you can see the HATEOAS implementation relies on having the ids available to construct the links for the response. Data shaping, on the other hand, enables us to return only the fields we want. And in our test scenario, we wanted just the names of the owners. That means we haven't returned the ids of those owners and we cannot construct the target URIs now. To solve this problem we want to make sure that id is always returned to the controller. First, we'll implement a wrapper named ShapedEntity which besides the entity contains the Id property:
      public class ShapedEntity
      {
      	public ShapedEntity()
      	{
      		Entity = new Entity();
      	}
      
      	public Guid Id { get; set; }
      	public Entity Entity { get; set; }
      }
      Next on, we are going to replace all usages of the Entity class in the entire project with the ShapedEntity. The classes in which you need to replace every usage of the Entity class: AccountRepository, IAccountRepository, OwnerRepository, IOwnerRepository, DataShaper, and IDataShaper. In addition to that, we need to extend the FetchDataForEntity method in the DataShaper class to get the id separately:
      private ShapedEntity FetchDataForEntity(T entity, IEnumerable<PropertyInfo> requiredProperties)
      {
      	var shapedObject = new ShapedEntity();
      
      	foreach (var property in requiredProperties)
      	{
      		var objectPropertyValue = property.GetValue(entity);
      		shapedObject.Entity.TryAdd(property.Name, objectPropertyValue);
      	}
      
      	var objectProperty = entity.GetType().GetProperty("Id");
      	shapedObject.Id = (Guid)objectProperty.GetValue(entity);
      
      	return shapedObject;
      }
      Now we have all we need to create the links no matter what properties we get using data shaping. All that is left is to tweak our controller actions a bit:
      [HttpGet]
      public IActionResult GetOwners([FromQuery] OwnerParameters ownerParameters)
      {
      	...//implementation
      
      	var shapedOwners = owners.Select(o => o.Entity).ToList();
      
      	for (var index = 0; index < owners.Count(); index++)
      	{
      		var ownerLinks = CreateLinksForOwner(owners[index].Id, ownerParameters.Fields);
      		shapedOwners[index].Add("Links", ownerLinks);
      	}
      
      	var ownersWrapper = new LinkCollectionWrapper<Entity>(shapedOwners);
      
      	return Ok(CreateLinksForOwners(ownersWrapper));
      }
      
      [HttpGet("{id}", Name = "OwnerById")]
      public IActionResult GetOwnerById(Guid id, [FromQuery] string fields)
      {
      	var owner = _repository.Owner.GetOwnerById(id, fields);
      
      	if (owner.Id == Guid.Empty)
      	{
      		_logger.LogError($"Owner with id: {id}, hasn't been found in db.");
      		return NotFound();
      	}
      
      	owner.Entity.Add("Links", CreateLinksForOwner(owner.Id, fields));
      
      	return Ok(owner.Entity);
      }
      The AccountController class gets the same treatment. Now let's try the same query again: GET https://localhost:5001/api/owner?fields=name And we get:
      {
          "value": [
              {
                  "Name": "John Keen",
                  "Links": [
                      {
                          "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906?fields=name",
                          "rel": "self",
                          "method": "GET"
                      },
                      {
                          "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                          "rel": "delete_owner",
                          "method": "DELETE"
                      },
                      {
                          "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                          "rel": "update_owner",
                          "method": "PUT"
                      }
                  ]
              },
      		...
          ],
          "links": [
              {
                  "href": "https://localhost:5001/api/owner",
                  "rel": "self",
                  "method": "GET"
              }
          ]
      }
      Great! That's exactly what we wanted.

      Introducing Custom Media Types

      Now, returning HATEOAS links is a nice addition to our API. But as you can see the responses can be a bit lengthy and we purposely shortened a few of them to make the article more readable. We want to give an API user the ability to do that, too. And how can we do that? The answer is by utilizing the power of custom media types. We are going to create our own media types that we use when we want to return a response that includes HATEOAS links. If we don't use it, the response won't include any links and will be plain and simple like we've used to return it. Before we start, let's see how we can create a custom media type. A custom media type should look something like this: application/vnd.codemaze.hateoas+json. To compare it to the typical json media type which we use by default: application/json. So let's break down the different parts of a custom media type:
      • vnd - vendor prefix, it's always there
      • codemaze - vendor identifier, we've chosen codemaze, because why not
      • hateoas - media type name
      • json - suffix, we can use it to describe if we want json or XML response for example
      Now let's implement that in our application.

      Registering Custom Media Types

      First, we want to register our new custom media types in the middleware. Otherwise, we'll just get 406 Not Acceptable. Let's add a new extension method to our ServiceExtensions:
      public static void AddCustomMediaTypes(this IServiceCollection services)
      {
      	services.Configure<MvcOptions>(config =>
      	{
      		var newtonsoftJsonOutputFormatter = config.OutputFormatters
      				.OfType<NewtonsoftJsonOutputFormatter>()?.FirstOrDefault();
      
      		if (newtonsoftJsonOutputFormatter != null)
      		{
      			newtonsoftJsonOutputFormatter.SupportedMediaTypes.Add("application/vnd.codemaze.hateoas+json");
      		}
      
      		var xmlOutputFormatter = config.OutputFormatters
      				.OfType<XmlDataContractSerializerOutputFormatter>()?.FirstOrDefault();
      
      		if (xmlOutputFormatter != null)
      		{
      			xmlOutputFormatter.SupportedMediaTypes.Add("application/vnd.codemaze.hateoas+xml");
      		}
      	});
      }
      We are registering two new custom media types: application/vnd.codemaze.hateoas+json for our newtonSoftJsonSerializerOutputFormatter and application/vnd.codemaze.hateoas+xml for our xmlDataContractSerializerOutputFormatter. This will make sure we don't get a 406 Not Acceptable response. Add that to our Startup.cs in the ConfigureServices method, just after the AddControllers method:
      services.AddControllers(config =>
      {
      	config.RespectBrowserAcceptHeader = true;
      	config.ReturnHttpNotAcceptable = true;
      }).AddXmlDataContractSerializerFormatters()
      .AddNewtonsoftJson();
      
      services.AddCustomMediaTypes();
      That takes care of the custom media types registration.

      Implementing a Media Type Validation Filter

      Now, since we've implemented custom media types, we want our Accept header to be present in our requests so we can detect when the user requested the HATEOAS enriched response. To do that, we'll implement an ActionFilter which will validate our Accept header and media types:
      public class ValidateMediaTypeAttribute : IActionFilter
      {
      	public void OnActionExecuting(ActionExecutingContext context)
      	{
      		var acceptHeaderPresent = context.HttpContext.Request.Headers.ContainsKey("Accept");
      
      		if (!acceptHeaderPresent)
      		{
      			context.Result = new BadRequestObjectResult($"Accept header is missing.");
      			return;
      		}
      
      		var mediaType = context.HttpContext.Request.Headers["Accept"].FirstOrDefault();
      
      		if (!MediaTypeHeaderValue.TryParse(mediaType, out MediaTypeHeaderValue outMediaType))
      		{
      			context.Result = new BadRequestObjectResult($"Media type not present. Please add Accept header with the required media type.");
      			return;
      		}
      
      		context.HttpContext.Items.Add("AcceptHeaderMediaType", outMediaType);
      	}
      
      	public void OnActionExecuted(ActionExecutedContext context)
      	{
      
      	}
      }
      We check for the existence of the Accept header first. If it's not present we return BadRequest. If it is, we parse the media type, and if there is no valid media type present, we return BadRequest. Once we've passed the validation checks, we pass the parsed media type to the HttpContext of the controller. Don't forget to register the filter in the IoC:
      services.AddScoped<ValidateMediaTypeAttribute>();
      Now we need to decorate our GetOwners and GetOwnerById actions with [ServiceFilter(typeof(ValidateMediaTypeAttribute))] for these validations to take place. As for the logic itself, we need to extend these actions a bit:
      [HttpGet]
      [ServiceFilter(typeof(ValidateMediaTypeAttribute))]
      public IActionResult GetOwners([FromQuery] OwnerParameters ownerParameters)
      {
      	//Implementation
      
      	var shapedOwners = owners.Select(o => o.Entity).ToList();
      
      	var mediaType = (MediaTypeHeaderValue)HttpContext.Items["AcceptHeaderMediaType"];
      
      	if (!mediaType.SubTypeWithoutSuffix.EndsWith("hateoas", StringComparison.InvariantCultureIgnoreCase))
      	{
      		return Ok(shapedOwners);
      	}
      
      	for (var index = 0; index < owners.Count(); index++)
      	{
      		var ownerLinks = CreateLinksForOwner(owners[index].Id, ownerParameters.Fields);
      		shapedOwners[index].Add("Links", ownerLinks);
      	}
      
      	var ownersWrapper = new LinkCollectionWrapper<Entity>(shapedOwners);
      
      	return Ok(CreateLinksForOwners(ownersWrapper));
      }
      We read the media type we've parsed in the ValidateMediaTypeAttribute ActionFilter and cast it to MediaTypeHeaderValue type. Using the SubTypeWithoutSuffix.EndsWith of the MediaTypeHeaderValue class we check if HATEOAS is requested, and if it's not we return owners immediately. If it is, we add the links and return them as we did before this implementation. Same story with the GetOwnerById action:
      [HttpGet("{id}", Name = "OwnerById")]
      [ServiceFilter(typeof(ValidateMediaTypeAttribute))]
      public IActionResult GetOwnerById(Guid id, [FromQuery] string fields)
      {
      	var owner = _repository.Owner.GetOwnerById(id, fields);
      
      	if (owner.Id == Guid.Empty)
      	{
      		_logger.LogError($"Owner with id: {id}, hasn't been found in db.");
      		return NotFound();
      	}
      
      	var mediaType = (MediaTypeHeaderValue)HttpContext.Items["AcceptHeaderMediaType"];
      
      	if (!mediaType.SubTypeWithoutSuffix.EndsWith("hateoas", StringComparison.InvariantCultureIgnoreCase))
      	{
      		_logger.LogInfo($"Returned shaped owner with id: {id}");
      		return Ok(owner.Entity);
      	}
      
      	owner.Entity.Add("Links", CreateLinksForOwner(owner.Id, fields));
      
      	return Ok(owner.Entity);
      }
      Now to test this out, let's try the simple request to get the owners with the Accept request header set to application/json: GET https://localhost:5001/api/owner We should get:
      [
          {
              "Id": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
              "Name": "John Keen",
              "DateOfBirth": "1980-12-05T00:00:00",
              "Address": "61 Wellfield Road"
          },
          {
              "Id": "261e1685-cf26-494c-b17c-3546e65f5620",
              "Name": "Anna Bosh",
              "DateOfBirth": "1974-11-14T00:00:00",
              "Address": "27 Colored Row"
          },
      	...
      ]
      And now, when we change the Accept header to application/vnd.codemaze.hateoas+json and we should get:
      {
          "value": [
              {
                  "Id": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                  "Name": "John Keen",
                  "DateOfBirth": "1980-12-05T00:00:00",
                  "Address": "61 Wellfield Road",
                  "Links": [
                      {
                          "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                          "rel": "self",
                          "method": "GET"
                      },
                      {
                          "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                          "rel": "delete_owner",
                          "method": "DELETE"
                      },
                      {
                          "href": "https://localhost:5001/api/owner/24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
                          "rel": "update_owner",
                          "method": "PUT"
                      }
                  ]
              },
              ...
          ],
          "links": [
              {
                  "href": "https://localhost:5001/api/owner",
                  "rel": "self",
                  "method": "GET"
              }
          ]
      }
      Fantastic. Now we have the means to choose between the json, XML and HATEOAS responses as we please. Let's summarize.

       Conclusion

      HATEOAS is indeed one of the most useful but at the same one of the most complicated REST concepts to implement. How you implement HATEOAS in your API and how much you invest in polishing it is up to you. HATEOAS is easy "the one" thing that separates the excellent API from the just good or bad ones. So what have we learned this time:
      • What HATEOAS is and how important it is in the RESTful world
      • How to implement HATEOAS in ASP.NET Core WebAPI project
      • Improved our solution to work nicely with data shaping
      • How to support XML serialization of dynamic objects with HATEOAS links
      • What custom media types are and how to utilize them to make HATEOAS optional in our responses
      This article turned out to be pretty lengthy, but we hope you learned something new and once again if you found it hard to follow, check out our finished project, and compare and contrast to your own solution. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      49442 0 0 0
      Code Maze Weekly #1 https://code-maze.com/code-maze-weekly-1/ Fri, 15 Nov 2019 11:00:56 +0000 https://code-maze.com/?p=49466 .NET and C#

      Worthy Reads

      News and Updates

      Live & Recordings

      DevOps

      Other Interesting Articles

      On Programming and Life

      Weekly Dose of Humor

      ]]>
      49466 0 0 0 Machine Learning from ProgrammerHumor ]]> JavaScript Null vs. Undefined Explained]]> Improvements in .NET Core 3.0 for troubleshooting and monitoring distributed apps]]> High-Performance Logging in .NET Core]]>
      Code Maze Weekly #2 https://code-maze.com/code-maze-weekly-2/ Fri, 22 Nov 2019 11:15:44 +0000 https://code-maze.com/?p=49578 .NET and C#

      Worthy Reads

      News and Updates

      Live & Recordings

      DevOps

      Other Interesting Articles

      On Programming and Life

      Weekly Dose of Humor

      ]]>
      49578 0 0 0 gRPC vs HTTP APIs]]> Using NSwag to Generate C# Client Classes for ASP.NET Core 3]]>
      Protecting Data with IDataProtector in ASP.NET Core https://code-maze.com/data-protection-aspnet-core/ Sat, 23 Nov 2019 10:11:31 +0000 https://code-maze.com/?p=49599 a starting project on our GitHub repo, which you can use to follow along with this article and the finished project if you get stuck. We are going to divide this article into the following sections:

      Encrypting and Decrypting Data with IDataProtector

      IDataProtector is an interface that provides data protection services. To be able to use its features, we have to add those data protection services to the specified IServiceCollection and then inject it using dependency injection. If we take a look at our starting project, we can see the Index action which lists all the employees from the database. There is also a Details link next to each employe which directs us to the Details page. Of course, if we take a look at the URI of a Details page, we can see the value of the employee’s Id property: Details page - IDataProtector Now, let’s assume that we don’t want to show that Id value in the URI, or at least not in the Guid form. In other words, we want to protect our Id. To do that, let’s modify the Startup.cs class, by registering protection services in IServiceCollection with the AddDataProtection method:
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext<EmployeeContext>(opts =>
              opts.UseSqlServer(Configuration.GetConnectionString("sqlConnection")));
      
          services.AddScoped<IEmployeeRepository, EmployeeRepository>();
      
          services.AddDataProtection();
      
           services.AddControllersWithViews();
      }
      
      So, that is all it takes. Or at least for now. Next, let’s create an IDataProtector object.

      Creating a Data Protection Object with IDataProtector and DI

      After the registration, we are going to create a data protection object to be able to protect and unprotect our data:
      private readonly IDataProtector _protector;
      
      public EmployeesController(IEmployeeRepository repo, IDataProtectionProvider provider)
      {
          _repo = repo;
          _protector = provider.CreateProtector("EmployeesApp.EmployeesController");
      }
      
      We can see that we create an object of type IDataProtector. But additionally, we need an IDataProtectionProvider object with the CreateProtector method to accomplish this action successfully. Both interfaces are part of the Microsoft.AspNetCore.DataProtection namespace. Therefore, we have to include it as a using directive. To continue, let’s modify the Employee model class and the Index action to protect Id values for each employee:
      public class Employee
      {
          public Guid Id { get; set; }
      
          [Required(ErrorMessage = "Name is required")]
          public string Name { get; set; }
      
          [Required(ErrorMessage = "Age is required")]
          public int Age { get; set; }
      
          [Required(ErrorMessage = "Account number is required")]
          public string AccountNumber { get; set; }
          [NotMapped]
          public string EncryptedId { get; set; }
      }
      
      public IActionResult Index()
      {
          var employees = _repo.GetAll();
      
          foreach (var emp in employees)
          {
              var stringId = emp.Id.ToString();
              emp.EncryptedId = _protector.Protect(stringId);
          }
      
          return View(employees);
      }
      
      So, we just add a new EncryptedId property in the Employee class and in the Index method iterate through each employee and assign protected value to the newly created property. Next, let’s modify the Index view file by changing the value of the asp-route-id attribute in a Details link:
      <a asp-action="Details" asp-route-id="@item.EncryptedId">Details</a> |
      Finally, let’s modify the Details action in the EmployeesController class:
      public IActionResult Details(string id)
      {
          var guid_id = Guid.Parse(_protector.Unprotect(id));
          var employee = _repo.GetEmployee(guid_id);
      
          return View(employee);
      }
      
      That’s it. We can test it now: Details page protected - IDataProtector We have the same result as before, but this time with encrypted Id in URI.

      Purpose String

      Let’s take a look at the following code:
      _protector = provider.CreateProtector("EmployeesApp.EmployeesController");
      As explained, we need an object of type IDataProtectionProvider and CreateProtector method to create a protector object. But we can see an additional parameter in the CreateProtector method. This is Purpose String. Any protector must have its own unique purpose string and it provides isolation between cryptographic consumers. In other words, two IDataProtector instances (created with different purpose strings) can’t read each other's payloads, only their own. If one protector object tries to read payload from a different protector, an exception will be thrown. We can test this with an example. Let’s slightly modify our controller:
      private readonly IEmployeeRepository _repo;
      private readonly IDataProtector _protector;
      private readonly IDataProtector _protectorTest;
      
      public EmployeesController(IEmployeeRepository repo, IDataProtectionProvider provider)
      {
          _repo = repo;
          _protector = provider.CreateProtector("EmployeesApp.EmployeesController");
          _protectorTest = provider.CreateProtector("TestProtector");
      }
      
      public IActionResult Index()
      {
          var employees = _repo.GetAll();
      
          foreach (var emp in employees)
          {
              var stringId = emp.Id.ToString();
              emp.EncryptedId = _protector.Protect(stringId);
          }
      
          var testData = _protectorTest.Protect("Test");
          var unprotectedTest = _protector.Unprotect(testData);
      
          return View(employees);
      }
      
      In this example, we create another IDataProtector instance (_protectorTest) and using that instance to create protected data. Additionally, we try to unprotect it with the other IDataProtector instance (_protector). Now, when we run our app: Invalid payload - IDataProtector As expected, CryptographicException was thrown.

      Data Protection for Limited Time

      There are situations where we want our protected payload to expire after a certain period of time. For that purpose, we can use the ITimeLimitedDataProtector interface. To create an instance of this mentioned interface, we have to have an instance of IDataProtector and then call the ToTimeLmitedDataProtector extension method. So, let’s find out how we can create a time-limited payload with an example. We are going to modify the Index action just for the example purpose:
      public IActionResult Index()
      {
          //Previous code removed for the example clarity
      
          var timeLimitedProtector = _protector.ToTimeLimitedDataProtector();
          var timeLimitedData = timeLimitedProtector.Protect("Test timed protector", lifetime: TimeSpan.FromSeconds(2));
      
          //just to test that this action works as long as life-time hasn't expired
          var timedUnprotectedData = timeLimitedProtector.Unprotect(timeLimitedData);
      
          Thread.Sleep(3000);
      
          var anotherTimedUnprotectTry = timeLimitedProtector.Unprotect(timeLimitedData);
      
          return View(employees);
      }
      
      As you can see, there is nothing strange about this code. We are using the _protector object to create a new instance of a time-limited protector with the help of the ToTimeLimitedDataProtector method. Then, we use the timeLimitedProtector object to protect our data and to define the expiration period for our payload. After that, we unprotect our protected data, just to prove that everything works well if the payload hasn’t expired. Finally, we create a 3 seconds pause and then try to unprotect our protected data. Once we start our application, we can see the result: Protected payload time expired There we go, it is obvious which action failed and why.

      Different Ways to Configure Data Protection in ASP.NET Core

      When we register data protection services in the StartUp class, the default configuration is applied, but sometimes we want to change that. That being said, let’s see how we can apply a different configuration for data protection in ASP.NET Core. If we want to store the data protection configuration in a local custom file, we can use the PersistKeysToFileSystem method:
      services.AddDataProtection()
           .PersistKeysToFileSystem(new DirectoryInfo(@"bin\debug\configuration"));
      
      This is the result: Local configuration file - IDataProtector We can see the creation date, expiration date, validation algorithm used and the value of the master key. Notice that we have a warning that our key is unencrypted, which is not that good. But, we can fix that:
      services.AddDataProtection()
          .PersistKeysToFileSystem(new DirectoryInfo(@"bin\debug\configuration"))
          .ProtectKeysWithDpapi();
      
      Let’s delete this configuration file and run our app again. Now if we take a look at our key, we are going to see that it is encrypted: Encypted key We don’t have to use just ProtectKeysWithDpapi method to encrypt our key. There are additional methods as well: Different methods for key encryption We can calculate, from the expirationDate and activationDate properties, that the expiration period is 90 days. If we want, we can modify that as well:
      services.AddDataProtection()
            .PersistKeysToFileSystem(new DirectoryInfo(@"bin\debug\configuration"))
            .ProtectKeysWithDpapi()
            .SetDefaultKeyLifetime(TimeSpan.FromDays(10));
      
      This is the result: Expiration date changed for the key - IDataProtector To check additional configuration options, you can visit the Data Protection configuration in ASP.NET Core.

      Conclusion

      We have covered many different features related to data protection in ASP.NET Core. This can be quite useful when protecting sensitive data in our application. To sum up, we have learned:
      • How to register data protection services in our application
      • The way to encrypt and decrypt sensitive data in our app
      • To protect the payloads for a limited time
      • Different ways to configure data protection
      [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      49599 0 0 0 1621 0 0 1622 1621 0 1643 0 0 1644 1643 0
      Code Maze Weekly #3 https://code-maze.com/code-maze-weekly-3/ Fri, 29 Nov 2019 12:15:48 +0000 https://code-maze.com/?p=49642 .NET and C#

      Worthy Reads

      News and Updates

      Live & Recordings

      DevOps

      Other Interesting Articles

      On Programming and Life

      Weekly Dose of Humor

        Hope you enjoy the selection we've prepared this week. If you do, subscribe to get notified each week. Have a great weekend!]]>
      49642 0 0 0
      Code Maze Weekly #4 https://code-maze.com/code-maze-weekly-4/ Fri, 06 Dec 2019 12:20:39 +0000 https://code-maze.com/?p=49682 .NET and C#

      Worthy Reads

      News and Updates

      Live & Recordings

      DevOps

      Other Interesting Articles

      On Programming and Life

      Weekly Dose of Humor

        We hope you've enjoyed the selection we've prepared this week. If you did, please subscribe to get notified each week. Have a great weekend!]]>
      49682 0 0 0
      Code Maze Weekly #5 https://code-maze.com/code-maze-weekly-5/ Fri, 13 Dec 2019 12:20:50 +0000 https://code-maze.com/?p=49719 .NET and C#

      Worthy Reads

      News and Updates

      Live & Recordings

      DevOps

      Other Interesting Articles

      On Programming and Life

      Weekly Dose of Humor

      We hope you've enjoyed the articles this week. If you haven't done so yet, please subscribe to get notified about the future newsletter issues. Have a great weekend!]]>
      49719 0 0 0 Add a swipe menu to your Xamarin.Forms app]]>
      Code Maze Weekly #6 https://code-maze.com/code-maze-weekly-6/ Fri, 20 Dec 2019 12:20:45 +0000 https://code-maze.com/?p=49872 .NET and C#
      • An Introduction to Blazor and Web Assembly [grapecity.com] Is Blazor and Web Assembly hype justified or not? If you haven't learned what they are yet, here's your chance.
      • Assertion messages in tests [enterprisecraftsmanship.com] Tests are important and we can easily agree on that. But more than that, when writing tests we need to make them readable and easily understandable. Vladimir Khorikov explains how we can achieve that.

      Worthy Reads

      Live & Recordings

      DevOps

      Other Interesting Articles

      On Programming and Life

      Weekly Dose of Humor

      We hope you've enjoyed the articles this week. If you haven't done so yet, please subscribe to get notified about the future newsletter issues. Have a great weekend!]]>
      49872 0 0 0
      Code Maze Weekly #7 https://code-maze.com/code-maze-weekly-7/ Fri, 27 Dec 2019 12:20:37 +0000 https://code-maze.com/?p=49987 .NET and C#

      Worthy Reads

      Live & Recordings

      DevOps

      Other Interesting Articles

      On Programming and Life

      Weekly Dose of Humor

      We hope you've enjoyed the articles this week. If you haven't done so yet, please subscribe to get notified about the future newsletter issues. Have a great weekend!]]>
      49987 0 0 0
      Getting Started with ASP.NET Core MVC - TEMP https://code-maze.com/__trashed-2/ Fri, 03 Jan 2020 10:33:59 +0000 https://code-maze.com/?p=50131 In this article, we are going to look at the basics of building an ASP.NET Core MVC web app. We are going to start by creating a simple ASP.NET Core MVC app using the default template provided by Visual Studio. The default template itself will translate into a working app. To the default template, we’re going to add a controller and a few action methods. Afterward, we’re going to introduce the views using the razor syntax and return them from the controller methods. Finally, we’re going to define some models and see how those can be passed into the views. We are also going to look at how the model data can be rendered on the web page. We strongly recommend visiting the complete navigation of this series: ASP.NET Core MVC Series. To download this article's source code visit: Getting Started with ASP.NET Core MVC Source Code. We have divided this article into the following sections:

      Creating an ASP.NET Core MVC project

      First, let’s create a new ASP.NET Core MVC project.    From Visual Studio, we are going to select Create a New Project , and in the next window ASP.NET Core Web Application option. Then, let's add the name for our application - BookStore. Complete the New ASP.NET Core Web Application - BookStore dialog:  
      1. In the version selector drop-down box select ASP.NET Core 3.1
      2. Select Web Application (Model-View-Controller)
      3. Then select Create
      New web application template Visual Studio creates an MVC project using the default template. The great thing is that we have a working app right now by entering a project name and selecting a few options. This is a basic starter project and a good place to start.

      The Project Structure

      Now, let's examine the project structure and look at the files generated as part of the default MVC template: Solution Explorer We can see that the project is well organized into separate folders for Models, Controllers, and Views. The Views are further organized into subfolders corresponding to each view. There are some default files generated in each of these folders as well. Then there are the usual configuration and startup files that come with the .NET Core project template. Now let’s run the app with Ctrl+F5. We can see a website based on the default layout provided by ASP.NET Core MVC: default mvc app Congrats! We have just created a website using ASP.NET Core MVC.

      Adding Controllers

      Since we have a working ASP.NET Core MVC app, let’s start experimenting with it. Let’s add an empty controller to the Controllers folder and name it BooksController. We can do it by right-clicking Controllers > Add > Controller add controller In the Add Scaffold dialog box, select MVC Controller - Empty add scaffold Then, in the Add Empty MVC Controller dialog box, give the controller name as BooksController and click Add: add empty mvc controller This will create BooksController with a default action method. Let’s change the code and create two action methods in it:
      public string Index()
      {
          return "This is the book index.";
      }
      
      public string Details()
      {
           return "This is the details of a book.";
      }
      Every public method in a controller is callable as an HTTP endpoint. In our controller, both methods return a string. Let’s run the application and navigate to the BooksController by changing the URL to https://localhost:44323/books *Please note that port number is randomly assigned by IIS Express and may vary in different systems. books index page We’ll cover routing in detail in an upcoming article, but for now, let’s just understand some basics. MVC invokes controller classes and the action methods within them depending on the incoming URL. The default URL routing logic used by MVC uses a format like this to determine what code to invoke: /[Controller]/[ActionName]/[Parameters] The ActionName defaults to Index when not supplied. Parameters are also optional. So in this case when we hit the above URL, the application executes the Index method of the BooksController. This method returns a string and what we see is an HTML page generated with the supplied string. Similarly, If we change the URL to https://localhost:44323/books/details, we can see the Details method of the BooksController executed: books details We have created our own controller with two methods and executed them which is awesome.

      Creating Views

      Even though returning plain strings from the controller works, that is not a good practice. The controller action methods should ideally return a view. Then the view should be responsible for displaying the page output. So let’s add a view file for the Index action method. Right-click on the Index action method and click Add View   Give the view name as Index and click Add: add mvc view This will create a new folder Books under Views and a view file Index.cshtml inside it: solution explorer view This is a razor view file. We’ll learn about creating views using the Razor syntax in detail in an upcoming article. For now, let’s just add some text inside the view file as below:
      @{
          ViewData["Title"] = "Index";
      }
      
      <h1>This is the book index generated by the view.</h1>
      
      Now let’s run the application again. https://localhost:44323/books book details view page We can see that a new page is displayed based on the view file we just created. Also, we can see that a default layout template is applied, which we’ll revisit when we look at the layout files in a later article. So we have created a view file, returned it from the controller action method and verified that it is displayed when we ran the application.

      Defining Models

      So far we have seen the controllers and views in action. Now, let’s introduce the models into the equation. Let’s add a new class Book into the Models folder with some properties:
      public class Book
      {
          public int Id { get; set; }
      
          public string Title { get; set; }
      
          public string Genre { get; set; }
      
          public List<string> Authors { get; set; }
      
          public decimal Price { get; set; }
      
          public DateTime PublishDate { get; set; }
      }
      We’ll return this model in the Details action method of the BooksController. But before that, we need to create a view for displaying the book details. To do that, we are going to add a new view called Details as we did for the Index above. Let’s also modify the Details action method to return this view. We'll pass the model into the view and display the book details on the page. Ideally, we would fetch the model data from a database. We will learn how to do that in an upcoming article. For now, we’ll just generate some mock data to return:
      public IActionResult Details()
      {
           Book book = new Book()
           {
               Id = 1,
               Title = "Learning ASP.NET Core 2.0",
               Genre = "Programming & Software Development",
               Price = 45,
               PublishDate = new System.DateTime(2012, 04, 23),
               Authors = new List<string> { "Jason De Oliveira", "Michel Bruchet" }
           };
      
           return View(book);
      }
      Let’s also modify the view to display the model data:
      @model BookStore.Models.Book
      
      @{
          ViewData["Title"] = "Details";
      }
      
      <h1>Details</h1>
      
      <div>
          <h4>Book</h4>
          <hr />
          <dl class="row">
              <dt class="col-sm-2">
                  @Html.DisplayNameFor(model => model.Title)
              </dt>
              <dd class="col-sm-10">
                  @Html.DisplayFor(model => model.Title)
              </dd>
              <dt class="col-sm-2">
                  @Html.DisplayNameFor(model => model.Genre)
              </dt>
              <dd class="col-sm-10">
                  @Html.DisplayFor(model => model.Genre)
              </dd>
              <dt class="col-sm-2">
                  @Html.DisplayNameFor(model => model.Price)
              </dt>
              <dd class="col-sm-10">
                  @Html.DisplayFor(model => model.Price)
              </dd>
              <dt class="col-sm-2">
                  @Html.DisplayNameFor(model => model.PublishDate)
              </dt>
              <dd class="col-sm-10">
                  @Html.DisplayFor(model => model.PublishDate)
              </dd>
          </dl>
          <table>
              <thead>
                  <tr>
                      <th>
                          Authors
                      </th>
                  </tr>
              </thead>
              <tbody>
                  @foreach (var item in Model.Authors)
                  {
                      <tr>
                          <td>
                              @Html.DisplayFor(modelItem => item)
                          </td>
                      </tr>
                  }
              </tbody>
          </table>
      </div>
      <hr />
      <div>
          <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
          <a asp-action="Index">Back to List</a>
      </div>
      
      Here we use the approach of strongly typed models. By including a @model statement at the top of the view file, we specify the type of object that the view expects. So here our view expects a model of type Book. We can access any property of the class Book via IntelliSense available in the Visual Studio. Next, we define an HTML template for displaying the view data. DisplayNameFor() and DisplayFor() are HTML Helper methods that show the name and value of the properties in the model. Model.Authors is a collection and we use the @foreach syntax to iterate through it and display the values. Now let’s run the app and navigate to the details page once again: book details with view Voila! We have now created an MVC app with Models, Views and Controllers. Next, let’s look into validating the model using data annotations.

      Data Annotations

      Data annotations provide a built-in set of validation attributes that we apply declaratively to any class or property. It also contains attributes that help with formatting the data:
          public class Book
          {
              public int Id { get; set; }
      
              [Display(Name = "Book Title")]
              [Required]
              [StringLength(maximumLength: 20, ErrorMessage = "The Title length should be between 2 and 20.", MinimumLength = 2)]
              public string Title { get; set; }
      
              public string Genre { get; set; }
      
              public List<string> Authors { get; set; }
      
              [DataType(DataType.Currency)]
              [Range(1, 100)]
              public decimal Price { get; set; }
      
              [Display(Name = "Publish Date")]
              [DataType(DataType.Date)]
              public DateTime PublishDate { get; set; }
          }
      
      In the above code, we have applied some annotations to the Book model class.   The validation attributes specify behavior that you want to enforce on the model properties they're applied to: The Required  attribute indicates that a property must have a value.  Using a MinimumLength attribute indicates that the property should have a minimum length which also means it cannot be empty. The RegularExpression attribute is used to limit what characters can be input. By using the Range attribute, we can constrain the value of a property within a specified range. The StringLength attribute lets us set the maximum length of a string property, and optionally its minimum length. DataTypes are used to specify the data type of the fields and are inherently required and don't need the Required attribute. Now let’s run the app once again and navigate to the book details page: Notice that the Title has now changed to Book Title and PublishDate to Publish Date as we have applied the Display attribute. Also, note that the Price and Publish Date is formatted as currency and date for the specific locale. Now let’s create a page for adding a new book and see the validations in action. In the controller, we’ll add two Create methods:
       public IActionResult Create()
       {
           return View();
       }
      
       [HttpPost]
       [ValidateAntiForgeryToken]
       public IActionResult Create(Book book)
       {
           if (ModelState.IsValid)
           {
               // Logic to add the book to DB
               return RedirectToAction("Index");
           }
           return View(book);
       }

      Create View

      The first Create action method displays the initial create form. Let's create a view for this action. To do that we are going to right-click on the first Create action and choose the Add View option (as we did for the Index and Details actions). In the next window, we are going to give a name to the view, to select a template for the view and to select a model class to connect this view with: C:\Users\a\Desktop\creating view with template After the view is created, just remove the div part which generates the Id control, because we don't need that for the Create view. Second Create method has a [HttpPost] attribute which signals that only POST requests can be handled by it. Since this is a post request and we’re submitting a form, we can use ModelState.IsValid to check whether the Book has any validation errors. Calling this method evaluates any validation attributes that have been applied to the object. If the object does not satisfy our validation criteria, the Create method re-displays the form. If there are no errors, the method should ideally save the new book in the database. (Not implemented now) By clicking the Create button without entering valid data, we’ll see the validation messages: Book create page with validation errors So we have successfully implemented model validations and data formatting using data annotations.

      Conclusion

      In this article, we looked at the following topics:
      • How to create an ASP.NET MVC Core project
      • Examining the project structure
      • Adding controllers, views, and models
      • Validating and formatting data using annotations
      In the next part of this series, we’re going to learn how to handle data in an ASP.NET Core MVC. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      50131 0 0 0
      Working with Data in ASP.NET Core MVC - TEMP https://code-maze.com/__trashed/ Fri, 03 Jan 2020 10:33:58 +0000 https://code-maze.com/?p=50135 In the previous part of this series, we built an ASP.NET Core MVC application using some mock data. In this part, we are going to look at the ways to connect the application with a database and work with data. We are going to use EF Core Code-First approach which is the preferred way to work with data when starting a new project. We strongly recommend visiting the complete navigation of this series: ASP.NET Core MVC Series. To download this article's source code visit: Working With Data in ASP.NET Core MVC Source Code. First, we are going to add a new project in the MvcSeries solution (Right-click on the solution => Add => NewProject). Then, we need to create a model. As soon as we do that, we are going to scaffold the model to produce the controller action methods and views corresponding to the Create, Read, Update, and Delete (CRUD) operations for the model. After that, we are going to create the database using the EF Core Migrations. Finally, we are going to seed the database with some initial data. Once these steps are completed, we’ll have a working ASP.NET Core MVC app that connects to the database. The article is divided into the following sections:

      Creating a Model in ASP.NET Core MVC

      Let’s create the Book model, which is a similar object to the one we created in the previous part of this series, just without Authors property:
      public class Book
      {
         public int Id { get; set; }
      
         [Display(Name = "Book Title")]
         [Required]
         public string Title { get; set; }
      
         public string Genre { get; set; }
      
         [DataType(DataType.Currency)]
         [Range(1, 100)]
         public decimal Price { get; set; }
      
         [Display(Name = "Publish Date")]
         [DataType(DataType.Date)]
         public DateTime PublishDate { get; set; }
      }
      
      This model will serve as a base for building the project.

      Scaffolding the Model

      The next step is to scaffold the model to generate the controller action methods and views. Scaffolding will create a new fully functional controller. Right-click on the Controllers folder > Add > New Scaffolded Item: add scaffolded item In the Add Scaffold dialog, we are going to select MVC Controller with views, using Entity Framework > Add: In the Add Scaffold dialog, select MVC Controller with views, using Entity Framework > Add. Let's complete the Add Controller dialog:
      1. Model class: Book (BookStoreWithData.Models)
      2. Data context class: Select the + icon.
      3. Leave the default name (BookStoreWithData.Models.BookStoreWithDataContext)
      4. Click Add
      add data context
      1. Views: Keep the default of each option checked
      2. Controller name: Keep the default BooksController
      3. Select Add
      Add view and controller details - ASP.NET Core MVC Visual Studio creates:
      • An Entity Framework Core database context class (Data/BookStoreWithDataContext.cs)
      • A Controller (Controllers/BooksController.cs)
      • Razor view files for Create,Delete, Details, Edit, and Index pages (Views/Books/*.cshtml)
      This process of automatic creation of the database context, CRUD (create, read,update and delete) action methods and views are known as scaffolding. Scaffolding is optional, and you can do the whole process manually, too. In situations where scaffolding is not needed or appropriate, we can take control of the entire creation process.

      Migrations

      Migrations automate the creation of a database based on our model. First, let’s run the following command in the Package Manager console: PM> Add-Migration BookStoreWithData.Models.EmployeeContext This will create the classes for supporting migrations. Now we need to apply those changes to the database. For that, let’s run the following command: PM> update-database This will update the database based on our models. We have covered the way to do this in detail in the ASP.NET Core Web API with EF Core Code-First Approach article. Once we complete all the steps, we are going to have the database and columns created as defined in the model. Our database table and columns will look like this: database and columns - ASP.NET Core MVC We have successfully created the database from our code using EF Core Code-First Migrations.

      Seeding the Data

      The next step is seeding the data. Data seeding allows us to provide the initial data during the creation of a database. As mentioned in the linked article above, let’s override the OnModelCreating method in the BookContext:
            protected override void OnModelCreating(ModelBuilder modelBuilder)
              {
                  modelBuilder.Entity<Book>().HasData(new Book
                  {
                      Id = 1,
                      Title = "Book1",
                      Genre = "Genre1",
                      Price = 20,
                      PublishDate = new DateTime(2012, 4, 23)
                  }, new Book
                  {
                      Id = 2,
                      Title = "Book2",
                      Genre = "Genre2",
                      Price = 30,
                      PublishDate = new DateTime(2008, 6, 13)
                  });
              }
      
      Once we apply the migration and update the database, we can see that the database tables are updated with the seed data that we provided: database table with data

      Running the ASP.NET Core MVC Application

      Now, let's try running the application. Make sure that the new project is your default starting project (Right-click on it => Set as startup project).

      Listing Page

      Let's navigate to /Books This will invoke the Index() method in the controller: list page - ASP.NET Core MVC We can see that the Index page displays the list of all books in the database. There are also links for editing, viewing details and deleting a book on the grid. At the top, there is also a link for creating a new book.

      Create Page

      Let's click on the ‘Create New’ link. Clicking on the link will take us to /Books/Create. This will invoke the Create() method in the controller using a GET request: create page This page can be used to create a new book. After entering the book details and clicking on the Create button, the Create() method with the [HttpPost] attribute will be invoked. This will be a POST request and the form data will be submitted. There is a link at the bottom to navigate back to the list.

      Details Page

      In the listing page, if we click on the details link of any book, we’ll be taken to /Books/Details/{id}. This will invoke the Details() method in the controller: details page This page shows the details of a book. In the details page, we can see buttons for editing and navigate back to the list.

      Edit Page

      If we click on the edit link from here or the listing page, it will take us to /Book/Edit/{id} This will invoke the first Edit() method in the controller which supports only GET requests: edit page Clicking the Save button will invoke the Edit() method with the [HttpPost] attribute. This will update the record with the values we provide on the page. Depending on the request type, MVC decides which Edit method to call. For editing a record, PUT request is the more appropriate method. But here the auto-generated code used a POST method that can also be used. However, when we are creating the controller methods by ourselves, the recommended approach is to use the PUT method for updating a record.

      Delete Page

      Finally, if we click on the Delete link from the listing page, we’ll be navigated to /Book/Delete/{id} This will invoke the first Delete() method in the controller which supports only GET requests. This is the delete confirmation page: delete confirmation page Once we confirm the delete by clicking the Delete button, the DeleteConfirmed() method with the [HttpPost]  attribute will be invoked. This will delete the record from the database.

      Code Explained

      By following the above steps, we have successfully created a fully functional app with database integration. Now let’s take a look at the auto-generated code and try to understand how the app functions. Visual Studio generates the following files as part of scaffolding: scaffolding generated files - ASP.NET Core MVC

      DB Context

      A DB Context file is responsible for facilitating all communication with the database. To learn about DBContext in more detail, check out our article: Context Class and the Database Connection. Context file for this app is auto-generated at Data/BookStoreWithDataContext as part of the scaffolding.

      Controller

      A Controller file is generated at Controllers/BooksController.cs with action methods corresponding to CRUD operations. If we look at the BooksController file, we can see the following action methods: GET api/books - Lists all the books. GET api/books/details/{id} - Gets the details of a book. GET api/books/create - Shows the initial create book page. POST api/books/create - Creates a new book. GET api/books/edit/{id} - Shows the initial edit page. POST api/books/edit/{id} - Updates the details of a book. GET api/books/delete/{id} - Shows the delete confirmation page. POST api/books/delete/{id} - Deletes a book. The action methods in the controller access the context to perform data operations. For example, In the Details() method, we can see that the book record is fetched by accessing the context:
        var book = await _context.Book
                      .FirstOrDefaultAsync(m => m.Id == id);
      
      Similarly, in the Create() method with [HttpPost] attribute, we access the context to add a new record:
      _context.Add(book);
      Ideally, a controller should not directly access the context file. Instead, we should introduce a repository layer in between. We have explained the repository pattern in detail in one of our other article sections: Implementing the repository pattern.

      Razor view files

      Inside the Views/Books folder, we can see that the view pages are created for CreateDelete, Details, Edit and Index methods. These view pages are based on the razor syntax. We’ll discuss creating the view pages using razor syntax in detail in a later article.

      Conclusion

      In this article, we have looked at the following:
      • Creating a model.
      • Scaffolding the model to generate context, controller and the view files.
      • Creating the database using migration.
      • Seeding the data.
      • The code auto-generated by scaffolding.
      In the next part of the series, we’ll look at creating the view pages using razor syntax. [sc name="subscribe" formNumber="2797" contentType=".NET Core"]]]>
      50135 0 0 0
      HTTP Reference: Status codes https://code-maze.com/?post_type=tablepress_table&p=391 Sat, 17 Jun 2017 10:17:32 +0000 http://www.code-maze.com/?post_type=tablepress_table&p=391 391 0 0 0 _tablepress_export_table_id HTTP Reference: Request headers https://code-maze.com/?post_type=tablepress_table&p=412 Sat, 17 Jun 2017 11:25:43 +0000 http://www.code-maze.com/?post_type=tablepress_table&p=412 412 0 0 0 _tablepress_export_table_id HTTP Reference: Response headers https://code-maze.com/?post_type=tablepress_table&p=414 Sat, 17 Jun 2017 11:26:48 +0000 http://www.code-maze.com/?post_type=tablepress_table&p=414 ; rel=\"alternate\""],["Location","Used in redirection, or when a new resource has been created.","Location: http:\/\/www.code-maze.com\/index.html"],["P3P","This header is supposed to set Platform for Privacy Preferences Project (P3P) policy, in the form of P3P:CP=\"your_compact_policy\". However, P3P did not take off, most browsers have never fully implemented it, a lot of websites set this header with fake policy text, that was enough to fool browsers the existence of P3P policy and grant permissions for third party cookies.","P3P: CP=\"This is not a P3P policy! See http:\/\/www.google.com\/support\/ accounts\/bin\/answer.py?hl=en&answer=151657 for more info.\""],["Pragma","Implementation-specific headers that may have various effects anywhere along the request-response chain.","Pragma: no-cache"],["Proxy-Authenticate","Request authentication to access the proxy.","Proxy-Authenticate: Basic"],["Refresh","Used in redirection, or when a new resource has been created. This refresh redirects after 5 seconds. This is a proprietary, non-standard header extension introduced by Netscape and supported by most web browsers.","Refresh: 5; url=http:\/\/www.code-maze.com\/index.html"],["Retry-After","If an entity is temporarily unavailable, this instructs the client to try again after a specified period of time (seconds).","Retry-After: 240"],["Server","A name for the server","Server: Apache\/2.4 (Unix)"],["Set-Cookie","Sets an HTTP Cookie","Set-Cookie: UserID=1; Max-Age=3600; Version=1"],["Strict-transfer-Security","A HSTS Policy informing the HTTP client how long to cache the HTTPS only policy and whether this applies to subdomains.","Strict-transfer-Security: max-age=16070400; includeSubDomains"],["Trailer","The Trailer general field value indicates that the given set of header fields is present in the trailer of a message encoded with chunked transfer coding.","Trailer: Max-Forwards"],["Transfer-Encoding","The form of encoding used to safely transfer the entity to the user. Currently defined methods are: chunked, compress, deflate, gzip, identity.","Transfer-Encoding: chunked"],["Vary","Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server.","Vary: *"],["Via","Informs the client of proxies through which the response was sent.","Via: 1.0 mick, 1.1 baselogic.com (Apache\/2.4)"],["Warning","A general warning about possible problems with the entity body.","A general warning about possible problems with the entity body."],["WWW-Authenticate","Indicates the authentication scheme that should be used to access the requested entity.","WWW-Authenticate: Basic"]]]]> 414 0 0 0 _tablepress_export_table_id HTTP Reference: MIME types - application https://code-maze.com/?post_type=tablepress_table&p=419 Sat, 17 Jun 2017 12:00:29 +0000 http://www.code-maze.com/?post_type=tablepress_table&p=419 419 0 0 0 _tablepress_export_table_id HTTP Reference: MIME types - primary https://code-maze.com/?post_type=tablepress_table&p=421 Sat, 17 Jun 2017 12:15:14 +0000 http://www.code-maze.com/?post_type=tablepress_table&p=421 421 0 0 0 _tablepress_export_table_id HTTP Reference: MIME types - multipart https://code-maze.com/?post_type=tablepress_table&p=425 Sat, 17 Jun 2017 12:23:57 +0000 http://www.code-maze.com/?post_type=tablepress_table&p=425 425 0 0 0 _tablepress_export_table_id HTTP Reference: MIME types - text https://code-maze.com/?post_type=tablepress_table&p=427 Sat, 17 Jun 2017 12:28:18 +0000 http://www.code-maze.com/?post_type=tablepress_table&p=427 427 0 0 0 _tablepress_export_table_id HTTP Reference: Request methods https://code-maze.com/?post_type=tablepress_table&p=429 Sat, 17 Jun 2017 12:53:30 +0000 http://www.code-maze.com/?post_type=tablepress_table&p=429 429 0 0 0 _tablepress_export_table_id https://code-maze.com/?post_type=td_nm_notification&p=2800 Wed, 02 May 2018 09:22:28 +0000 https://code-maze.com/?post_type=td_nm_notification&p=2800 2800 0 0 0 Widget https://code-maze.com/?post_type=tve_form_type&p=2805 Wed, 02 May 2018 09:42:09 +0000 https://code-maze.com/?post_type=tve_form_type&p=2805 2805 2804 0 0 Widget https://code-maze.com/?post_type=tve_form_type&p=2817 Wed, 02 May 2018 13:24:57 +0000 https://code-maze.com/?post_type=tve_form_type&p=2817 2817 2816 0 0 Mobile CI Wrap-up https://code-maze.com/?post_type=tablepress_table&p=2821 Wed, 02 May 2018 14:07:25 +0000 https://code-maze.com/?post_type=tablepress_table&p=2821 Testing<\/b>","Functional tests do not require extra action","Functional tests<\/b> require the use of an emulator\/simulator or a physical device"],["Deployment<\/b>","Deploy to live instantly, no code signing","Submit to store for approval, may need uploading of certificates to CI"],["Beta testing<\/b>","Deploy to staging and invite users","Distribute build via email or 3rd party"],["Legacy Version Support<\/b>","N\/A: newest version instantly available for all","Use CI to keep track of binaries versions for reference"]]]]> 2821 0 0 0 _tablepress_export_table_id Widget https://code-maze.com/?post_type=tve_form_type&p=3131 Fri, 18 May 2018 21:45:18 +0000 https://code-maze.com/?post_type=tve_form_type&p=3131 3131 3130 0 0 Widget https://code-maze.com/?post_type=tve_form_type&p=3753 Sun, 01 Jul 2018 14:19:22 +0000 https://code-maze.com/?post_type=tve_form_type&p=3753 3753 3752 0 0 Widget https://code-maze.com/?post_type=tve_form_type&p=3758 Sun, 01 Jul 2018 15:58:56 +0000 https://code-maze.com/?post_type=tve_form_type&p=3758 3758 3757 0 0 Widget https://code-maze.com/?post_type=tve_form_type&p=3821 Thu, 05 Jul 2018 18:28:11 +0000 https://code-maze.com/?post_type=tve_form_type&p=3821 3821 3819 0 0 whole numbers data types https://code-maze.com/?post_type=tablepress_table&p=4148 Tue, 24 Jul 2018 17:42:33 +0000 https://code-maze.com/?post_type=tablepress_table&p=4148 4148 0 0 0 _tablepress_export_table_id Static vs Dynamic languages https://code-maze.com/?post_type=tablepress_table&p=5581 Thu, 24 Jan 2019 07:47:11 +0000 https://code-maze.com/?post_type=tablepress_table&p=5581 5581 0 0 0 _tablepress_export_table_id Widget https://code-maze.com/?post_type=tve_form_type&p=47513 Sat, 02 Mar 2019 22:04:31 +0000 https://code-maze.com/?post_type=tve_form_type&p=47513 47513 47512 0 0 AddARecord https://code-maze.com/?post_type=tablepress_table&p=47772 Sun, 31 Mar 2019 12:05:33 +0000 https://code-maze.com/?post_type=tablepress_table&p=47772 47772 0 0 0 _tablepress_export_table_id HTTP Book Shortcode https://code-maze.com/?post_type=tve_lead_shortcode&p=2790 Tue, 01 May 2018 21:53:55 +0000 https://code-maze.com/?post_type=tve_lead_shortcode&p=2790 2790 0 0 0 Book-download https://code-maze.com/?post_type=tve_lead_1c_signup&p=2791 Tue, 01 May 2018 21:55:18 +0000 https://code-maze.com/?post_type=tve_lead_1c_signup&p=2791 2791 0 0 0 HTTP Book Dowload Box https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=2792 Tue, 01 May 2018 21:56:04 +0000 https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=2792 2792 0 0 0 ASP.NET Core Best Practices Box https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=2797 Wed, 02 May 2018 08:52:55 +0000 https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=2797 2797 0 0 0 HTTP Book Widget https://code-maze.com/tve_lead_group/widget-forms/ Wed, 02 May 2018 09:42:01 +0000 https://code-maze.com/tve_lead_group/widget-forms/ 2804 0 0 0 Angular Widget https://code-maze.com/tve_lead_group/angular-widget__trashed/ Wed, 02 May 2018 13:24:45 +0000 https://code-maze.com/tve_lead_group/angular-widget/ 2816 0 0 0 Troy Hunt Infographic Widget https://code-maze.com/tve_lead_group/troy-hunt-infographic-widget/ Fri, 18 May 2018 21:45:09 +0000 https://code-maze.com/tve_lead_group/troy-hunt-infographic-widget/ 3130 0 0 0 Troy Hunt Infographic Box https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=3133 Fri, 18 May 2018 22:27:29 +0000 https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=3133 3133 0 0 0 ASP.NET Core Best Practices Widget https://code-maze.com/tve_lead_group/asp-net-core-best-practices-widget/ Sun, 01 Jul 2018 14:19:14 +0000 https://code-maze.com/tve_lead_group/asp-net-core-best-practices-widget/ 3752 0 0 0 Angular Best Practices Widget https://code-maze.com/tve_lead_group/angular-best-practices-widget/ Sun, 01 Jul 2018 15:58:45 +0000 https://code-maze.com/tve_lead_group/angular-best-practices-widget/ 3757 0 0 0 Angular Best Practices Box https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=3785 Tue, 03 Jul 2018 17:51:09 +0000 https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=3785 3785 0 0 0 The Definitive Guide to Continuous Integration Widget https://code-maze.com/tve_lead_group/the-definitive-guide-to-continuous-integration-widget/ Thu, 05 Jul 2018 18:27:25 +0000 https://code-maze.com/tve_lead_group/the-definitive-guide-to-continuous-integration-widget/ 3819 0 0 0 The Definitive Guide to Continuous Integration Box https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=3820 Thu, 05 Jul 2018 18:27:34 +0000 https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=3820 3820 0 0 0 GumCoTest Box https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=5823 Sat, 02 Feb 2019 15:25:18 +0000 https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=5823 5823 0 0 0 Mastering Angular Material Widget https://code-maze.com/tve_lead_group/mastering-angular-material-widget/ Sat, 02 Mar 2019 22:04:04 +0000 https://code-maze.com/tve_lead_group/mastering-angular-material-widget/ 47512 0 0 0 Angular Material Box https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=47515 Sat, 02 Mar 2019 22:19:34 +0000 https://code-maze.com/?post_type=tve_lead_2s_lightbox&p=47515 47515 0 0 0 You are at the right place! https://code-maze.com/testimonial/you-are-at-the-right-place/ Tue, 24 Sep 2019 13:25:00 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48861 48861 0 22 0 Source for golden information https://code-maze.com/testimonial/source-for-golden-information/ Thu, 26 Sep 2019 09:06:04 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48864 48864 0 21 0 Great resource for .NET developers https://code-maze.com/testimonial/great-resource-for-net-developers/ Fri, 27 Sep 2019 09:35:14 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48882 48882 0 20 0 Contest November 2019 Shortcode https://code-maze.com/?post_type=tve_lead_shortcode&p=49632 Mon, 25 Nov 2019 00:33:02 +0000 https://code-maze.com/?post_type=tve_lead_shortcode&p=49632 49632 0 0 0 Great series! https://code-maze.com/testimonial/great-series/ Fri, 27 Sep 2019 10:23:51 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48884 48884 0 19 0 EF Core Feedback https://code-maze.com/testimonial/ef-core-feedback/ Fri, 27 Sep 2019 16:19:57 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48885 48885 0 18 0 Loved your work https://code-maze.com/testimonial/loved-your-work/ Fri, 27 Sep 2019 16:52:55 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48887 48887 0 17 0 Hidden Gem in the .NET world https://code-maze.com/testimonial/hidden-gem-in-the-net-world/ Fri, 27 Sep 2019 19:07:41 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48895 48895 0 16 0 The most awesome code blog in C# world perhaps I dare say in programming https://code-maze.com/testimonial/the-most-awesome-code-blog-in-c-world-perhaps-i-dare-say-in-programming/ Fri, 27 Sep 2019 19:48:04 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48897 48897 0 15 0 Best C# resource https://code-maze.com/testimonial/best-c-resource/ Sat, 28 Sep 2019 08:59:52 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48900 48900 0 14 0 Excellent and up to date blog! https://code-maze.com/testimonial/excellent-and-up-to-date-blog/ Tue, 01 Oct 2019 13:19:23 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48930 48930 0 13 0 Kindly start video tutorials https://code-maze.com/testimonial/kindly-start-video-tutorials/ Wed, 02 Oct 2019 20:29:48 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48945 48945 0 12 0 Perfect Stop https://code-maze.com/testimonial/perfect-stop/ Fri, 04 Oct 2019 06:28:27 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48950 48950 0 11 0 Best Explaination https://code-maze.com/testimonial/best-explaination/ Wed, 09 Oct 2019 19:42:43 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=48994 48994 0 10 0 Extraordinary .NET and C# source of code and knowledge https://code-maze.com/testimonial/extraordinary-net-and-c-source-of-code-and-knowledge/ Sat, 12 Oct 2019 20:11:03 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=49026 49026 0 9 0 The articles are really nice and helpfull https://code-maze.com/testimonial/the-articles-are-really-nice-and-helpfull/ Sun, 20 Oct 2019 08:32:31 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=49067 49067 0 8 0 Code Maze is an amazing site https://code-maze.com/testimonial/code-maze-is-an-amazing-site/ Sun, 20 Oct 2019 18:17:51 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=49076 49076 0 7 0 Code Maze is a true discovery https://code-maze.com/testimonial/code-maze-is-a-true-discovery/ Thu, 31 Oct 2019 10:06:34 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=49354 49354 0 6 0 ASP.NET Core Web API – Logging With NLog https://code-maze.com/testimonial/asp-net-core-web-api-logging-with-nlog/ Tue, 05 Nov 2019 10:39:26 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=49439 49439 0 5 0 Awesome https://code-maze.com/testimonial/awesome/ Tue, 05 Nov 2019 21:26:22 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=49447 49447 0 4 0 Intermediate to professionalism https://code-maze.com/testimonial/intermediate-to-professionalism/ Tue, 05 Nov 2019 21:27:45 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=49448 49448 0 3 0 Fantastic https://code-maze.com/testimonial/fantastic/ Thu, 14 Nov 2019 12:17:15 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=49530 49530 0 2 0 Awesome and Very useful article https://code-maze.com/testimonial/awesome-and-very-useful-article/ Mon, 16 Dec 2019 09:14:32 +0000 https://code-maze.com/?post_type=wpm-testimonial&p=49836 49836 0 1 0