{"id":852,"date":"2025-05-05T03:15:37","date_gmt":"2025-05-04T19:15:37","guid":{"rendered":"https:\/\/www.hyy.net\/?p=852"},"modified":"2025-05-05T03:15:37","modified_gmt":"2025-05-04T19:15:37","slug":"pro-asp-net-core-7-tenth-edition","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=852","title":{"rendered":"Pro ASP.NET Core 7 Tenth Edition"},"content":{"rendered":"<style>\n    .body {\n    color: black;\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 1em;\n    line-height: 1.4;\n    margin-bottom: 1em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em\n    }\n.body-center {\n    color: black;\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 1em;\n    line-height: 1.4;\n    margin-bottom: 1em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em;\n    text-align: center\n    }\n.body-center-b {\n    color: black;\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 0.83333em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: center\n    }\n.calibre {\n    display: block;\n    font-size: 1em;\n    margin-bottom: 0;\n    margin-left: 5pt;\n    margin-right: 5pt;\n    margin-top: 0;\n    padding-left: 0;\n    padding-right: 0\n    }\n.calibre1 {\n    display: block\n    }\n.calibre2 {\n    height: 227px;\n    line-height: 1.4;\n    width: 227px\n    }\n.calibre3 {\n    height: auto;\n    line-height: 1.4;\n    width: 40px\n    }\n.calibre4 {\n    height: 71px;\n    width: 100px\n    }\n.calibre5 {\n    height: 311px;\n    width: 254px\n    }\n.calibre6 {\n    display: block;\n    line-height: 1.4;\n    list-style-type: disc;\n    margin-bottom: 1em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em\n    }\n.calibre7 {\n    height: 274px;\n    width: 665px\n    }\n.calibre8 {\n    height: 173px;\n    width: 362px\n    }\n.calibre9 {\n    height: 210px;\n    width: 360px\n    }\n.calibre10 {\n    height: 351px;\n    width: 628px\n    }\n.calibre11 {\n    height: 209px;\n    width: 628px\n    }\n.calibre12 {\n    height: 266px;\n    width: 374px\n    }\n.calibre13 {\n    height: 333px;\n    width: 500px\n    }\n.calibre14 {\n    height: 383px;\n    width: 590px\n    }\n.calibre15 {\n    height: 350px;\n    width: 530px\n    }\n.calibre16 {\n    height: 241px;\n    width: 605px\n    }\n.calibre17 {\n    height: 287px;\n    width: 482px\n    }\n.calibre18 {\n    height: 242px;\n    width: 239px\n    }\n.calibre19 {\n    height: 340px;\n    width: 602px\n    }\n.calibre20 {\n    height: 145px;\n    width: 391px\n    }\n.calibre21 {\n    height: 248px;\n    width: 525px\n    }\n.calibre22 {\n    height: 113px;\n    width: 378px\n    }\n.calibre23 {\n    height: 296px;\n    width: 638px\n    }\n.calibre24 {\n    height: 322px;\n    width: 595px\n    }\n.calibre25 {\n    height: 109px;\n    width: 378px\n    }\n.calibre26 {\n    height: 137px;\n    width: 419px\n    }\n.calibre27 {\n    height: 152px;\n    width: 701px\n    }\n.calibre28 {\n    height: 185px;\n    width: 635px\n    }\n.calibre29 {\n    height: 161px;\n    width: 698px\n    }\n.calibre30 {\n    height: 179px;\n    width: 419px\n    }\n.calibre31 {\n    height: 220px;\n    width: 455px\n    }\n.calibre32 {\n    height: 258px;\n    width: 590px\n    }\n.calibre33 {\n    height: 223px;\n    width: 427px\n    }\n.calibre34 {\n    height: 167px;\n    width: 398px\n    }\n.calibre35 {\n    height: 374px;\n    width: 419px\n    }\n.calibre36 {\n    height: 146px;\n    width: 396px\n    }\n.calibre37 {\n    height: 217px;\n    width: 425px\n    }\n.calibre38 {\n    height: 378px;\n    width: 474px\n    }\n.calibre39 {\n    height: 255px;\n    width: 583px\n    }\n.calibre40 {\n    height: 414px;\n    width: 554px\n    }\n.calibre41 {\n    height: 119px;\n    width: 378px\n    }\n.calibre42 {\n    height: 136px;\n    width: 586px\n    }\n.calibre43 {\n    height: 240px;\n    width: 635px\n    }\n.calibre44 {\n    height: 131px;\n    width: 373px\n    }\n.calibre45 {\n    height: 370px;\n    width: 492px\n    }\n.calibre46 {\n    height: 246px;\n    width: 492px\n    }\n.calibre47 {\n    height: 116px;\n    width: 376px\n    }\n.calibre48 {\n    height: 157px;\n    width: 617px\n    }\n.calibre49 {\n    height: 231px;\n    width: 634px\n    }\n.calibre50 {\n    height: 198px;\n    width: 725px\n    }\n.calibre51 {\n    height: 95px;\n    width: 404px\n    }\n.calibre52 {\n    height: 265px;\n    width: 567px\n    }\n.calibre53 {\n    height: 268px;\n    width: 569px\n    }\n.calibre54 {\n    height: 368px;\n    width: 514px\n    }\n.calibre55 {\n    height: 141px;\n    width: 378px\n    }\n.calibre56 {\n    height: 201px;\n    width: 394px\n    }\n.calibre57 {\n    height: 222px;\n    width: 536px\n    }\n.calibre58 {\n    line-height: 1.4\n    }\n.calibre59 {\n    height: 293px;\n    width: 500px\n    }\n.calibre60 {\n    height: 224px;\n    width: 505px\n    }\n.calibre61 {\n    height: 305px;\n    width: 554px\n    }\n.calibre62 {\n    height: 280px;\n    width: 536px\n    }\n.calibre63 {\n    height: 266px;\n    width: 555px\n    }\n.calibre64 {\n    height: 425px;\n    width: 554px\n    }\n.calibre65 {\n    height: 157px;\n    width: 662px\n    }\n.calibre66 {\n    height: 149px;\n    width: 478px\n    }\n.calibre67 {\n    height: 296px;\n    width: 479px\n    }\n.calibre68 {\n    height: 313px;\n    width: 480px\n    }\n.calibre69 {\n    height: 263px;\n    width: 616px\n    }\n.calibre70 {\n    height: 251px;\n    width: 608px\n    }\n.calibre71 {\n    height: 325px;\n    width: 499px\n    }\n.calibre72 {\n    height: 536px;\n    width: 347px\n    }\n.calibre73 {\n    height: 362px;\n    width: 535px\n    }\n.calibre74 {\n    height: 258px;\n    width: 467px\n    }\n.calibre75 {\n    height: 191px;\n    width: 515px\n    }\n.calibre76 {\n    height: 194px;\n    width: 462px\n    }\n.calibre77 {\n    height: 241px;\n    width: 600px\n    }\n.calibre78 {\n    height: 195px;\n    width: 506px\n    }\n.calibre79 {\n    height: 381px;\n    width: 456px\n    }\n.calibre80 {\n    height: 329px;\n    width: 536px\n    }\n.calibre81 {\n    height: 338px;\n    width: 713px\n    }\n.calibre82 {\n    height: 371px;\n    width: 598px\n    }\n.calibre83 {\n    height: 296px;\n    width: 700px\n    }\n.calibre84 {\n    height: 246px;\n    width: 478px\n    }\n.calibre85 {\n    height: 276px;\n    width: 615px\n    }\n.calibre86 {\n    height: 287px;\n    width: 397px\n    }\n.calibre87 {\n    height: 358px;\n    width: 499px\n    }\n.calibre88 {\n    height: 112px;\n    width: 376px\n    }\n.calibre89 {\n    height: 136px;\n    width: 679px\n    }\n.calibre90 {\n    height: 235px;\n    width: 668px\n    }\n.calibre91 {\n    height: 273px;\n    width: 491px\n    }\n.calibre92 {\n    height: 185px;\n    width: 556px\n    }\n.calibre93 {\n    height: 169px;\n    width: 413px\n    }\n.calibre94 {\n    height: 111px;\n    width: 775px\n    }\n.calibre95 {\n    height: 145px;\n    width: 413px\n    }\n.calibre96 {\n    height: 145px;\n    width: 432px\n    }\n.calibre97 {\n    height: 134px;\n    width: 788px\n    }\n.calibre98 {\n    height: 230px;\n    width: 777px\n    }\n.calibre99 {\n    height: 162px;\n    width: 620px\n    }\n.calibre100 {\n    height: 124px;\n    width: 461px\n    }\n.calibre101 {\n    height: 122px;\n    width: 650px\n    }\n.calibre102 {\n    height: 109px;\n    width: 458px\n    }\n.calibre103 {\n    height: 109px;\n    width: 376px\n    }\n.calibre104 {\n    height: 108px;\n    width: 374px\n    }\n.calibre105 {\n    height: 125px;\n    width: 613px\n    }\n.calibre106 {\n    height: 127px;\n    width: 572px\n    }\n.calibre107 {\n    height: 127px;\n    width: 526px\n    }\n.calibre108 {\n    height: 158px;\n    width: 374px\n    }\n.calibre109 {\n    height: 113px;\n    width: 600px\n    }\n.calibre110 {\n    height: 98px;\n    width: 392px\n    }\n.calibre111 {\n    height: 99px;\n    width: 392px\n    }\n.calibre112 {\n    height: 135px;\n    width: 392px\n    }\n.calibre113 {\n    height: 107px;\n    width: 393px\n    }\n.calibre114 {\n    height: 106px;\n    width: 391px\n    }\n.calibre115 {\n    height: 156px;\n    width: 390px\n    }\n.calibre116 {\n    height: 223px;\n    width: 608px\n    }\n.calibre117 {\n    height: 136px;\n    width: 392px\n    }\n.calibre118 {\n    height: 107px;\n    width: 390px\n    }\n.calibre119 {\n    height: 211px;\n    width: 596px\n    }\n.calibre120 {\n    height: 106px;\n    width: 383px\n    }\n.calibre121 {\n    height: 122px;\n    width: 389px\n    }\n.calibre122 {\n    height: 150px;\n    width: 655px\n    }\n.calibre123 {\n    height: 161px;\n    width: 655px\n    }\n.calibre124 {\n    height: 153px;\n    width: 390px\n    }\n.calibre125 {\n    height: 152px;\n    width: 389px\n    }\n.calibre126 {\n    height: 131px;\n    width: 616px\n    }\n.calibre127 {\n    height: 149px;\n    width: 613px\n    }\n.calibre128 {\n    height: 150px;\n    width: 656px\n    }\n.calibre129 {\n    height: 146px;\n    width: 390px\n    }\n.calibre130 {\n    height: 167px;\n    width: 649px\n    }\n.calibre131 {\n    height: 234px;\n    width: 485px\n    }\n.calibre132 {\n    height: 140px;\n    width: 422px\n    }\n.calibre133 {\n    height: 120px;\n    width: 422px\n    }\n.calibre134 {\n    height: 151px;\n    width: 423px\n    }\n.calibre135 {\n    height: 152px;\n    width: 656px\n    }\n.calibre136 {\n    height: 108px;\n    width: 376px\n    }\n.calibre137 {\n    height: 133px;\n    width: 788px\n    }\n.calibre138 {\n    height: 232px;\n    width: 788px\n    }\n.calibre139 {\n    height: 114px;\n    width: 376px\n    }\n.calibre140 {\n    height: 115px;\n    width: 406px\n    }\n.calibre141 {\n    height: 122px;\n    width: 374px\n    }\n.calibre142 {\n    height: 342px;\n    width: 539px\n    }\n.calibre143 {\n    height: 160px;\n    width: 416px\n    }\n.calibre144 {\n    height: 110px;\n    width: 408px\n    }\n.calibre145 {\n    height: 126px;\n    width: 408px\n    }\n.calibre146 {\n    height: 149px;\n    width: 656px\n    }\n.calibre147 {\n    height: 138px;\n    width: 656px\n    }\n.calibre148 {\n    height: 110px;\n    width: 656px\n    }\n.calibre149 {\n    height: 134px;\n    width: 616px\n    }\n.calibre150 {\n    height: 286px;\n    width: 632px\n    }\n.calibre151 {\n    height: 172px;\n    width: 606px\n    }\n.calibre152 {\n    height: 123px;\n    width: 376px\n    }\n.calibre153 {\n    height: 233px;\n    width: 655px\n    }\n.calibre154 {\n    height: 322px;\n    width: 566px\n    }\n.calibre155 {\n    height: 259px;\n    width: 444px\n    }\n.calibre156 {\n    height: 155px;\n    width: 442px\n    }\n.calibre157 {\n    height: 143px;\n    width: 401px\n    }\n.calibre158 {\n    height: 157px;\n    width: 392px\n    }\n.calibre159 {\n    height: 104px;\n    width: 374px\n    }\n.calibre160 {\n    height: 140px;\n    width: 639px\n    }\n.calibre161 {\n    height: 137px;\n    width: 656px\n    }\n.calibre162 {\n    height: 176px;\n    width: 587px\n    }\n.calibre163 {\n    height: 188px;\n    width: 656px\n    }\n.calibre164 {\n    height: 130px;\n    width: 498px\n    }\n.calibre165 {\n    height: 81px;\n    width: 336px\n    }\n.calibre166 {\n    height: 132px;\n    width: 373px\n    }\n.calibre167 {\n    height: 144px;\n    width: 446px\n    }\n.calibre168 {\n    height: 221px;\n    width: 689px\n    }\n.calibre169 {\n    height: 209px;\n    width: 718px\n    }\n.calibre170 {\n    height: 147px;\n    width: 583px\n    }\n.calibre171 {\n    height: 107px;\n    width: 401px\n    }\n.calibre172 {\n    height: 219px;\n    width: 445px\n    }\n.calibre173 {\n    height: 152px;\n    width: 482px\n    }\n.calibre174 {\n    height: 221px;\n    width: 476px\n    }\n.calibre175 {\n    height: 239px;\n    width: 569px\n    }\n.calibre176 {\n    height: 261px;\n    width: 692px\n    }\n.calibre177 {\n    height: 99px;\n    width: 384px\n    }\n.calibre178 {\n    height: 340px;\n    width: 596px\n    }\n.calibre179 {\n    height: 343px;\n    width: 602px\n    }\n.calibre180 {\n    height: 130px;\n    width: 541px\n    }\n.calibre181 {\n    height: 134px;\n    width: 449px\n    }\n.calibre182 {\n    display: block;\n    line-height: 1.4;\n    list-style-type: decimal;\n    margin-bottom: 1em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em\n    }\n.calibre183 {\n    height: 182px;\n    width: 737px\n    }\n.calibre184 {\n    height: 140px;\n    width: 597px\n    }\n.calibre185 {\n    height: 341px;\n    width: 655px\n    }\n.calibre186 {\n    height: 358px;\n    width: 537px\n    }\n.calibre187 {\n    height: 213px;\n    width: 452px\n    }\n.calibre188 {\n    height: 182px;\n    width: 503px\n    }\n.calibre189 {\n    height: 425px;\n    width: 541px\n    }\n.calibre190 {\n    height: 190px;\n    width: 578px\n    }\n.calibre191 {\n    height: 263px;\n    width: 572px\n    }\n.calibre192 {\n    height: 316px;\n    width: 593px\n    }\n.calibre193 {\n    height: 162px;\n    width: 456px\n    }\n.calibre194 {\n    height: 200px;\n    width: 467px\n    }\n.calibre195 {\n    height: 194px;\n    width: 635px\n    }\n.calibre196 {\n    height: 137px;\n    width: 642px\n    }\n.calibre197 {\n    height: 191px;\n    width: 537px\n    }\n.calibre198 {\n    height: 208px;\n    width: 655px\n    }\n.calibre199 {\n    height: 467px;\n    width: 609px\n    }\n.calibre200 {\n    height: 317px;\n    width: 552px\n    }\n.calibre201 {\n    height: 204px;\n    width: 537px\n    }\n.calibre202 {\n    height: 235px;\n    width: 537px\n    }\n.calibre203 {\n    height: 161px;\n    width: 656px\n    }\n.calibre204 {\n    height: 224px;\n    width: 487px\n    }\n.calibre205 {\n    height: 209px;\n    width: 656px\n    }\n.calibre206 {\n    height: 235px;\n    width: 478px\n    }\n.calibre207 {\n    height: 227px;\n    width: 440px\n    }\n.calibre208 {\n    height: 218px;\n    width: 537px\n    }\n.calibre209 {\n    height: 195px;\n    width: 476px\n    }\n.calibre210 {\n    height: 224px;\n    width: 656px\n    }\n.calibre211 {\n    height: 309px;\n    width: 590px\n    }\n.calibre212 {\n    height: 326px;\n    width: 572px\n    }\n.calibre213 {\n    height: 141px;\n    width: 452px\n    }\n.calibre214 {\n    height: 209px;\n    width: 473px\n    }\n.calibre215 {\n    height: 323px;\n    width: 537px\n    }\n.calibre216 {\n    height: 215px;\n    width: 524px\n    }\n.calibre217 {\n    height: 248px;\n    width: 593px\n    }\n.calibre218 {\n    height: 255px;\n    width: 524px\n    }\n.calibre219 {\n    height: 230px;\n    width: 537px\n    }\n.calibre220 {\n    height: 234px;\n    width: 523px\n    }\n.calibre221 {\n    height: 144px;\n    width: 654px\n    }\n.calibre222 {\n    height: 262px;\n    width: 524px\n    }\n.calibre223 {\n    height: 261px;\n    width: 523px\n    }\n.calibre224 {\n    height: 334px;\n    width: 653px\n    }\n.calibre225 {\n    height: 109px;\n    width: 389px\n    }\n.calibre226 {\n    height: 215px;\n    width: 536px\n    }\n.calibre227 {\n    height: 110px;\n    width: 384px\n    }\n.calibre228 {\n    height: 159px;\n    width: 629px\n    }\n.calibre229 {\n    height: 133px;\n    width: 518px\n    }\n.calibre230 {\n    height: 230px;\n    width: 505px\n    }\n.calibre231 {\n    height: 113px;\n    width: 376px\n    }\n.calibre232 {\n    height: 215px;\n    width: 433px\n    }\n.calibre233 {\n    height: 296px;\n    width: 639px\n    }\n.calibre234 {\n    height: 254px;\n    width: 590px\n    }\n.calibre235 {\n    height: 218px;\n    width: 508px\n    }\n.calibre236 {\n    height: 205px;\n    width: 446px\n    }\n.calibre237 {\n    height: 244px;\n    width: 537px\n    }\n.calibre238 {\n    height: 200px;\n    width: 558px\n    }\n.calibre239 {\n    height: 224px;\n    width: 537px\n    }\n.calibre240 {\n    height: 279px;\n    width: 537px\n    }\n.calibre241 {\n    height: 186px;\n    width: 653px\n    }\n.calibre242 {\n    height: 275px;\n    width: 662px\n    }\n.calibre243 {\n    height: 207px;\n    width: 510px\n    }\n.calibre244 {\n    height: 248px;\n    width: 537px\n    }\n.calibre245 {\n    height: 235px;\n    width: 655px\n    }\n.calibre246 {\n    height: 263px;\n    width: 617px\n    }\n.calibre247 {\n    height: 299px;\n    width: 505px\n    }\n.calibre248 {\n    height: 116px;\n    width: 524px\n    }\n.calibre249 {\n    height: 281px;\n    width: 657px\n    }\n.calibre250 {\n    height: 315px;\n    width: 651px\n    }\n.calibre251 {\n    height: 189px;\n    width: 462px\n    }\n.calibre252 {\n    height: 185px;\n    width: 404px\n    }\n.calibre253 {\n    height: 225px;\n    width: 436px\n    }\n.calibre254 {\n    height: 182px;\n    width: 434px\n    }\n.calibre255 {\n    height: 183px;\n    width: 435px\n    }\n.calibre256 {\n    height: 182px;\n    width: 433px\n    }\n.calibre257 {\n    height: 292px;\n    width: 602px\n    }\n.calibre258 {\n    height: 310px;\n    width: 601px\n    }\n.calibre259 {\n    height: 309px;\n    width: 599px\n    }\n.calibre260 {\n    height: 227px;\n    width: 573px\n    }\n.calibre261 {\n    height: 261px;\n    width: 484px\n    }\n.calibre262 {\n    height: 204px;\n    width: 656px\n    }\n.calibre263 {\n    height: 216px;\n    width: 459px\n    }\n.calibre264 {\n    height: 245px;\n    width: 527px\n    }\n.calibre265 {\n    height: 89px;\n    width: 523px\n    }\n.calibre266 {\n    height: 329px;\n    width: 603px\n    }\n.calibre267 {\n    height: 388px;\n    width: 603px\n    }\n.calibre268 {\n    height: 336px;\n    width: 603px\n    }\n.calibre269 {\n    height: 212px;\n    width: 392px\n    }\n.calibre270 {\n    height: 223px;\n    width: 226px\n    }\n.calibre271 {\n    height: 287px;\n    width: 603px\n    }\n.calibre272 {\n    height: 251px;\n    width: 603px\n    }\n.calibre273 {\n    height: 203px;\n    width: 602px\n    }\n.calibre274 {\n    height: 379px;\n    width: 605px\n    }\n.calibre275 {\n    height: 278px;\n    width: 533px\n    }\n.calibre276 {\n    height: 221px;\n    width: 638px\n    }\n.calibre277 {\n    height: 232px;\n    width: 655px\n    }\n.calibre278 {\n    height: 245px;\n    width: 684px\n    }\n.calibre279 {\n    height: 361px;\n    width: 603px\n    }\n.calibre280 {\n    height: 474px;\n    width: 639px\n    }\n.calibre281 {\n    height: 451px;\n    width: 603px\n    }\n.calibre282 {\n    height: 295px;\n    width: 582px\n    }\n.calibre283 {\n    height: 360px;\n    width: 603px\n    }\n.calibre284 {\n    height: 266px;\n    width: 655px\n    }\n.calibre285 {\n    height: 211px;\n    width: 655px\n    }\n.calibre286 {\n    height: 283px;\n    width: 623px\n    }\n.calibre287 {\n    height: 281px;\n    width: 656px\n    }\n.calibre288 {\n    height: 335px;\n    width: 623px\n    }\n.calibre289 {\n    height: 248px;\n    width: 462px\n    }\n.calibre290 {\n    height: 244px;\n    width: 655px\n    }\n.calibre291 {\n    height: 192px;\n    width: 425px\n    }\n.calibre292 {\n    height: 186px;\n    width: 537px\n    }\n.calibre293 {\n    height: 156px;\n    width: 655px\n    }\n.calibre294 {\n    height: 269px;\n    width: 413px\n    }\n.calibre295 {\n    height: 316px;\n    width: 509px\n    }\n.calibre296 {\n    height: 274px;\n    width: 409px\n    }\n.calibre297 {\n    height: 284px;\n    width: 462px\n    }\n.calibre298 {\n    height: 287px;\n    width: 464px\n    }\n.calibre299 {\n    height: 262px;\n    width: 530px\n    }\n.calibre300 {\n    height: 278px;\n    width: 565px\n    }\n.calibre301 {\n    height: 343px;\n    width: 463px\n    }\n.calibre302 {\n    height: 285px;\n    width: 462px\n    }\n.calibre303 {\n    height: 212px;\n    width: 527px\n    }\n.calibre304 {\n    height: 257px;\n    width: 462px\n    }\n.calibre305 {\n    height: 359px;\n    width: 512px\n    }\n.calibre306 {\n    height: 324px;\n    width: 460px\n    }\n.calibre307 {\n    height: 290px;\n    width: 494px\n    }\n.calibre308 {\n    height: 301px;\n    width: 489px\n    }\n.calibre309 {\n    height: 317px;\n    width: 497px\n    }\n.calibre310 {\n    height: 257px;\n    width: 553px\n    }\n.calibre311 {\n    height: 332px;\n    width: 495px\n    }\n.calibre312 {\n    height: 224px;\n    width: 421px\n    }\n.calibre313 {\n    height: 347px;\n    width: 491px\n    }\n.calibre314 {\n    height: 284px;\n    width: 488px\n    }\n.calibre315 {\n    height: 321px;\n    width: 577px\n    }\n.calibre316 {\n    height: 300px;\n    width: 518px\n    }\n.calibre317 {\n    height: 263px;\n    width: 566px\n    }\n.calibre318 {\n    height: 325px;\n    width: 398px\n    }\n.calibre319 {\n    height: 264px;\n    width: 577px\n    }\n.calibre320 {\n    height: 113px;\n    width: 578px\n    }\n.calibre321 {\n    height: 135px;\n    width: 576px\n    }\n.calibre322 {\n    height: 233px;\n    width: 575px\n    }\n.calibre323 {\n    height: 196px;\n    width: 753px\n    }\n.calibre324 {\n    height: 217px;\n    width: 577px\n    }\n.calibre325 {\n    height: 126px;\n    width: 630px\n    }\n.calibre326 {\n    height: 97px;\n    width: 536px\n    }\n.calibre327 {\n    height: 120px;\n    width: 462px\n    }\n.calibre328 {\n    height: 185px;\n    width: 463px\n    }\n.calibre329 {\n    height: 176px;\n    width: 599px\n    }\n.calibre330 {\n    height: 206px;\n    width: 618px\n    }\n.calibre331 {\n    height: 191px;\n    width: 577px\n    }\n.calibre332 {\n    height: 191px;\n    width: 576px\n    }\n.calibre333 {\n    height: 226px;\n    width: 452px\n    }\n.calibre334 {\n    height: 211px;\n    width: 460px\n    }\n.calibre335 {\n    height: 208px;\n    width: 460px\n    }\n.calibre336 {\n    height: 335px;\n    width: 463px\n    }\n.calibre337 {\n    height: 280px;\n    width: 418px\n    }\n.calibre338 {\n    height: 300px;\n    width: 461px\n    }\n.calibre339 {\n    height: 216px;\n    width: 389px\n    }\n.calibre340 {\n    height: 316px;\n    width: 476px\n    }\n.calibre341 {\n    font-style: italic\n    }\n.calibre342 {\n    height: 281px;\n    width: 479px\n    }\n.calibre343 {\n    height: 354px;\n    width: 425px\n    }\n.calibre344 {\n    height: 320px;\n    width: 564px\n    }\n.calibre345 {\n    height: 335px;\n    width: 578px\n    }\n.calibre346 {\n    height: 347px;\n    width: 533px\n    }\n.calibre347 {\n    height: 80px;\n    width: 338px\n    }\n.calibre348 {\n    height: 339px;\n    width: 461px\n    }\n.calibre349 {\n    height: 326px;\n    width: 429px\n    }\n.calibre350 {\n    height: 121px;\n    width: 710px\n    }\n.calibre351 {\n    height: 227px;\n    width: 712px\n    }\n.calibre352 {\n    height: 313px;\n    width: 437px\n    }\n.calibre353 {\n    height: 335px;\n    width: 391px\n    }\n.calibre354 {\n    height: 168px;\n    width: 519px\n    }\n.calibre355 {\n    height: 277px;\n    width: 521px\n    }\n.calibre356 {\n    height: 263px;\n    width: 508px\n    }\n.calibre357 {\n    height: 233px;\n    width: 580px\n    }\n.calibre358 {\n    height: 242px;\n    width: 577px\n    }\n.calibre359 {\n    height: 254px;\n    width: 476px\n    }\n.calibre360 {\n    height: 275px;\n    width: 503px\n    }\n.calibre361 {\n    height: 248px;\n    width: 572px\n    }\n.calibre362 {\n    height: 296px;\n    width: 407px\n    }\n.calibre363 {\n    height: 275px;\n    width: 481px\n    }\n.calibre364 {\n    height: 297px;\n    width: 564px\n    }\n.calibre365 {\n    height: 135px;\n    width: 377px\n    }\n.calibre366 {\n    height: 335px;\n    width: 462px\n    }\n.calibre367 {\n    height: 281px;\n    width: 450px\n    }\n.calibre368 {\n    height: 224px;\n    width: 593px\n    }\n.calibre369 {\n    height: 176px;\n    width: 462px\n    }\n.calibre370 {\n    height: 356px;\n    width: 459px\n    }\n.calibre371 {\n    height: 328px;\n    width: 462px\n    }\n.calibre372 {\n    height: 312px;\n    width: 578px\n    }\n.calibre373 {\n    height: 325px;\n    width: 482px\n    }\n.calibre374 {\n    height: 298px;\n    width: 577px\n    }\n.calibre375 {\n    height: 257px;\n    width: 576px\n    }\n.calibre376 {\n    height: 168px;\n    width: 460px\n    }\n.calibre377 {\n    height: 231px;\n    width: 656px\n    }\n.calibre378 {\n    height: 266px;\n    width: 459px\n    }\n.calibre379 {\n    height: 197px;\n    width: 665px\n    }\n.calibre380 {\n    height: 248px;\n    width: 577px\n    }\n.calibre381 {\n    height: 241px;\n    width: 579px\n    }\n.calibre382 {\n    height: 242px;\n    width: 377px\n    }\n.calibre383 {\n    height: 344px;\n    width: 461px\n    }\n.calibre384 {\n    height: 215px;\n    width: 656px\n    }\n.calibre385 {\n    height: 206px;\n    width: 632px\n    }\n.calibre386 {\n    height: 201px;\n    width: 632px\n    }\n.calibre387 {\n    height: 222px;\n    width: 632px\n    }\n.calibre388 {\n    height: 255px;\n    width: 632px\n    }\n.calibre389 {\n    height: 226px;\n    width: 632px\n    }\n.calibre390 {\n    height: 232px;\n    width: 632px\n    }\n.calibre391 {\n    height: 230px;\n    width: 632px\n    }\n.calibre392 {\n    height: 287px;\n    width: 604px\n    }\n.calibre393 {\n    height: 232px;\n    width: 620px\n    }\n.calibre394 {\n    height: 215px;\n    width: 620px\n    }\n.calibre395 {\n    height: 326px;\n    width: 528px\n    }\n.calibre396 {\n    height: 347px;\n    width: 461px\n    }\n.calibre397 {\n    height: 316px;\n    width: 590px\n    }\n.calibre398 {\n    height: 364px;\n    width: 569px\n    }\n.calibre399 {\n    height: 173px;\n    width: 775px\n    }\n.calibre400 {\n    height: 320px;\n    width: 554px\n    }\n.calibre401 {\n    height: 222px;\n    width: 775px\n    }\n.calibre402 {\n    height: 341px;\n    width: 536px\n    }\n.calibre403 {\n    height: 251px;\n    width: 750px\n    }\n.calibre404 {\n    height: 206px;\n    width: 578px\n    }\n.calibre405 {\n    height: 350px;\n    width: 536px\n    }\n.calibre406 {\n    height: 268px;\n    width: 671px\n    }\n.calibre407 {\n    height: 444px;\n    width: 365px\n    }\n.calibre408 {\n    height: 236px;\n    width: 653px\n    }\n.calibre409 {\n    height: 356px;\n    width: 509px\n    }\n.calibre410 {\n    height: 310px;\n    width: 648px\n    }\n.calibre411 {\n    height: 352px;\n    width: 464px\n    }\n.calibre412 {\n    height: 212px;\n    width: 441px\n    }\n.calibre413 {\n    height: 236px;\n    width: 578px\n    }\n.calibre414 {\n    height: 261px;\n    width: 446px\n    }\n.calibre415 {\n    height: 254px;\n    width: 460px\n    }\n.calibre416 {\n    height: 255px;\n    width: 577px\n    }\n.calibre417 {\n    height: 254px;\n    width: 462px\n    }\n.calibre418 {\n    height: 243px;\n    width: 461px\n    }\n.calibre419 {\n    height: 298px;\n    width: 521px\n    }\n.calibre420 {\n    height: 161px;\n    width: 697px\n    }\n.calibre421 {\n    height: 239px;\n    width: 655px\n    }\n.calibre422 {\n    height: 425px;\n    width: 432px\n    }\n.calibre423 {\n    height: 183px;\n    width: 599px\n    }\n.calibre424 {\n    height: 184px;\n    width: 668px\n    }\n.calibre425 {\n    height: 275px;\n    width: 536px\n    }\n.calibre426 {\n    height: 240px;\n    width: 521px\n    }\n.calibre427 {\n    height: 235px;\n    width: 567px\n    }\n.calibre428 {\n    height: 233px;\n    width: 601px\n    }\n.calibre429 {\n    height: 203px;\n    width: 551px\n    }\n.calibre430 {\n    height: 263px;\n    width: 535px\n    }\n.calibre431 {\n    height: 275px;\n    width: 461px\n    }\n.calibre432 {\n    height: 163px;\n    width: 377px\n    }\n.calibre433 {\n    height: 219px;\n    width: 700px\n    }\n.calibre434 {\n    height: 229px;\n    width: 594px\n    }\n.co-summary-bullet {\n    color: #005;\n    display: list-item;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1em;\n    line-height: 1.4;\n    page-break-inside: avoid\n    }\n.co-summary-head {\n    color: #005;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1.29167em;\n    font-weight: Bold;\n    line-height: 1.2;\n    margin-bottom: 0.6em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1.8em;\n    page-break-after: avoid\n    }\n.contenttable-0-col {\n    display: table-column;\n    line-height: 1.4\n    }\n.contenttable-0-colgroup {\n    display: table-column-group;\n    line-height: 1.4\n    }\n.contenttable-0-p-l {\n    color: black;\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 0.83333em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: right\n    }\n.contenttable-0-p-r {\n    color: black;\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 0.83333em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0\n    }\n.contenttable-0-table {\n    border-bottom-color: currentColor;\n    border-bottom-style: none;\n    border-bottom-width: 0;\n    border-collapse: separate;\n    border-left-color: currentColor;\n    border-left-style: none;\n    border-left-width: 0;\n    border-right-color: currentColor;\n    border-right-style: none;\n    border-right-width: 0;\n    border-spacing: 2px;\n    border-top-color: currentColor;\n    border-top-style: none;\n    border-top-width: 0;\n    display: table;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-top: 0;\n    text-indent: 0;\n    vertical-align: top;\n    width: 100%\n    }\n.contenttable-0-tbody {\n    display: table-row-group;\n    line-height: 1.4;\n    vertical-align: middle\n    }\n.contenttable-0-td {\n    display: table-cell;\n    line-height: 1.4;\n    padding-bottom: 1px;\n    padding-left: 1px;\n    padding-right: 1px;\n    padding-top: 1px;\n    text-align: inherit;\n    vertical-align: inherit\n    }\n.contenttable-0-tr {\n    display: table-row;\n    vertical-align: inherit\n    }\n.contenttable-1-table {\n    border-collapse: collapse;\n    border-spacing: 2px;\n    display: table;\n    line-height: 1.4;\n    margin-bottom: 5px;\n    margin-top: 0;\n    page-break-inside: avoid;\n    text-indent: 0;\n    width: 100%\n    }\n.contenttable-1-tbody {\n    display: table-row-group;\n    vertical-align: middle\n    }\n.contenttable-1-td {\n    border-bottom-color: black;\n    border-bottom-style: solid;\n    border-bottom-width: 2px;\n    border-left-color: black;\n    border-left-style: solid;\n    border-left-width: 2px;\n    border-right-color: black;\n    border-right-style: solid;\n    border-right-width: 2px;\n    border-top-color: black;\n    border-top-style: solid;\n    border-top-width: 2px;\n    display: table-cell;\n    font-size: 1em;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    padding-bottom: 2px;\n    padding-left: 5px;\n    padding-right: 5px;\n    padding-top: 2px;\n    text-align: left;\n    vertical-align: top\n    }\n.contenttable-1-th {\n    background-color: #e7f0f9;\n    border-bottom-color: black;\n    border-bottom-style: solid;\n    border-bottom-width: 2px;\n    border-left-color: black;\n    border-left-style: solid;\n    border-left-width: 2px;\n    border-right-color: black;\n    border-right-style: solid;\n    border-right-width: 2px;\n    border-top-color: black;\n    border-top-style: solid;\n    border-top-width: 2px;\n    display: table-cell;\n    font-size: 1em;\n    font-weight: bold;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    padding-bottom: 2px;\n    padding-left: 5px;\n    padding-right: 5px;\n    padding-top: 2px;\n    vertical-align: top\n    }\n.contenttable-1-thead {\n    display: table-header-group;\n    vertical-align: middle\n    }\n.copyright {\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 1em;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0.67em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0.67em;\n    text-align: center\n    }\n.copyrighta {\n    color: #005;\n    display: block;\n    font-family: \"Garamond\", sans-serif;\n    font-size: 1.83333em;\n    font-style: italic;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: center\n    }\n.copyrightb {\n    color: gray;\n    display: block;\n    font-family: \"Garamond\", sans-serif;\n    font-size: 1.29167em;\n    font-variant: small-caps;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: center\n    }\n.copyrightbody {\n    color: black;\n    display: block;\n    font-family: \"Garamond\", sans-serif;\n    font-size: 1.29167em;\n    line-height: 1.2;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: center\n    }\n.copyrightbodyb {\n    color: black;\n    display: block;\n    font-family: \"Garamond\", sans-serif;\n    font-size: 0.75em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: center\n    }\n.copyrightc {\n    color: black;\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 1.29167em;\n    font-weight: bold;\n    line-height: 1.2;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: center\n    }\n.copyrightfigures {\n    color: black;\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 1em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: center\n    }\n.copyrightfiguresb {\n    color: black;\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 1em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: right\n    }\n.figure {\n    display: block;\n    line-height: 1.4;\n    page-break-inside: avoid\n    }\n.figure1 {\n    color: #00B050;\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 1.29167em;\n    line-height: 1.4;\n    margin-bottom: 0.8em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1.2em;\n    page-break-after: avoid\n    }\n.figurecaption {\n    color: #005;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1em;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 1em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em\n    }\n.fm-bold {\n    font-weight: Bold\n    }\n.fm-callout-head {\n    color: #005;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1.2em;\n    font-weight: bold;\n    line-height: 1.4;\n    text-transform: uppercase\n    }\n.fm-code-continuation-arrow {\n    color: #b2b2b2;\n    font-family: monospace\n    }\n.fm-code-in-text {\n    font-family: monospace;\n    font-size: 1.29167em;\n    line-height: 1.2\n    }\n.fm-code-in-text1 {\n    font-family: monospace;\n    font-size: 1.2em;\n    line-height: 1.4\n    }\n.fm-code-listing-caption {\n    background-color: #005;\n    color: #EAEAEA;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1em;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0.6em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em\n    }\n.fm-head {\n    color: #141464;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1.66667em;\n    font-style: italic;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0.2em;\n    margin-left: 3em;\n    margin-right: 0;\n    margin-top: 1.2em;\n    page-break-after: avoid;\n    text-indent: -3em\n    }\n.fm-head-1toc {\n    color: #141464;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 5em;\n    margin-right: 0;\n    margin-top: 0.3em;\n    page-break-after: avoid;\n    text-indent: -2em\n    }\n.fm-head-2toc {\n    color: #141464;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 0.83333em;\n    font-style: italic;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 9.4em;\n    margin-right: 0;\n    margin-top: 0.3em;\n    page-break-after: avoid;\n    text-indent: -3em\n    }\n.fm-head1 {\n    color: #141464;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1.29167em;\n    font-style: italic;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0.2em;\n    margin-left: 3em;\n    margin-right: 0;\n    margin-top: 1em;\n    page-break-after: avoid;\n    text-indent: -3em\n    }\n.fm-head2 {\n    color: #141464;\n    display: block;\n    font-family: \"Arial\", sans-serif;\n    font-size: 1.29167em;\n    font-variant: small-caps;\n    font-weight: bold;\n    line-height: 1.2;\n    margin-bottom: 0.2em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em\n    }\n.fm-head3 {\n    color: #141464;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1em;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0.2em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em;\n    page-break-after: avoid\n    }\n.fm-italics {\n    font-style: Italic;\n    text-transform: none\n    }\n.fm-list-bullet {\n    display: list-item;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 1em;\n    line-height: 1.4;\n    page-break-inside: avoid\n    }\n.fm-quote {\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 1em;\n    font-style: italic;\n    line-height: 1.4;\n    margin-bottom: 1.2em;\n    margin-left: 2.8em;\n    margin-right: 0;\n    margin-top: 0.6em\n    }\n.fm-quote-source {\n    display: block;\n    font-family: \"Verdana\", sans-serif;\n    font-size: 1em;\n    line-height: 1.4;\n    margin-bottom: 1.2em;\n    margin-left: 2.8em;\n    margin-right: 0;\n    margin-top: 0.6em;\n    text-align: right\n    }\n.fm-sidebar-block {\n    background-color: #e7f0f9;\n    border-bottom-color: currentColor;\n    border-bottom-style: solid;\n    border-bottom-width: 0;\n    border-left-color: currentColor;\n    border-left-style: solid;\n    border-left-width: 0;\n    border-radius: 12px 12px 12px 12px;\n    border-right-color: currentColor;\n    border-right-style: solid;\n    border-right-width: 0;\n    border-top-color: currentColor;\n    border-top-style: solid;\n    border-top-width: 0;\n    bottom: 25px;\n    color: #8fb1d0;\n    display: block;\n    font-size: 1em;\n    left: 25px;\n    line-height: 1.4;\n    margin-bottom: 1em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em;\n    padding-bottom: 10px;\n    padding-left: 10px;\n    padding-right: 10px;\n    padding-top: 10px;\n    right: 25px;\n    top: 35px;\n    z-index: 1\n    }\n.fm-sidebar-text {\n    color: black;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 0.83333em;\n    line-height: 1.4;\n    margin-bottom: 1em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em\n    }\n.fm-sidebar-title {\n    border-bottom-color: currentColor;\n    border-bottom-style: none;\n    border-bottom-width: 0;\n    border-left-color: currentColor;\n    border-left-style: none;\n    border-left-width: 0;\n    border-right-color: currentColor;\n    border-right-style: none;\n    border-right-width: 0;\n    border-top-color: currentColor;\n    border-top-style: none;\n    border-top-width: 0;\n    color: #005;\n    display: block;\n    font-family: Futura, sans-serif;\n    font-size: 1.29167em;\n    font-weight: bold;\n    line-height: 1.2;\n    margin-bottom: 1em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em;\n    padding-bottom: -10px;\n    padding-left: 1px;\n    padding-right: 1px;\n    padding-top: 15px;\n    text-align: left;\n    vertical-align: top;\n    z-index: 1\n    }\n.fm-superscript {\n    font-size: 0.625em;\n    line-height: 1.4;\n    vertical-align: super\n    }\n.fm-table-body {\n    color: black;\n    display: block;\n    font-family: \"Arial\", sans-serif;\n    font-size: 0.83333em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 30px;\n    margin-top: 0\n    }\n.fm-table-caption {\n    color: #141464;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1em;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0.6em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1.2em;\n    page-break-after: avoid;\n    text-align: justify\n    }\n.fm-table-head {\n    color: #005;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1em;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0.2em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0.2em;\n    page-break-after: avoid\n    }\n.grouptitlesix {\n    color: #262626;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 0.83333em;\n    font-weight: Bold;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0.9em;\n    text-align: left;\n    text-indent: 0\n    }\n.index {\n    color: #4863A0;\n    text-decoration: none\n    }\n.level1ix {\n    color: #000;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 0.83333em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 1.8em;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: left;\n    text-indent: -1.8em\n    }\n.level2ix {\n    color: #000;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 0.83333em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 1.8em;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: left;\n    text-indent: -0.9em\n    }\n.level3ix {\n    color: #000;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 0.83333em;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 3em;\n    margin-right: 0;\n    margin-top: 0;\n    text-align: left;\n    text-indent: -1.8em\n    }\n.list {\n    display: block;\n    font-size: 1em;\n    line-height: 1.4;\n    margin-bottom: 5px;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 5px;\n    page-break-inside: avoid\n    }\n.mc-small-caps {\n    color: #005;\n    font-family: \"Arial\", sans-serif;\n    font-size: 1em;\n    font-variant: small-caps;\n    font-weight: Bold\n    }\n.programlisting {\n    background-color: #f9f9f9;\n    color: black;\n    display: block;\n    font-family: monospace;\n    font-size: 0.83333em;\n    line-height: 1.4;\n    margin-bottom: 1em;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 1em;\n    padding-bottom: 10px;\n    padding-left: 5px;\n    padding-right: 5px;\n    padding-top: 10px;\n    text-indent: 0;\n    white-space: pre-wrap;\n    z-index: 1\n    }\n.segoe {\n    font-family: \"Segoe UI Symbol\", sans-serif\n    }\n.tocchapters {\n    color: #005;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1em;\n    font-style: italic;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 2.4em;\n    margin-right: 0;\n    margin-top: 0.3em;\n    text-indent: -1.6em\n    }\n.tochead {\n    border-bottom-color: #005;\n    border-bottom-style: solid;\n    border-bottom-width: 0.1em;\n    color: #005;\n    display: block;\n    font-family: \"Franklin Gothic Medium\", sans-serif;\n    font-size: 1.66667em;\n    font-style: italic;\n    font-weight: bold;\n    line-height: 1.4;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0.3em;\n    page-break-after: avoid\n    }\n.tocparts {\n    color: #647B9C;\n    display: block;\n    font-family: \"NACNM D+ New Baskerville\", serif;\n    font-size: 1.29167em;\n    font-weight: bold;\n    line-height: 1.2;\n    margin-bottom: 0;\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0.4em;\n    page-break-after: avoid\n    }\n.url {\n    color: #4080BF;\n    text-decoration: none\n    }\n.url1 {\n    color: #4080BF;\n    line-height: 1.4;\n    text-decoration: none\n    }<\/p>\n<p>@page {\n    margin-bottom: 5pt;\n    margin-top: 5pt\n    }<\/p>\n<\/style>\n<div class=\"calibre\" id=\"calibre_link-0\">\n<div class=\"calibre1\" id=\"calibre_link-1264\">\n<h1 class=\"tochead\" id=\"calibre_link-1265\">Praises from reviewers of <i class=\"fm-italics\">Pro ASP.NET Core 7, Tenth Edition<\/i><\/h1>\n<p class=\"fm-quote\">If you\u2019re looking for breadth and depth coverage of ASP.NET Core development, this is the book for you.<\/p>\n<p class=\"fm-quote-source\">&mdash;Greg White, Software Development Manager, PicoBrew Inc.<\/p>\n<p class=\"fm-quote\">A must have book for the .NET developer\/engineer.<\/p>\n<p class=\"fm-quote-source\">&mdash;Foster Haines, Consultant, Foster\u2019s Website Company<\/p>\n<p class=\"fm-quote\">The book for web development professionals.<\/p>\n<p class=\"fm-quote-source\">&mdash;Renato Gentile, Solutions Architect, S3K S.p.A.<\/p>\n<p class=\"fm-quote\">This book guides you as a beginner and will remain your for-ever reference book.<\/p>\n<p class=\"fm-quote-source\">&mdash;Werner Nindl, Partner, Nova Advisory<\/p>\n<p class=\"fm-quote\">An encyclopedic journey.<\/p>\n<p class=\"fm-quote-source\">&mdash;Richard Young, IT Director, Design Synthesis, Inc<\/p>\n<p class=\"fm-quote\">From tiny throw-away sites to large production websites, this book teaches all you need to know.<\/p>\n<p class=\"fm-quote-source\">&mdash;Samuel Bosch, Team Lead, ILVO<\/p>\n<p class=\"fm-quote\">By the end of this book you should be able to write code for real-world projects.<\/p>\n<p class=\"fm-quote-source\">&mdash;Rich Yonts, Senior Software Engineer, Teradata<\/p>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-1\">\n<div class=\"calibre1\" id=\"calibre_link-1266\">\n<p class=\"copyrightbody\">&nbsp;&nbsp;<\/p>\n<p class=\"copyrightfigures\"><img decoding=\"async\" alt=\"\" class=\"calibre2\" src=\"\/images\/proaspnetcore7\/000442.png\" \/><\/p>\n<p class=\"copyrightc\">&nbsp;<\/p>\n<p class=\"copyrightc\">&nbsp;<\/p>\n<h1 class=\"copyrighta\" id=\"calibre_link-1267\">Pro ASP.NET Core 7<\/h1>\n<p class=\"copyrightb\">Tenth Edition<\/p>\n<p class=\"copyrightc\">&nbsp;<\/p>\n<p class=\"copyrightc\"><a id=\"calibre_link-1268\"><\/a>Adam Freeman<\/p>\n<p class=\"copyrightc\">&nbsp;&nbsp;<\/p>\n<p class=\"copyrightbodyb\">To comment go to <a class=\"index\" href=\"https:\/\/livebook.manning.com\/#!\/book\/pro-aspdotnet-core-7-tenth-edition\/discussion\">liveBook<\/a><\/p>\n<p class=\"copyrightc\">&nbsp;<\/p>\n<p class=\"copyrightc\">&nbsp;<\/p>\n<p class=\"copyrightfigures\"><img loading=\"lazy\" decoding=\"async\" alt=\"\" class=\"calibre3\" height=\"71\" src=\"\/images\/proaspnetcore7\/000441.png\" width=\"100\" \/><\/p>\n<p class=\"copyright\">Manning<\/p>\n<p class=\"copyright\">Shelter Island<\/p>\n<p class=\"copyrightc\">&nbsp;<\/p>\n<p class=\"copyrightbodyb\">For more information on this and other Manning titles go to<\/p>\n<p class=\"copyrightbodyb\"><a class=\"index\" href=\"https:\/\/www.manning.com\/\">www.manning.com<\/a><\/p>\n<p class=\"copyrightc\">&nbsp;<\/p>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-2\">\n<div class=\"calibre1\" id=\"calibre_link-1269\">\n<h1 class=\"copyright\" id=\"calibre_link-1270\">Copyright<\/h1>\n<p class=\"body\">For online information and ordering of these&nbsp; and other Manning books, please visit <a class=\"url\" href=\"https:\/\/www.manning.com\/\">www.manning.com<\/a>. The publisher offers discounts on these books when ordered in quantity.<\/p>\n<p class=\"body-center\">For more information, please contact<\/p>\n<p class=\"body-center\">&nbsp;&nbsp;<\/p>\n<p class=\"body-center-b\">Special Sales Department<\/p>\n<p class=\"body-center-b\">Manning Publications Co.<\/p>\n<p class=\"body-center-b\">20 Baldwin Road<\/p>\n<p class=\"body-center-b\">PO Box 761<\/p>\n<p class=\"body-center-b\">Shelter Island, NY 11964<\/p>\n<p class=\"body-center-b\">Email: <a class=\"url\" href=\"mailto:orders@manning.com\">orders@manning.com<\/a><\/p>\n<p class=\"body-center\">&nbsp;&nbsp;<\/p>\n<p class=\"copyright\">\u00a92023 Adam Freeman. All rights reserved.<\/p>\n<p class=\"body-center\">&nbsp;&nbsp;<\/p>\n<p class=\"body\">No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without prior written permission of the publisher.<\/p>\n<p class=\"body\">Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in the book, and Manning Publications was aware of a trademark claim, the designations have been printed in initial caps or all caps.<\/p>\n<p class=\"body\"><span class=\"segoe\">\u267e<\/span> Recognizing the importance of preserving what has been written, it is Manning\u2019s policy to have the books we publish printed on acid-free paper, and we exert our best efforts to that end. Recognizing also our responsibility to conserve the resources of our planet, Manning books are printed on paper that is at least 15 percent recycled and processed without the use of elemental chlorine.<\/p>\n<p class=\"body\">&nbsp;<\/p>\n<table class=\"contenttable-0-table\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"2\" width=\"50%\"><\/col>\n<\/colgroup>\n<tbody class=\"contenttable-0-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"copyrightfiguresb\"><img decoding=\"async\" alt=\"\" class=\"calibre4\" src=\"\/images\/proaspnetcore7\/000441.png\" \/>&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<\/td>\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-r\">Manning Publications Co.<\/p>\n<p class=\"contenttable-0-p-r\">20 Baldwin Road Technical<\/p>\n<p class=\"contenttable-0-p-r\">PO Box 761<\/p>\n<p class=\"contenttable-0-p-r\">Shelter Island, NY 11964<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body-center\">&nbsp;&nbsp;<\/p>\n<table class=\"contenttable-0-table\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"2\" width=\"50%\"><\/col>\n<\/colgroup>\n<tbody class=\"contenttable-0-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-l\">Development editor:&nbsp;&nbsp;<\/p>\n<\/td>\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-r\">Marina Michaels<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-l\">Technical editor:&nbsp;&nbsp;<\/p>\n<\/td>\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-r\">Fabio Ferracchiati<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-l\">Production editor:&nbsp;&nbsp;<\/p>\n<\/td>\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-r\">Aleksandar Dragosavljevi\u0107<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-l\">Copy editor:&nbsp;&nbsp;<\/p>\n<\/td>\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-r\">Katie Petito<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-l\">Typesetter:&nbsp;&nbsp;<\/p>\n<\/td>\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-r\">Tamara \u0160veli\u0107 Sablji\u0107<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-l\">Cover designer:&nbsp;&nbsp;<\/p>\n<\/td>\n<td class=\"contenttable-0-td\" colspan=\"1\" rowspan=\"1\">\n<p class=\"contenttable-0-p-r\">Marija Tudor<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body-center\">&nbsp;&nbsp;<\/p>\n<p class=\"body-center\">&nbsp;&nbsp;<\/p>\n<p class=\"body-center\">ISBN: 9781633437821<\/p>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-3\">\n<div class=\"calibre1\" id=\"calibre_link-1271\">\n<h1 class=\"tochead\" id=\"calibre_link-1272\">dedication<\/h1>\n<p class=\"fm-quote\">Dedicated to my lovely wife, Jacqui Griffyth.<\/p>\n<p class=\"fm-quote\">(And also to Peanut.)<\/p>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-4\">\n<div class=\"calibre1\" id=\"calibre_link-1273\">\n<h1 class=\"tochead\" id=\"calibre_link-1274\">contents<\/h1>\n<p class=\"fm-head-2toc\">&nbsp;&nbsp;<\/p>\n<p class=\"tocchapters\">&nbsp;&nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-5\">Front matter<\/a><\/p>\n<p class=\"fm-head-1toc\"><a class=\"url\" href=\"\/#calibre_link-6\">preface<\/a><\/p>\n<p class=\"fm-head-1toc\"><a class=\"url\" href=\"\/#calibre_link-7\">about this book<\/a><\/p>\n<p class=\"fm-head-1toc\"><a class=\"url\" href=\"\/#calibre_link-8\">about the author<\/a><\/p>\n<p class=\"fm-head-1toc\"><a class=\"url\" href=\"\/#calibre_link-9\">about the cover illustration<\/a><\/p>\n<p class=\"fm-head-2toc\">&nbsp;&nbsp;<\/p>\n<p class=\"tocchapters\">&nbsp;&nbsp;1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-10\">Putting ASP.NET Core in context<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;1.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-11\">Understanding the application frameworks<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-12\">Understanding the MVC Framework<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-13\">Understanding Razor Pages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-14\">Understanding Blazor<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-15\">Understanding the utility frameworks<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-16\">Understanding the ASP.NET Core platform<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;1.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-17\">Understanding this book<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-18\">What software do I need to follow the examples?<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-19\">What platform do I need to follow the examples?<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-20\">What if I have problems following the examples?<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-21\">What if I find an error in the book?<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-22\">What does this book cover?<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-23\">What doesn\u2019t this book cover?<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-24\">How do I contact the author?<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-25\">What if I really enjoyed this book?<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-26\">What if this book has made me angry and I want to complain?<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-27\">Summary<\/a><\/p>\n<p class=\"tocparts\"><a class=\"url\" href=\"\/#calibre_link-28\">Part 1.<\/a><\/p>\n<p class=\"tocchapters\">&nbsp;&nbsp;2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-29\">Getting started<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;2.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-30\">Choosing a code editor<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-31\">Installing Visual Studio<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-32\">Installing Visual Studio Code<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;2.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-33\">Creating an ASP.NET Core project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-34\">Opening the project using Visual Studio<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-35\">Opening the project with Visual Studio Code<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;2.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-36\">Running the ASP.NET Core application<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-37\">Understanding endpoints<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-38\">Understanding routes<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-39\">Understanding HTML rendering<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-40\">Putting the pieces together<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-41\">Summary<\/a><\/p>\n<p class=\"tocchapters\">&nbsp;&nbsp;3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-42\">Your first ASP.NET Core application<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;3.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-43\">Setting the scene<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;3.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-44\">Creating the project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-45\">Preparing the project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-46\">Adding a data model<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-47\">Creating a second action and view<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-48\">Linking action methods<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-49\">Building the form<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-50\">Receiving form data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-51\">Adding the thanks view<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-52\">Displaying responses<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-53\">Adding validation<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-54\">Styling the content<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-55\">Summary<\/a><\/p>\n<p class=\"tocchapters\">&nbsp;&nbsp;4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-56\">Using the development tools<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;4.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-57\">Creating ASP.NET Core projects<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-58\">Creating a project using the command line<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;4.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-59\">Adding code and content to projects<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-60\">Understanding item scaffolding<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;4.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-61\">Building and running projects<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-62\">Using the hot reload feature<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;4.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-63\">Managing packages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-64\">Managing NuGet packages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-65\">Managing tool packages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-66\">Managing client-side packages<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;4.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-67\">Debugging projects<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-68\">Summary<\/a><\/p>\n<p class=\"tocchapters\">&nbsp;&nbsp;5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-69\">Essential C# features<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-70\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-71\">Opening the project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-72\">Enabling the MVC Framework<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-73\">Creating the application components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-74\">Selecting the HTTP port<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-75\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-76\">Understanding top-level statements<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-77\">Understanding global using statements<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-78\">Understanding implicit using statements<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-79\">Understanding null state analysis<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-80\">Ensuring fields and properties are assigned values<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-81\">Providing a default value for non-nullable types<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-82\">Using nullable types<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-83\">Checking for null values<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-84\">Overriding null state analysis<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-85\">Disabling null state analysis warnings<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-86\">Using string interpolation<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-87\">Using object and collection initializers<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-88\">Using an index initializer<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.7 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-89\">Using target-typed new expressions<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.8 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-90\">Pattern Matching<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-91\">Pattern matching in switch statements<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.9 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-92\">Using extension methods<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-93\">Applying extension methods to an interface<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-94\">Creating filtering extension methods<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.10&nbsp;<a class=\"url\" href=\"\/#calibre_link-95\">Using lambda expressions<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-96\">Defining functions<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-97\">Using lambda expression methods and properties<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.11&nbsp;<a class=\"url\" href=\"\/#calibre_link-98\">Using type inference and anonymous types<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-99\">Using anonymous types<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.12&nbsp;<a class=\"url\" href=\"\/#calibre_link-100\">Using default implementations in interfaces<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.13&nbsp;<a class=\"url\" href=\"\/#calibre_link-101\">Using asynchronous methods<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-102\">Working with tasks directly<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-103\">Applying the async and await keywords<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-104\">Using an asynchronous enumerable<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;5.14&nbsp;<a class=\"url\" href=\"\/#calibre_link-105\">Getting names<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-106\">Summary<\/a><\/p>\n<p class=\"tocchapters\">&nbsp;&nbsp;6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-107\">Testing ASP.NET Core applications<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;6.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-108\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-109\">Opening the project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-110\">Selecting the HTTP port<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-111\">Enabling the MVC Framework<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-112\">Creating the application components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-113\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;6.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-114\">Creating a unit test project<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;6.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-115\">Writing and running unit tests<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-116\">Running tests with the Visual Studio Test Explorer<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-117\">Running tests with Visual Studio Code<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-118\">Running tests from the command line<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-119\">Correcting the unit test<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-120\">Isolating components for unit testing<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-121\">Using a mocking package<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-122\">Creating a mock object<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-123\">Summary<\/a><\/p>\n<p class=\"tocchapters\">&nbsp;&nbsp;7 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-124\">SportsStore: A real application<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;7.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-125\">Creating the projects<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-126\">Creating the unit test project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-127\">Opening the projects<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-128\">Configuring the HTTP port<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-129\">Creating the application project folders<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-130\">Preparing the services and the request pipeline<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-131\">Configuring the Razor view engine<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-132\">Creating the controller and view<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-133\">Starting the data model<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-134\">Checking and running the application<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;7.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-135\">Adding data to the application<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-136\">Installing the Entity Framework Core packages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-137\">Defining the connection string<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-138\">Creating the database context class<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-139\">Configuring Entity Framework Core<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-140\">Creating a repository<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-141\">Creating the database migration<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-142\">Creating seed data<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;7.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-143\">Displaying a list of products<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-144\">Preparing the controller<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-145\">Updating the view<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-146\">Running the application<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;7.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-147\">Adding pagination<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-148\">Displaying page links<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-149\">Improving the URLs<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;7.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-150\">Styling the content<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-151\">Installing the Bootstrap package<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-152\">Applying Bootstrap styles<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-153\">Creating a partial view<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-154\">Summary<\/a><\/p>\n<p class=\"tocchapters\">&nbsp;&nbsp;8 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-155\">SportsStore: Navigation and cart<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;8.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-156\">Adding navigation controls<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-157\">Filtering the product list<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-158\">Refining the URL scheme<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-159\">Building a category navigation menu<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-160\">Correcting the page count<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;8.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-161\">Building the shopping cart<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-162\">Configuring Razor Pages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-163\">Creating a Razor Page<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-164\">Creating the Add to Cart buttons<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-165\">Enabling sessions<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-166\">Implementing the cart feature<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-167\">Summary<\/a><\/p>\n<p class=\"tocchapters\">&nbsp;&nbsp;9 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-168\">SportsStore: Completing the cart<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;9.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-169\">Refining the cart model with a service<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-170\">Creating a storage-aware cart class<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-171\">Registering the service<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-172\">Simplifying the cart Razor Page<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;9.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-173\">Completing the cart functionality<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-174\">Removing items from the cart<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-175\">Adding the cart summary widget<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp;&nbsp;9.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-176\">Submitting orders<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-177\">Creating the model class<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-178\">Adding the checkout process<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-179\">Creating the controller and view<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-180\">Implementing order processing<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-181\">Completing the order controller<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-182\">Displaying validation errors<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-183\">Displaying a summary page<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-184\">Summary<\/a><\/p>\n<p class=\"tocchapters\">10 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-185\">SportsStore: Administration<\/a><\/p>\n<p class=\"fm-head-1toc\">10.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-186\">Preparing Blazor Server<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-187\">Creating the imports file<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-188\">Creating the startup Razor Page<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-189\">Creating the routing and layout components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-190\">Creating the Razor Components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-191\">Checking the Blazor setup<\/a><\/p>\n<p class=\"fm-head-1toc\">10.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-192\">Managing orders<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-193\">Enhancing the model<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-194\">Displaying orders to the administrator<\/a><\/p>\n<p class=\"fm-head-1toc\">10.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-195\">Adding catalog management<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-196\">Expanding the repository<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-197\">Applying validation attributes to the data model<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-198\">Creating the list component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-199\">Creating the detail component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-200\">Creating the editor component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-201\">Deleting products<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-202\">Summary<\/a><\/p>\n<p class=\"tocchapters\">11 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-203\">SportsStore: Security and deployment<\/a><\/p>\n<p class=\"fm-head-1toc\">11.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-204\">Creating the Identity database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-205\">Installing the Identity package for Entity Framework Core<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-206\">Creating the context class<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-207\">Defining the connection string<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-208\">Configuring the application<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-209\">Creating and applying the database migration<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-210\">Defining the seed data<\/a><\/p>\n<p class=\"fm-head-1toc\">11.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-211\">Adding a conventional administration feature<\/a><\/p>\n<p class=\"fm-head-1toc\">11.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-212\">Applying a basic authorization policy<\/a><\/p>\n<p class=\"fm-head-1toc\">11.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-213\">Creating the account controller and views<\/a><\/p>\n<p class=\"fm-head-1toc\">11.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-214\">Testing the security policy<\/a><\/p>\n<p class=\"fm-head-1toc\">11.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-215\">Preparing ASP.NET Core for deployment<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-216\">Configuring error handling<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-217\">Creating the production configuration settings<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-218\">Creating the Docker image<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-219\">Running the containerized application<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-220\">Summary<\/a><\/p>\n<p class=\"tocparts\"><a class=\"url\" href=\"\/#calibre_link-221\">Part 2.<\/a><\/p>\n<p class=\"tocchapters\">12 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-222\">Understanding the ASP.NET Core platform<\/a><\/p>\n<p class=\"fm-head-1toc\">12.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-223\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-224\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">12.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-225\">Understanding the ASP.NET Core platform<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-226\">Understanding middleware and the request pipeline<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-227\">Understanding services<\/a><\/p>\n<p class=\"fm-head-1toc\">12.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-228\">Understanding the ASP.NET Core project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-229\">Understanding the entry point<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-230\">Understanding the project file<\/a><\/p>\n<p class=\"fm-head-1toc\">12.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-231\">Creating custom middleware<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-232\">Defining middleware using a class<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-233\">Understanding the return pipeline path<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-234\">Short-Circuiting the request pipeline<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-235\">Creating pipeline branches<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-236\">Creating terminal middleware<\/a><\/p>\n<p class=\"fm-head-1toc\">12.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-237\">Configuring middleware<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-238\">Using the options pattern with class-based middleware<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-239\">Summary<\/a><\/p>\n<p class=\"tocchapters\">13 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-240\">Using URL routing<\/a><\/p>\n<p class=\"fm-head-1toc\">13.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-223\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-241\">Understanding URL routing<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-242\">Adding the routing middleware and defining an endpoint<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-243\">Simplifying the pipeline configuration<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-244\">Understanding URL patterns<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-245\">Using segment variables in URL patterns<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-246\">Generating URLs from routes<\/a><\/p>\n<p class=\"fm-head-1toc\">13.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-247\">Managing URL matching<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-248\">Matching multiple values from a single URL segment<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-249\">Using default values for segment variables<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-250\">Using optional segments in a URL Pattern<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-251\">Using a catchall segment variable<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-252\">Constraining segment matching<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-253\">Defining fallback routes<\/a><\/p>\n<p class=\"fm-head-1toc\"><a class=\"url\" href=\"\/#calibre_link-254\">13.3 &nbsp;Advanced routing features<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-255\">Creating custom constraints<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-256\">Avoiding ambiguous route exceptions<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-257\">Accessing the endpoint in a middleware component<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-258\">Summary<\/a><\/p>\n<p class=\"tocchapters\">14 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-259\">Using dependency injection<\/a><\/p>\n<p class=\"fm-head-1toc\">14.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-260\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-261\">Creating a middleware component and an endpoint<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-262\">Configuring the request pipeline<\/a><\/p>\n<p class=\"fm-head-1toc\">14.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-263\">Understanding service location and tight coupling<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-264\">Understanding the service location problem<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-265\">Understanding the tightly coupled components problem<\/a><\/p>\n<p class=\"fm-head-1toc\">14.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-266\">Using dependency injection<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-267\">Using a Service with a Constructor Dependency<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-268\">Getting services from the HttpContext object<\/a><\/p>\n<p class=\"fm-head-1toc\">14.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-269\">Using Service Lifecycles<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-270\">Creating transient services<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-271\">Avoiding the transient service reuse pitfall<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-272\">Using scoped services<\/a><\/p>\n<p class=\"fm-head-1toc\">14.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-273\">Other dependency injection features<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-274\">Creating dependency chains<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-275\">Accessing services in the Program.cs file<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-276\">Using service factory functions<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-277\">Creating services with multiple implementations<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-278\">Using unbound types in services<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-279\">Summary<\/a><\/p>\n<p class=\"tocchapters\">15 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-280\">Using the platform features, part 1<\/a><\/p>\n<p class=\"fm-head-1toc\">15.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-281\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-1toc\">15.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-282\">Using the configuration service<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-283\">Understanding the environment configuration file<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-284\">Accessing configuration settings<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-285\">Using the configuration data in the Program.cs file<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-286\">Using configuration data with the options pattern<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-287\">Understanding the launch settings file<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-288\">Using the environment service<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-289\">Storing user secrets<\/a><\/p>\n<p class=\"fm-head-1toc\">15.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-290\">Using the logging service<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-291\">Generating logging messages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-292\">Logging messages with attributes<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-293\">Configuring minimum logging levels<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-294\">Logging HTTP requests and responses<\/a><\/p>\n<p class=\"fm-head-1toc\">15.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-295\">Using static content and client-side packages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-296\">Adding the static content middleware<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-297\">Using client-side packages<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-298\">Summary<\/a><\/p>\n<p class=\"tocchapters\">16 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-299\">Using the platform features, part 2<\/a><\/p>\n<p class=\"fm-head-1toc\">16.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-300\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-1toc\">16.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-301\">Using cookies<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-302\">Enabling cookie consent checking<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-303\">Managing cookie consent<\/a><\/p>\n<p class=\"fm-head-1toc\">16.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-304\">Using sessions<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-305\">Configuring the session service and middleware<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-306\">Using session data<\/a><\/p>\n<p class=\"fm-head-1toc\">16.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-307\">Working with HTTPS connections<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-308\">Enabling HTTPS connections<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-309\">Detecting HTTPS requests<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-310\">Enforcing HTTPS requests<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-311\">Enabling HTTP strict transport security<\/a><\/p>\n<p class=\"fm-head-1toc\">16.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-312\">Using rate limits<\/a><\/p>\n<p class=\"fm-head-1toc\">16.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-313\">Handling exceptions and errors<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-314\">Returning an HTML error response<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-315\">Enriching status code responses<\/a><\/p>\n<p class=\"fm-head-1toc\">16.7 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-316\">Filtering requests using the host header<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-317\">Summary<\/a><\/p>\n<p class=\"tocchapters\">17 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-318\">Working with data<\/a><\/p>\n<p class=\"fm-head-1toc\">17.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-319\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-1toc\">17.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-320\">Caching data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-321\">Caching data values<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-322\">Using a shared and persistent data cache<\/a><\/p>\n<p class=\"fm-head-1toc\">17.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-323\">Caching responses<\/a><\/p>\n<p class=\"fm-head-1toc\">17.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-324\">Caching output<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-325\">Defining a custom cache policy<\/a><\/p>\n<p class=\"fm-head-1toc\">17.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-326\">Using Entity Framework Core<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-327\">Installing Entity Framework Core<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-328\">Creating the data model<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-329\">Configuring the database service<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-330\">Creating and applying the database migration<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-331\">Seeding the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-332\">Using data in an endpoint<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-333\">Summary<\/a><\/p>\n<p class=\"tocparts\"><a class=\"url\" href=\"\/#calibre_link-334\">Part 3.<\/a><\/p>\n<p class=\"tocchapters\">18 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-335\">Creating the example project<\/a><\/p>\n<p class=\"fm-head-1toc\">18.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-336\">Creating the project<\/a><\/p>\n<p class=\"fm-head-1toc\">18.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-337\">Adding a data model<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-338\">Adding NuGet packages to the project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-339\">Creating the data model<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-340\">Preparing the seed data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-341\">Configuring EF Core services and middleware<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-342\">Creating and applying the migration<\/a><\/p>\n<p class=\"fm-head-1toc\">18.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-343\">Adding the CSS framework<\/a><\/p>\n<p class=\"fm-head-1toc\">18.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-344\">Configuring the request pipeline<\/a><\/p>\n<p class=\"fm-head-1toc\">18.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-345\">Running the example application<\/a><\/p>\n<p class=\"tocchapters\">19 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-346\">Creating RESTful web services<\/a><\/p>\n<p class=\"fm-head-1toc\">19.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-347\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-1toc\">19.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-348\">Understanding RESTful web services<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-349\">Understanding request URLs and methods<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-350\">Understanding JSON<\/a><\/p>\n<p class=\"fm-head-1toc\">19.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-351\">Creating a web service using the minimal API<\/a><\/p>\n<p class=\"fm-head-1toc\">19.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-352\">Creating a web service using a controller<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-353\">Enabling the MVC Framework<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-354\">Creating a controller<\/a><\/p>\n<p class=\"fm-head-1toc\">19.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-355\">Improving the web service<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-356\">Using asynchronous actions<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-357\">Preventing over-binding<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-358\">Using action results<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-359\">Validating data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-360\">Applying the API controller attribute<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-361\">Omitting Null properties<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-362\">Applying a rate limit<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-363\">Summary<\/a><\/p>\n<p class=\"tocchapters\">20 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-364\">Advanced web service features<\/a><\/p>\n<p class=\"fm-head-1toc\">20.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-365\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-366\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-367\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">20.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-368\">Dealing with related data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-369\">Breaking circular references in related data<\/a><\/p>\n<p class=\"fm-head-1toc\">20.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-370\">Supporting the HTTP PATCH method<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-371\">Understanding JSON Patch<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-372\">Installing and configuring the JSON Patch package<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-373\">Defining the action method<\/a><\/p>\n<p class=\"fm-head-1toc\">20.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-374\">Understanding content formatting<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-375\">Understanding the default content policy<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-376\">Understanding content negotiation<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-377\">Specifying an action result format<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-378\">Requesting a format in the URL<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-379\">Restricting the formats received by an action method<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-380\">Caching output<\/a><\/p>\n<p class=\"fm-head-1toc\">20.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-381\">Documenting and exploring web services<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-382\">Resolving action conflicts<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-383\">Installing and configuring the Swashbuckle package<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-384\">Fine-Tuning the API description<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-385\">Summary<\/a><\/p>\n<p class=\"tocchapters\">21 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-386\">Using controllers with views, part I<\/a><\/p>\n<p class=\"fm-head-1toc\">21.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-387\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-388\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-389\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">21.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-390\">Getting started with views<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-391\">Configuring the application<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-392\">Creating an HTML controller<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-393\">Creating a Razor View<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-394\">Selecting a View by name<\/a><\/p>\n<p class=\"fm-head-1toc\">21.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-395\">Working with Razor Views<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-396\">Setting the view model type<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-397\">Understanding the view model type pitfall<\/a><\/p>\n<p class=\"fm-head-1toc\">21.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-398\">Understanding the Razor syntax<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-399\">Understanding directives<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-400\">Understanding content expressions<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-401\">Setting element content<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-402\">Setting attribute values<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-403\">Using conditional expressions<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-404\">Enumerating sequences<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-405\">Using Razor code blocks<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-406\">Summary<\/a><\/p>\n<p class=\"tocchapters\">22 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-407\">Using controllers with views, part II<\/a><\/p>\n<p class=\"fm-head-1toc\">22.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-408\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-409\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-410\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">22.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-411\">Using the view bag<\/a><\/p>\n<p class=\"fm-head-1toc\">22.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-412\">Using temp data<\/a><\/p>\n<p class=\"fm-head-1toc\">22.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-413\">Working with layouts<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-414\">Configuring layouts using the view bag<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-415\">Using a view start file<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-416\">Overriding the default layout<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-417\">Using layout sections<\/a><\/p>\n<p class=\"fm-head-1toc\">22.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-418\">Using partial views<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-419\">Enabling partial views<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-420\">Creating a partial view<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-421\">Applying a partial view<\/a><\/p>\n<p class=\"fm-head-1toc\">22.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-422\">Understanding content-encoding<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-423\">Understanding HTML encoding<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-424\">Understanding JSON encoding<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-425\">Summary<\/a><\/p>\n<p class=\"tocchapters\">23 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-426\">Using Razor Pages<\/a><\/p>\n<p class=\"fm-head-1toc\">23.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-427\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-428\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">23.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-429\">Understanding Razor Pages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-430\">Configuring Razor Pages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-431\">Creating a Razor Page<\/a><\/p>\n<p class=\"fm-head-1toc\">23.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-432\">Understanding Razor Pages routing<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-433\">Specifying a routing pattern in a Razor Page<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-434\">Adding routes for a Razor Page<\/a><\/p>\n<p class=\"fm-head-1toc\">23.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-435\">Understanding the Page model class<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-436\">Using a code-behind class file<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-437\">Understanding action results in Razor Pages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-438\">Handling multiple HTTP methods<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-439\">Selecting a handler method<\/a><\/p>\n<p class=\"fm-head-1toc\">23.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-440\">Understanding the Razor Page view<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-441\">Creating a layout for Razor Pages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-442\">Using partial views in Razor Pages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-443\">Creating Razor Pages without page models<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-444\">Summary<\/a><\/p>\n<p class=\"tocchapters\">24 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-445\">Using view components<\/a><\/p>\n<p class=\"fm-head-1toc\">24.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-446\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-447\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-448\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">24.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-449\">Understanding view components<\/a><\/p>\n<p class=\"fm-head-1toc\">24.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-450\">Creating and using a view component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-451\">Applying a view component<\/a><\/p>\n<p class=\"fm-head-1toc\">24.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-452\">Understanding view component results<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-453\">Returning a partial view<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-454\">Returning HTML fragments<\/a><\/p>\n<p class=\"fm-head-1toc\">24.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-455\">Getting context data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-456\">Providing context from the parent view using arguments<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-457\">Creating asynchronous view components<\/a><\/p>\n<p class=\"fm-head-1toc\">24.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-458\">Creating view components classes<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-459\">Creating a hybrid controller class<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-460\">Summary<\/a><\/p>\n<p class=\"tocchapters\">25 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-461\">Using tag helpers<\/a><\/p>\n<p class=\"fm-head-1toc\">25.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-462\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-463\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-464\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">25.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-465\">Creating a tag helper<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-466\">Defining the tag helper class<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-467\">Registering tag helpers<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-468\">Using a tag helper<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-469\">Narrowing the scope of a tag helper<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-470\">Widening the scope of a tag helper<\/a><\/p>\n<p class=\"fm-head-1toc\">25.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-471\">Advanced tag helper features<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-472\">Creating shorthand elements<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-473\">Creating elements programmatically<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-474\">Prepending and appending content and elements<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-475\">Getting view context data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-476\">Working with model expressions<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-477\">Coordinating between tag helpers<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-478\">Suppressing the output element<\/a><\/p>\n<p class=\"fm-head-1toc\">25.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-479\">Using tag helper components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-480\">Creating a tag helper component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-481\">Expanding tag helper component element selection<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-482\">Summary<\/a><\/p>\n<p class=\"tocchapters\">26 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-483\">Using the built-in tag helpers<\/a><\/p>\n<p class=\"fm-head-1toc\">26.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-484\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-485\">Adding an image file<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-486\">Installing a client-side package<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-487\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-488\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">26.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-489\">Enabling the built-in tag helpers<\/a><\/p>\n<p class=\"fm-head-1toc\">26.3 &nbsp;<a class=\"url\" href=\"\/#calibre_link-490\">Transforming anchor elements<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-491\">Using anchor elements for Razor Pages<\/a><\/p>\n<p class=\"fm-head-1toc\">26.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-492\">Using the JavaScript and CSS tag helpers<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-493\">Managing JavaScript files<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-494\">Managing CSS stylesheets<\/a><\/p>\n<p class=\"fm-head-1toc\">26.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-495\">Working with image elements<\/a><\/p>\n<p class=\"fm-head-1toc\">26.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-496\">Using the data cache<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-497\">Setting cache expiry<\/a><\/p>\n<p class=\"fm-head-1toc\">26.7 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-498\">Using the hosting environment tag helper<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-499\">Summary<\/a><\/p>\n<p class=\"tocchapters\">27 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-500\">Using the forms tag helpers<\/a><\/p>\n<p class=\"fm-head-1toc\">27.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-501\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-502\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-503\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">27.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-504\">Understanding the form handling pattern<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-505\">Creating a controller to handle forms<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-506\">Creating a Razor Page to handle forms<\/a><\/p>\n<p class=\"fm-head-1toc\">27.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-507\">Using tag helpers to improve HTML forms<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-508\">Working with form elements<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-509\">Transforming form buttons<\/a><\/p>\n<p class=\"fm-head-1toc\">27.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-510\">Working with input elements<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-511\">Transforming the input element type attribute<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-512\">Formatting input element values<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-513\">Displaying values from related data in input elements<\/a><\/p>\n<p class=\"fm-head-1toc\">27.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-514\">Working with label elements<\/a><\/p>\n<p class=\"fm-head-1toc\">27.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-515\">Working with select and option elements<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-516\">Populating a select element<\/a><\/p>\n<p class=\"fm-head-1toc\">27.7 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-517\">Working with text areas<\/a><\/p>\n<p class=\"fm-head-1toc\">27.8 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-518\">Using the anti-forgery feature<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-519\">Enabling the anti-forgery feature in a controller<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-520\">Enabling the anti-forgery feature in a Razor Page<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-521\">Using anti-forgery tokens with JavaScript clients<\/a><\/p>\n<p class=\"tocchapters\">28 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-522\">Using model binding<\/a><\/p>\n<p class=\"fm-head-1toc\">28.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-523\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-524\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-525\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">28.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-526\">Understanding model binding<\/a><\/p>\n<p class=\"fm-head-1toc\">28.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-527\">Binding simple data types<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-528\">Binding simple data types in Razor Pages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-529\">Understanding default binding values<\/a><\/p>\n<p class=\"fm-head-1toc\">28.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-530\">Binding complex types<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-531\">Binding to a property<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-532\">Binding nested complex types<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-533\">Selectively binding properties<\/a><\/p>\n<p class=\"fm-head-1toc\">28.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-534\">Binding to arrays and collections<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-535\">Binding to arrays<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-536\">Binding to simple collections<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-537\">Binding to dictionaries<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-538\">Binding to collections of complex types<\/a><\/p>\n<p class=\"fm-head-1toc\">28.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-539\">Specifying a model binding source<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-540\">Selecting a binding source for a property<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-541\">Using headers for model binding<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-542\">Using request bodies as binding sources<\/a><\/p>\n<p class=\"fm-head-1toc\">28.7 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-543\">Manual model binding<\/a><\/p>\n<p class=\"tocchapters\">29 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-544\">Using model validation<\/a><\/p>\n<p class=\"fm-head-1toc\">29.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-545\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-546\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-547\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">29.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-548\">Understanding the need for model validation<\/a><\/p>\n<p class=\"fm-head-1toc\">29.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-549\">Validating data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-550\">Displaying validation messages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-551\">Understanding the implicit validation checks<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-552\">Performing explicit validation<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-553\">Configuring the default validation error messages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-554\">Displaying property-level validation messages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-555\">Displaying model-level messages<\/a><\/p>\n<p class=\"fm-head-1toc\">29.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-556\">Explicitly validating data in a Razor Page<\/a><\/p>\n<p class=\"fm-head-1toc\">29.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-557\">Specifying validation rules using metadata<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-558\">Creating a custom property validation attribute<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-559\">Creating a custom model validation attribute<\/a><\/p>\n<p class=\"fm-head-1toc\">29.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-560\">Performing client-side validation<\/a><\/p>\n<p class=\"fm-head-1toc\">29.7 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-561\">Performing remote validation<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-562\">Performing remote validation in Razor Pages<\/a><\/p>\n<p class=\"tocchapters\">30 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-563\">Using filters<\/a><\/p>\n<p class=\"fm-head-1toc\">30.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-564\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-565\">Enabling HTTPS Connections<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-566\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-567\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">30.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-568\">Using filters<\/a><\/p>\n<p class=\"fm-head-1toc\">30.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-569\">Understanding filters<\/a><\/p>\n<p class=\"fm-head-1toc\">30.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-570\">Creating custom filters<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-571\">Understanding authorization filters<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-572\">Understanding resource filters<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-573\">Understanding action filters<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-574\">Understanding page filters<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-575\">Understanding result filters<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-576\">Understanding exception filters<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-577\">Creating an exception filter<\/a><\/p>\n<p class=\"fm-head-1toc\">30.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-578\">Managing the filter lifecycle<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-579\">Creating filter factories<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-580\">Using dependency injection scopes to manage filter lifecycles<\/a><\/p>\n<p class=\"fm-head-1toc\">30.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-581\">Creating global filters<\/a><\/p>\n<p class=\"fm-head-1toc\">30.7 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-582\">Understanding and changing filter order<\/a><\/p>\n<p class=\"tocchapters\">31 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-583\">Creating form applications<\/a><\/p>\n<p class=\"fm-head-1toc\">31.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-584\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-585\">Dropping the database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-586\">Running the example application<\/a><\/p>\n<p class=\"fm-head-1toc\">31.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-587\">Creating an MVC forms application<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-588\">Preparing the view model and the view<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-589\">Reading data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-590\">Creating data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-591\">Editing data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-592\">Deleting data<\/a><\/p>\n<p class=\"fm-head-1toc\">31.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-593\">Creating a Razor Pages forms application<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-594\">Creating common functionality<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-595\">Defining pages for the CRUD operations<\/a><\/p>\n<p class=\"fm-head-1toc\">31.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-596\">Creating new related data objects<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-597\">Providing the related data in the same request<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-598\">Breaking out to create new data<\/a><\/p>\n<p class=\"tocparts\"><a class=\"url\" href=\"\/#calibre_link-599\">Part 4.<\/a><\/p>\n<p class=\"tocchapters\">32 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-600\">Creating the example project<\/a><\/p>\n<p class=\"fm-head-1toc\">32.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-601\">Creating the project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-602\">Adding NuGet packages to the project<\/a><\/p>\n<p class=\"fm-head-1toc\">32.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-603\">Adding a data model<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-604\">Preparing the seed data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-605\">Configuring Entity Framework Core<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-606\">Creating and applying the migration<\/a><\/p>\n<p class=\"fm-head-1toc\">32.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-607\">Adding the Bootstrap CSS framework<\/a><\/p>\n<p class=\"fm-head-1toc\">32.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-608\">Configuring the services and middleware<\/a><\/p>\n<p class=\"fm-head-1toc\">32.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-609\">Creating a controller and view<\/a><\/p>\n<p class=\"fm-head-1toc\">32.6 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-610\">Creating a Razor Page<\/a><\/p>\n<p class=\"fm-head-1toc\">32.7 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-611\">Running the example application<\/a><\/p>\n<p class=\"tocchapters\">33 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-612\">Using Blazor Server, part 1<\/a><\/p>\n<p class=\"fm-head-1toc\">33.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-613\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-1toc\">33.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-614\">Understanding Blazor Server<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-615\">Understanding the Blazor Server advantages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-616\">Understanding the Blazor Server disadvantages<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-617\">Choosing between Blazor Server and Angular\/React\/Vue.js<\/a><\/p>\n<p class=\"fm-head-1toc\">33.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-618\">Getting started with Blazor<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-619\">Configuring ASP.NET Core for Blazor Server<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-620\">Creating a Razor Component<\/a><\/p>\n<p class=\"fm-head-1toc\">33.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-621\">Understanding the basic Razor Component features<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-622\">Understanding Blazor events and data bindings<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-623\">Working with data bindings<\/a><\/p>\n<p class=\"fm-head-1toc\">33.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-624\">Using class files to define components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-625\">Using a code-behind class<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-626\">Defining a Razor Component class<\/a><\/p>\n<p class=\"tocchapters\">34 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-627\">Using Blazor Server, part 2<\/a><\/p>\n<p class=\"fm-head-1toc\">34.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-628\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-1toc\">34.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-629\">Combining components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-630\">Configuring components with attributes<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-631\">Creating custom events and bindings<\/a><\/p>\n<p class=\"fm-head-1toc\">34.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-632\">Displaying child content in a component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-633\">Creating template components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-634\">Using generic type parameters in template components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-635\">Cascading parameters<\/a><\/p>\n<p class=\"fm-head-1toc\">34.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-636\">Handling errors<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-637\">Handling connection errors<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-638\">Handling uncaught application errors<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-639\">Using error boundaries<\/a><\/p>\n<p class=\"tocchapters\">35 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-640\">Advanced Blazor features<\/a><\/p>\n<p class=\"fm-head-1toc\">35.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-641\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-1toc\">35.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-642\">Using component routing<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-643\">Preparing the Razor Page<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-644\">Adding routes to components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-645\">Navigating between routed components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-646\">Receiving routing data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-647\">Defining common content using layouts<\/a><\/p>\n<p class=\"fm-head-1toc\">35.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-648\">Understanding the component lifecycle methods<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-649\">Using the lifecycle methods for asynchronous tasks<\/a><\/p>\n<p class=\"fm-head-1toc\">35.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-650\">Managing component interaction<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-651\">Using references to child components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-652\">Interacting with components from other code<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-653\">Interacting with components using JavaScript<\/a><\/p>\n<p class=\"tocchapters\">36 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-654\">Blazor forms and data<\/a><\/p>\n<p class=\"fm-head-1toc\">36.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-655\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-656\">Dropping the database and running the application<\/a><\/p>\n<p class=\"fm-head-1toc\">36.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-657\">Using the Blazor form components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-658\">Creating custom form components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-659\">Validating form data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-660\">Handling form events<\/a><\/p>\n<p class=\"fm-head-1toc\">36.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-661\">Using Entity Framework Core with Blazor<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-662\">Understanding the EF Core context scope issue<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-663\">Understanding the repeated query issue<\/a><\/p>\n<p class=\"fm-head-1toc\">36.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-664\">Performing CRUD operations<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-665\">Creating the list component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-666\">Creating the details component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-667\">Creating the editor component<\/a><\/p>\n<p class=\"fm-head-1toc\">36.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-668\">Extending the Blazor form features<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-669\">Creating a custom validation constraint<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-670\">Creating a valid-only submit button component<\/a><\/p>\n<p class=\"tocchapters\">37 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-671\">Using Blazor WebAssembly<\/a><\/p>\n<p class=\"fm-head-1toc\">37.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-672\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-673\">Dropping the database and running the application<\/a><\/p>\n<p class=\"fm-head-1toc\">37.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-674\">Setting Up Blazor WebAssembly<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-675\">Creating the shared project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-676\">Creating the Blazor WebAssembly project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-677\">Preparing the ASP.NET Core project<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-678\">Adding the solution references<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-679\">Opening the projects<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-680\">Completing the Blazor WebAssembly configuration<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-681\">Testing the placeholder components<\/a><\/p>\n<p class=\"fm-head-1toc\">37.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-682\">Creating a Blazor WebAssembly component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-683\">Importing the data model namespace<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-684\">Creating a component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-685\">Creating a layout<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-686\">Defining CSS styles<\/a><\/p>\n<p class=\"fm-head-1toc\">37.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-687\">Completing the Blazor WebAssembly Form application<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-688\">Creating the details component<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-689\">Creating the editor component<\/a><\/p>\n<p class=\"tocchapters\">38 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-690\">Using ASP.NET Core Identity<\/a><\/p>\n<p class=\"fm-head-1toc\">38.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-691\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-1toc\">38.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-692\">Preparing the project for ASP.NET Core Identity<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-693\">Preparing the ASP.NET Core Identity database<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-694\">Configuring the application<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-695\">Creating and applying the Identity database migration<\/a><\/p>\n<p class=\"fm-head-1toc\">38.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-696\">Creating user management tools<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-697\">Preparing for user management tools<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-698\">Enumerating user accounts<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-699\">Creating users<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-700\">Editing users<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-701\">Deleting users<\/a><\/p>\n<p class=\"fm-head-1toc\">38.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-702\">Creating role management tools<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-703\">Preparing for role management tools<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-704\">Enumerating and deleting roles<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-705\">Creating roles<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-706\">Assigning role membership<\/a><\/p>\n<p class=\"tocchapters\">39 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-707\">Applying ASP.NET Core Identity<\/a><\/p>\n<p class=\"fm-head-1toc\">39.1 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-708\">Preparing for this chapter<\/a><\/p>\n<p class=\"fm-head-1toc\">39.2 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-709\">Authenticating users<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-710\">Creating the login feature<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-711\">Inspecting the ASP.NET Core Identity cookie<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-712\">Creating a Sign-Out page<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-713\">Testing the authentication feature<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-714\">Enabling the Identity authentication middleware<\/a><\/p>\n<p class=\"fm-head-1toc\">39.3 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-715\">Authorizing access to endpoints<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-716\">Applying the authorization attribute<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-717\">Enabling the authorization middleware<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-718\">Creating the access denied endpoint<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-719\">Creating the seed data<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-720\">Testing the authentication sequence<\/a><\/p>\n<p class=\"fm-head-1toc\">39.4 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-721\">Authorizing access to Blazor applications<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-722\">Performing authorization in Blazor components<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-723\">Displaying content to authorized users<\/a><\/p>\n<p class=\"fm-head-1toc\">39.5 &nbsp;&nbsp;<a class=\"url\" href=\"\/#calibre_link-724\">Authenticating and authorizing web services<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-725\">Building a simple JavaScript client<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-726\">Restricting access to the web service<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-727\">Using cookie authentication<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-728\">Using bearer token authentication<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-729\">Creating tokens<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-730\">Authenticating with tokens<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-731\">Restricting access with tokens<\/a><\/p>\n<p class=\"fm-head-2toc\"><a class=\"url\" href=\"\/#calibre_link-732\">Using tokens to request data<\/a><\/p>\n<p class=\"fm-head-1toc\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a class=\"url\" href=\"\/#calibre_link-733\">index<\/a><\/p>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-5\">\n<div class=\"calibre1\" id=\"calibre_link-1275\">\n<h1 class=\"tochead\" id=\"calibre_link-1276\">front matter<\/h1>\n<h2 class=\"fm-head\" id=\"calibre_link-6\">preface<\/h2>\n<p class=\"body\">This is the 49<sup class=\"fm-superscript\">th<\/sup> book I have written. I wrote my first book in 1996, and I would not have believed anyone who told me that I would still be writing over a quarter of a century later, or that books would become such an important part of my life.<\/p>\n<p class=\"body\">I have a bookshelf on which I keep every book I have written. It is an act of pure self-indulgence, but I am proud of these books and what they represent. They span 2.5 meters on a single shelf (or 8 feet if you prefer) and they mark the chapters of my life: the book I wrote the year I married my beloved wife; the book I was writing when my father died; the book I finished while we moved house; the book I wrote after I retired. Each book reminds me of people and places going back 27 years.<\/p>\n<p class=\"body\">Of all the books I have written, Pro ASP.NET Core is my favourite. This is the 10<sup class=\"fm-superscript\">th<\/sup> edition, but I almost didn\u2019t write it at all. I had already written a book about ASP.NET Web Forms and found it to be a frustrating process, so I wasn\u2019t keen to write about the MVC framework and Microsoft\u2019s attempt to modernize their web development products. My wife persuaded me to accept the publisher\u2019s offer and I have never looked back. ASP.NET has evolved into ASP.NET Core, and each edition of this book has been a little bigger and a little more detailed.<\/p>\n<p class=\"body\">This is a big and complicated book because ASP.NET Core is big and complicated. But I put a lot of effort into writing books that are easy to follow, even if the topics can be difficult to understand. As I write this preface and I think of you, my future reader, my hope is that the book you hold in your hand helps you with your career, makes your project easier to implement, or helps you move into a new and more exciting role.<\/p>\n<p class=\"body\">There is something unique about receiving the first copies of a book, fresh from the printers. The process of getting a book into print takes just enough time for it to be a surprise when the box arrives at the door. Writing is an abstract process and writing about software especially so. The finished book feels like an idea made real. These days, ebooks are more popular and more convenient, but my heart will always beat with joy for the printed version. As you hold this book, I hope you feel some of that joy, and that this book plays some small part in helping you achieve something you will be proud of, whatever that may be.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-7\">about this book<\/h2>\n<p class=\"body\"><i class=\"fm-italics\">Pro ASP.NET Core, Tenth Edition<\/i> was written to help you build web applications using the latest version of .NET and ASP.NET Core. It begins with setting up the development environment and creating a simple web application, before moving on to creating a simple but realistic online store, and then diving into the detail of important ASP.NET Core features.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-8\">Who should read this book<\/h3>\n<p class=\"body\">This book is for experienced developers who are new to ASP.NET Core, or who are moving from an earlier version of ASP.NET, including legacy Web Forms.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-9\">How this book is organized: a roadmap<\/h3>\n<p class=\"body\">The book has four parts. The first part covers setting up the development environment, creating a simple web application, and using the development tools. There is also a primer on important C# features for readers who are moving from an earlier version of ASP.NET or ASP.NET Core. The rest of this part of the book contains the SportsStore example application, which shows how to create a basic but functional online store, and demonstrates how the many different ASP.NET Core features work together.<\/p>\n<p class=\"body\">The second part of the book describes the key features of the ASP.NET Core platform. I explain how HTTP requests are processed, how to create and use middleware components, how to create routes, how to define and consume services, and how to work with Entity Framework Core. These chapters explain the foundations of ASP.NET Core, and understanding them is essential for effective ASP.NET Core development.<\/p>\n<p class=\"body\">The third part of the book focuses on the ASP.NET features you will need every day, including HTTP request handling, creating RESTful web services, generating HTML responses, and receiving data from users.<\/p>\n<p class=\"body\">The final part of this book describes advanced ASP.NET Core features, including using Blazor to create rich client-side applications, and using ASP.NET Core Identity to authenticate users.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-1277\">About the code<\/h3>\n<p class=\"body\">This book contains many examples of source code both in numbered listings and in line with normal text. In both cases, the source code is formatted in a fixed-width font to separate it from ordinary text. Code is also in bold to highlight statements that have changed from previous listings.<\/p>\n<p class=\"body\">The source code for every chapter in this book is available at <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-1278\">liveBook discussion forum<\/h3>\n<p class=\"body\">Purchase of <i class=\"fm-italics\">Pro ASP.NET Core 7, Tenth Edition<\/i> includes free access to liveBook, Manning\u2019s online reading platform. Using liveBook\u2019s exclusive discussion features, you can attach comments to the book globally or to specific sections or paragraphs. It\u2019s a snap to make notes for yourself, ask and answer technical questions, and receive help from the author and other users. <a id=\"calibre_link-1279\"><\/a>To access the forum, go to <a class=\"url\" href=\"https:\/\/livebook.manning.com\/book\/pro-aspdotnet-core-7-tenth-edition\/discussion\">https:\/\/livebook.manning.com\/book\/pro-aspdotnet-core-7-tenth-edition\/discussion<\/a>. You can also learn more about Manning\u2019s forums and the rules of conduct at <a class=\"url\" href=\"https:\/\/livebook.manning.com\/discussion\">https:\/\/livebook.manning.com\/discussion<\/a>.<\/p>\n<p class=\"body\">Manning\u2019s commitment to our readers is to provide a venue where a meaningful dialogue between individual readers and between readers and the author can take place. It is not a commitment to any specific amount of participation on the part of the author, whose contribution to the forum remains voluntary (and unpaid). We suggest you try asking the author some challenging questions lest his interest stray! The forum and the archives of previous discussions will be accessible from the publisher\u2019s website as long as the book is in print.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-1280\">about the author<\/h2>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre5\" src=\"\/images\/proaspnetcore7\/000443.png\" \/><\/p>\n<\/div>\n<p class=\"body\"><span class=\"mc-small-caps\">Adam Freeman<\/span> is an experienced IT professional who started his career as a programmer. He has held senior positions in a range of companies, most recently serving as Chief Technology Officer and Chief Operating Officer of a global bank. He has written 49 programming books, focusing mostly on web application development. Now retired, he spends his time writing and trying to make furniture.<\/p>\n<p class=\"fm-head2\">About the technical editor<\/p>\n<p class=\"body\">Fabio Claudio Ferracchiati is a senior consultant and a senior analyst\/developer using Microsoft technologies. He works for TIM (<a class=\"url\" href=\"https:\/\/www.telecomitalia.it\">www.telecomitalia.it<\/a>). He is a Microsoft Certified Solution Developer for .NET, a Microsoft Certified Application Developer for .NET, a Microsoft Certified Professional, and a prolific author and technical reviewer. Over the past ten years, he\u2019s written articles for Italian and international magazines and coauthored more than ten books on a variety of computer topics.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-1281\">about the cover illustration<\/h2>\n<p class=\"body\">The figure on the cover of <i class=\"fm-italics\">Pro ASP.NET Core 7, Tenth Edition<\/i> is \u201cTurc en habit d\u2019hiver,\u201d or \u201cTurk in winter clothes,\u201d taken from a collection by Jacques Grasset de Saint-Sauveur, published in 1788. Each illustration is finely drawn and colored by hand.<\/p>\n<p class=\"body\">In those days, it was easy to identify where people lived and what their trade or station in life was just by their dress. Manning celebrates the inventiveness and initiative of the computer business with book covers based on the rich diversity of regional culture centuries ago, brought back to life by pictures from collections such as this one.<\/p>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-10\">\n<div class=\"calibre1\" id=\"calibre_link-1282\">\n<h1 class=\"tochead\" id=\"calibre_link-1283\">1 Putting ASP.NET Core in context<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Putting ASP.NET Core in context<\/li>\n<li class=\"co-summary-bullet\">Understanding the role of the ASP.NET Core platform<\/li>\n<li class=\"co-summary-bullet\">Putting the ASP.NET Core application frame works in context<\/li>\n<li class=\"co-summary-bullet\">Understanding the structure of this book<\/li>\n<li class=\"co-summary-bullet\">Getting support when something doesn\u2019t work<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-742\"><\/a>ASP.NET Core is Microsoft\u2019s web development platform. The original ASP.NET was introduced in 2002, and it has been through several reinventions and reincarnations to become <a id=\"calibre_link-1284\"><\/a>ASP.NET Core 7, which is the topic of this book.<\/p>\n<p class=\"body\">ASP.NET Core consists of a platform for processing HTTP requests, a series of principal frameworks for creating applications, and secondary utility frameworks that provide supporting features, as illustrated by figure 1.1.<a id=\"calibre_link-1285\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre7\" src=\"\/images\/proaspnetcore7\/000000.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 1.1 The structure of ASP.NET Core<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding .NET Core, .NET Framework, and .NET<\/p>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1286\"><\/a>If you have never worked for a large corporation, you might have the impression that Microsoft is a disciplined organization with a clear strategy and an army of programmers working together to deliver complex products like ASP.NET Core.<\/p>\n<p class=\"fm-sidebar-text\">In reality, Microsoft is a chaotic collection of dysfunctional tribes that are constantly trying to undermine each other to get prestige and promotions. Products are released during lulls in the fighting, and successes are often entirely unexpected. This isn\u2019t unique to Microsoft&mdash;it is true of any large company&mdash;but it has a particular bearing on ASP.NET Core and the naming confusion that Microsoft has created.<\/p>\n<p class=\"fm-sidebar-text\">Several years ago, the part of Microsoft responsible for ASP.NET created its own version of the .NET platform, allowing ASP.NET to be updated more often than the rest of .NET. <i class=\"fm-italics\">ASP.NET Core<\/i> and <i class=\"fm-italics\">.NET Core<\/i> were created, allowing cross-platform development, and using a subset of the original .NET APIs, many of which were specific to Windows. It was a painful transition, but it meant that web development could evolve independently of the \u201clegacy\u201d Windows-only development, which would continue under the renamed <i class=\"fm-italics\">.NET Framework<\/i>.<\/p>\n<p class=\"fm-sidebar-text\">But no one wants to be in the \u201clegacy\u201d tribe because there is no glory in keeping the lights on at Microsoft. .NET Core was clearly the future and, one by one, the.NET groups at Microsoft argued that their technology and APIs should be part of .NET Core. The .NET Core APIs were gradually expanded, and the result was an incoherent mess, with half-hearted attempts to differentiate .NET Core and .NET Framework and standardize the APIs.<\/p>\n<p class=\"fm-sidebar-text\">To clean up the mess, Microsoft has merged <i class=\"fm-italics\">.NET Core<\/i> and <i class=\"fm-italics\">.NET Framework<\/i> into <i class=\"fm-italics\">.NET<\/i>, dropping the <i class=\"fm-italics\">Core<\/i> part of the name. \u201c.NET\u201d is a name I like to think was chosen on the way out of the office on a holiday weekend but which I suspect is the result of many months of heated argument.<\/p>\n<p class=\"fm-sidebar-text\">The problem with dropping <i class=\"fm-italics\">Core<\/i> from the name is that it cannot be carried out consistently. The name <i class=\"fm-italics\">ASP.NET Core<\/i> originally denoted the .NET Core version of ASP.NET, and going back to that name would be even more confusing.<\/p>\n<p class=\"fm-sidebar-text\">The result is that even Microsoft can\u2019t decide what name to use. You will see the term <i class=\"fm-italics\">ASP.NET Core<\/i> in a lot of the developer documentation&mdash;and that\u2019s the name I use in this book&mdash;but you will also see <i class=\"fm-italics\">ASP.NET Core in .NET<\/i>, especially in press releases and marketing material. It is not clear which name will win out, but until there is clarity, you should take care to determine whether you are using .NET Framework, .NET Core, or .NET.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-11\">1.1 Understanding the application frameworks<\/h2>\n<p class=\"body\">When you start using ASP.NET Core, it can be confusing to find that there are different application frameworks available. As you will learn, these frameworks are complementary and solve different problems, or, for some features, solve the same problems in different ways. Understanding the relationship between these frameworks means understanding the changing design patterns that Microsoft has supported, as I explain in the sections that follow.<a id=\"calibre_link-1287\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-12\">1.1.1 Understanding the MVC Framework<\/h3>\n<p class=\"body\"><a id=\"calibre_link-737\"><\/a>The MVC Framework was introduced in the early ASP.NET, long before .NET Core and the newer .NET were introduced. The original ASP.NET relied on a development model called Web Forms, which re-created the experience of writing desktop applications but resulted in unwieldy web projects that did not scale well. The MVC Framework was introduced alongside Web Forms with a development model that embraced the character of HTTP and HTML, rather than trying to hide it.<a id=\"calibre_link-1288\"><\/a><a id=\"calibre_link-1289\"><\/a><a id=\"calibre_link-1290\"><\/a><a id=\"calibre_link-1291\"><\/a><\/p>\n<p class=\"body\">MVC stands<a id=\"calibre_link-1292\"><\/a> for Model-View-Controller, which is a design pattern that describes the shape of an application. The MVC pattern emphasizes <i class=\"fm-italics\">separation of concerns<\/i>, where areas of functionality are defined independently, which was an effective antidote to the indistinct architectures that Web Forms led to.<\/p>\n<p class=\"body\">Early versions of the MVC Framework were built on the ASP.NET foundations that were originally designed for Web Forms, which led to some awkward features<a id=\"calibre_link-1293\"><\/a> and workarounds. With the move to .NET Core, ASP.NET became ASP.NET Core, and the MVC Framework was rebuilt on an open, extensible, and cross-platform foundation.<a id=\"calibre_link-1294\"><\/a><a id=\"calibre_link-1295\"><\/a><a id=\"calibre_link-1296\"><\/a><a id=\"calibre_link-1297\"><\/a><a id=\"calibre_link-1298\"><\/a><\/p>\n<p class=\"body\">The MVC Framework remains an important part of ASP.NET Core, but the way it is commonly used has changed with the rise of single-page applications (SPAs). In an SPA, the browser makes a single HTTP request and receives an HTML document that delivers a rich client, typically written in a JavaScript framework such as Angular or React. The shift to SPAs means that the clean separation that the MVC Framework was originally intended for is not as important, and the emphasis placed on following the MVC pattern is no longer essential, even though the MVC Framework remains useful (and is used to support SPAs through web services, as described in chapter 19).<a id=\"calibre_link-1299\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Putting patterns in their place<\/p>\n<p class=\"fm-sidebar-text\">Design patterns provoke strong reactio<a id=\"calibre_link-1300\"><\/a>ns, as the emails I receive from readers will testify. A substantial proportion of the messages I receive are complaints that I have not applied a pattern correctly.<a id=\"calibre_link-1301\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">Patterns are just other people\u2019s solutions to the problems they encountered in other projects. If you find yourself facing the same problem, understanding how it has been solved before can be helpful. But that doesn\u2019t mean you have to follow the pattern exactly, or at all, as long as you understand the consequences. If a pattern is intended to make projects manageable, for example, and you choose to deviate from that pattern, then you must accept that your project may be more difficult to manage. But a pattern followed slavishly can be worse than no pattern at all, and no pattern is suited to every project.<\/p>\n<p class=\"fm-sidebar-text\">My advice is to use patterns freely, adapt them as necessary, and ignore zealots who confuse patterns with commandments.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-13\">1.1.2 Understanding Razor Pages<\/h3>\n<p class=\"body\"><a id=\"calibre_link-741\"><\/a>One drawback of the MVC Framework is that it can require a lot of preparatory work before an application can start producing content. Despite its structural problems, one a<a id=\"calibre_link-1302\"><\/a>dvantage of Web Forms was that simple applications could be created in a couple of hours.<\/p>\n<p class=\"body\">Razor Pages takes the development ethos of Web Forms and implements it using the platform features originally developed for the MVC Framework. Code and content are mixed to form self-contained pages; this re-creates the speed of Web Forms development without some of the underlying technical problems (although scaling up complex projects can still be an issue).<a id=\"calibre_link-1303\"><\/a><a id=\"calibre_link-1304\"><\/a><\/p>\n<p class=\"body\">Razor Pages can be used alongside the MVC Framework, which is how I tend to use them. I write the main parts of the application using the MVC Framework and use Razor Pages for the secondary features, such as administration and reporting tools. You can see this approach in chapters 7&ndash;11, where I develop a realistic ASP.NET Core application called SportsStore.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-14\">1.1.3 Understanding Blazor<\/h3>\n<p class=\"body\">The rise of JavaScript client-side frameworks can be a barrier for C# developers, who must learn a different&mdash;and somewhat idiosyncratic&mdash;programming language. I have come to love JavaScript, which is as fluid and expressive as C#. But it takes time and commitment to become proficient in a new programming language, especially one that has fundamental differences from C#.<a id=\"calibre_link-1305\"><\/a><a id=\"calibre_link-1306\"><\/a><a id=\"calibre_link-1307\"><\/a><a id=\"calibre_link-1308\"><\/a><\/p>\n<p class=\"body\">Blazor attempts to bridge this gap by allowing C# to be used to write client-side applications. There are two versions of Blazor: Blazor Server and Blazor WebAssembly. Blazor Server relies on a persistent HTTP connection to the ASP.NET Core server, where the application\u2019s C# code is executed. Blazor WebAssembly goes one step further and executes the application\u2019s C# code in the browser. Neither version of Blazor is suited for all situations, as I explain in chapter 33, but they both give a sense of direction for the future of ASP.NET Core development.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-15\">1.1.4 Understanding the utility frameworks<\/h3>\n<p class=\"body\"><a id=\"calibre_link-743\"><\/a>Two frameworks are closely associated with ASP.NET Core but are not used directly to generate HTML<a id=\"calibre_link-1309\"><\/a> content or data. Entity Framework Co<a id=\"calibre_link-1310\"><\/a>re is Microsoft\u2019s object-relational mapping (ORM) framework, which represents data stored in a relational database as .NET objects. Entity Framework Core can be used in any .NET application, and it is commonly used to access databases in ASP.NET Core applications.<a id=\"calibre_link-1311\"><\/a><a id=\"calibre_link-1312\"><\/a><a id=\"calibre_link-1313\"><\/a><a id=\"calibre_link-1314\"><\/a><\/p>\n<p class=\"body\">ASP.NET Core Identity is Microsoft\u2019s authentication and authorization framework, and it is used to validate user credentials in ASP.NET Core applications and restrict access to application features.<\/p>\n<p class=\"body\">I describe only the basic features of both frameworks in this book, focusing on the capabilities required by most ASP.NET Core applications. But these are both complex frameworks that are too large to describe in detail in what is already a large book about ASP.NET Core.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Topics for future editions<\/p>\n<p class=\"fm-sidebar-text\">I don\u2019t have space in this book to cover every ASP.NET Core, Entity Framework Core, and ASP.NET Core Identity feature, so I have focused on those aspects that most projects require. If there are topics you think I should include in the next edition or in new deep-dive books, then please send me your suggestions at adam@adam-freeman.com.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-16\">1.1.5 Understanding the ASP.NET Core platform<\/h3>\n<p class=\"body\">The ASP.NET Core platform contains the low-level features required to receive and process HTTP requests and create responses. There is an integrated HTTP server, a system of middleware components to handle requests, and core features that the application frameworks depend on, such as URL routing and the Razor view engine.<a id=\"calibre_link-1315\"><\/a><a id=\"calibre_link-1316\"><\/a><a id=\"calibre_link-1317\"><\/a><\/p>\n<p class=\"body\">Most of your development time will be spent with the application frameworks, but effective ASP.NET Core use requires an understanding of the powerful capabilities that the platform provides, without which the higher-level frameworks could not function. I demonstrate how the ASP.NET Core platform works in detail in part 2 of this book and explain how the features it provides underpin every aspect of ASP.NET Core development.<\/p>\n<p class=\"body\">I have not described two notable platform features in this book: SignalR and gRPC. SignalR is used to create low-latency communication channels between applications. It provides the foundation for the Blazor Server framework that I describe in part 4 of this book, but SignalR is rarely used directly, and there are better alternatives for those few projects that need low-la<a id=\"calibre_link-1318\"><\/a>tency messaging, such as Azure Event Grid or Azure Service Bus.<a id=\"calibre_link-1319\"><\/a><a id=\"calibre_link-1320\"><\/a><a id=\"calibre_link-1321\"><\/a><a id=\"calibre_link-1322\"><\/a><\/p>\n<p class=\"body\">gRPC is an emerging<a id=\"calibre_link-1323\"><\/a> standard for cross-platform remote procedure calls (RPCs) over HTTP that was originally created by Google (the <i class=\"fm-italics\">g<\/i> in gRPC) and offers efficiency and scalability benefits. gRPC may be the future standard for web services, but it cannot be used in web applications because it requires low-level control of the HTTP messages that it sends, which browsers do not allow. (There is a browser library that allows gRPC to be used via a proxy server, but that undermines the benefits of using gRPC.) Until gRPC can be used in the browser, its inclusion in ASP.NET Core is of interest only for projects that use <a id=\"calibre_link-1324\"><\/a>it for communication between back-end servers, such as in microservices development. I may cover gRPC in future editions of this book but not until it can be used in the browser.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-17\">1.2 Understanding this book<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1325\"><\/a>To get the most from this book, you should be familiar with the basics of web development, understand how HTML and CSS work, and have a working knowledge of C#. Don\u2019t worry if you haven\u2019t done any client-side development, such as JavaScript. The emphasis in this book is on C# and ASP.NET Core, and you will be able to pick up everything you need to know as you progress through the chapters. In chapter 5, I summarize the most important C# features for ASP.NET Core development.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-18\">1.2.1 What software do I need to follow the examples?<\/h3>\n<p class=\"body\">You need a code editor (either Visual Studio or Visual Studio Code), the .NET Core Software Development Kit, and SQL Server LocalDB. All are available for use from Microsoft without charge, and chapter 2 contains instructions for installing everything you need.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-19\">1.2.2 What platform do I need to follow the examples?<\/h3>\n<p class=\"body\">This book is written for Windows. I used Windows 10 Pro, but any version of Windows supported by Visual Studio, Visual Studio Code, and .NET Core should work. ASP.NET Core is supported on other platforms, but the examples in this book rely on the SQL Server LocalDB feature, which is specific to Windows. You can contact me at adam@adam-freeman.com if you are trying to use another platform, and I will give you some general pointers for adapting the examples, albeit with the caveat that I won\u2019t be able to provide detailed help if you get stuck.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-20\">1.2.3 What if I have problems following the examples?<\/h3>\n<p class=\"body\">The first thing to do is to go back to the start of the chapter and begin again. Most problems are caused by missing a step or not fully following a listing. Pay close attention to the emphasis in code listings, which highlights the changes that are required.<\/p>\n<p class=\"body\">Next, check the errata\/corrections list, which is included in the book\u2019s GitHub repository. Technical books are complex, and mistakes are inevitable, despite my best efforts and those of my editors. Check the errata list for the list of known errors and instructions to resolve them.<\/p>\n<p class=\"body\">If you still have problems, then download the project for the chapter you are reading from the book\u2019s GitHub repository, <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>, and compare it to your project. I create the code for the GitHub repository by working through each chapter, so you should have the same files with the same contents in your project.<\/p>\n<p class=\"body\">If you still can\u2019t get the examples working, then you can contact me at adam@adam-freeman.com for help. Please make it clear in your email which book you are reading and which chapter\/example is causing the problem. Please remember that I get a lot of emails and that I may not respond immediately.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-21\">1.2.4 What if I find an error in the book?<\/h3>\n<p class=\"body\"><a id=\"calibre_link-948\"><\/a>You can report errors to me by email at adam@adam-freeman.com, although I ask that you first check the errata\/corrections list for this book, which you can find in the book\u2019s GitHub repository at <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>, in case it has already been reported.<a id=\"calibre_link-1326\"><\/a><\/p>\n<p class=\"body\">I add errors that are likely to cause confusion to readers, especially problems with example code, to the errata\/corrections file on the GitHub repository, with a grateful acknowledgment to the first reader who reported them. I also publish a typos list, which contains less serious issues, which usually means errors in the text surrounding examples that are unlikely to prevent a reader from following or understanding the examples.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Errata bounty<\/p>\n<p class=\"fm-sidebar-text\">Manning has agreed to give a free ebook to readers who are the first to report errors that make it onto the GitHub errata list for this book, which is for serious issues that will disrupt a reader\u2019s progress. Readers can select any Manning ebook, not just my books.<\/p>\n<p class=\"fm-sidebar-text\">This is an entirely discretionary and experimental program. Discretionary means that only I decide which errors are listed in the errata and which reader is the first to make a report. Experimental means Manning may decide not to give away any more books at any time for any reason. There are no appeals, and this is not a promise or a contract or any kind of formal offer or competition. Or, put another way, this is a nice and informal way to say thank you and to encourage readers to report mistakes that I have missed when writing this book.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-22\">1.2.5 What does this book cover?<\/h3>\n<p class=\"body\">I have tried to cover the features that will be required by most ASP.NET Core projects. This book is split into four parts, each of which covers a set of related topics.<\/p>\n<p class=\"fm-head2\">Part 1: Introducing ASP.NET Core<\/p>\n<p class=\"body\">This part of the book introduces ASP.NET Core. In addition to setting up your development environment and creating your first application, you\u2019ll learn about the most important C# features for ASP.NET Core development and how to use the ASP.NET Core development tools. Most of part 1 is given over to the development of a project called SportsStore, through which I show you a realistic development process from inception to deployment, touching on all the main features of ASP.NET Core and showing how they fit together&mdash;something that can be lost in the deep-dive chapters in the rest of the book.<\/p>\n<p class=\"fm-head2\">Part 2: The ASP.NET Core platform<\/p>\n<p class=\"body\">The chapters in this part of the book describe the key features of the ASP.NET Core platform. I explain how HTTP requests are processed, how to create and use middleware components, how to create routes, how to define and consume services, and how to work with Entity Framework Core. These chapters explain the foundations of ASP.NET Core, and understanding them is essential for effective ASP.NET Core development.<a id=\"calibre_link-1327\"><\/a><\/p>\n<p class=\"fm-head2\">Part 3: ASP.NET Core applications<\/p>\n<p class=\"body\">The chapters in this part of the book explain how to create different types of applications, including RESTful web services and HTML applications using controllers and Razor Pages. These chapters also describe the features that make it easy to generate HTML, including the views, view components, and tag helpers.<\/p>\n<p class=\"fm-head2\">Part 4: Advanced ASP.NET Core features<\/p>\n<p class=\"body\">The final part of the book explains how to create applications using Blazor Server, how to use the experimental Blazor WebAssembly, and how to authenticate users and authorize access using ASP.NET Core Identity.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-23\">1.2.6 What doesn\u2019t this book cover?<\/h3>\n<p class=\"body\">This book doesn\u2019t cover basic web development topics, such as HTML and CSS, and doesn\u2019t teach basic C# (although chapter 5 does describe C# features useful for ASP.NET Core development that may not be familiar to developers using older versions of .NET).<\/p>\n<p class=\"body\">As much as I like to dive into the details in my books, not every ASP.NET Core feature is useful in mainstream development, and I have to keep my books to a printable size. When I decide to omit a feature, it is because I don\u2019t think it is important or because the same outcome can be achieved using a technique that I do cover.<\/p>\n<p class=\"body\">As noted earlier, I have not described the ASP.NET Core support for SignalR and gRPC, and I note other features in later chapters that I don\u2019t describe, either because they are not broadly applicable or because there are better alternatives available. In each case, I explain why I have omitted a description and provide a reference to the Microsoft documentation for that topic.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-24\">1.2.7 How do I contact the author?<\/h3>\n<p class=\"body\">You can email me at adam@adam-freeman.com. It has been a few years since I first published an email address in my books. I wasn\u2019t entirely sure that it was a good idea, but I am glad that I did it. I have received emails from around the world, from readers working or studying in every industry, and&mdash;for the most part anyway&mdash;the emails are positive, polite, and a pleasure to receive.<\/p>\n<p class=\"body\">I try to reply promptly, but I get a lot of email, and sometimes I get a backlog, especially when I have my head down trying to finish writing a book. I always try to help readers who are stuck with an example in the book, although I ask that you follow the steps described earlier in this chapter before contacting me.<\/p>\n<p class=\"body\">While I welcome reader emails, there are some common questions for which the answers will always be no. I am afraid that I won\u2019t write the code for your new startup, help you with your college assignment, get involved in your development team\u2019s design dispute, or teach you how to program.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-25\">1.2.8 What if I really enjoyed this book?<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1328\"><\/a>Please email me at adam@adam-freeman.com and let me know. It is always a delight to hear from a happy reader, and I appreciate the time it takes to send those emails. Writing these books can be difficult, and those emails provide essential motivation to persist at an activity that can sometimes feel impossible.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-26\">1.2.9 What if this book has made me angry and I want to complain?<\/h3>\n<p class=\"body\">You can still email me at adam@adam-freeman.com, and I will still try to help you. Bear in mind that I can only help if you explain what the problem is and what you would like me to do about it. You should understand that sometimes the only outcome is to accept I am not the writer for you and that we will have closure only when you return this book and select another. I\u2019ll give careful thought to whatever has upset you, but after 25 years of writing books, I have come to understand that not everyone enjoys reading the books I like to write.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-27\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core is a cross-platform framework for creating web applications.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The ASP.NET Core platform is a powerful foundation on which application frameworks have been built.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The MVC Framework was the original ASP.NET Core framework. It is powerful and flexible but takes time to prepare.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The Razor Pages framework is a newer addition, which requires less initial preparation but can be more difficult to manage in complex projects.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Blazor is a framework that allows client-side applications to be written in C#, rather than JavaScript. There are versions of Blazor that execute the C# code within the ASP.NET Core server and entirely within the browser.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-28\">\n<div class=\"calibre1\" id=\"calibre_link-1329\">\n<h1 class=\"tochead\" id=\"calibre_link-1330\">Part 1.<\/h1>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-29\">\n<div class=\"calibre1\" id=\"calibre_link-1331\">\n<h1 class=\"tochead\" id=\"calibre_link-1332\">2 Getting started<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Installing the code editor and SDK required for ASP.NET Core development<\/li>\n<li class=\"co-summary-bullet\">Creating a simple ASP.NET Core project<\/li>\n<li class=\"co-summary-bullet\">Responding to HTTP requests using a combination of code and markup<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-1333\"><\/a>The best way to appreciate a software development framework is to jump right in and use it. In this chapter, I explain how to prepare for ASP.NET Core development and how to create and run an ASP.NET Core application.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-30\">2.1 Choosing a code editor<\/h2>\n<p class=\"body\">Microsoft provides a choice of tools for ASP.NET Core development: Visual Studio and Visual Studio Code. Visual Studio is the traditional development environment for .NET applications, and it offers an enormous range of tools and features for developing all sorts of applications. But it can be resource-hungry and slow, and some of the features are so determined to be helpful they get in the way of development.<\/p>\n<p class=\"body\">Visual Studio Code is a lightweight alternative that doesn\u2019t have the bells and whistles of Visual Studio but is perfectly capable of handling ASP.NET Core development.<\/p>\n<p class=\"body\">All the examples in this book include instructions for both editors, and both Visual Studio and Visual Studio Code can be used without charge, so you can use whichever suits your development style.<\/p>\n<p class=\"body\">If you are new to .NET development, then start with Visual Studio. It provides more structured support for creating the different types of files used in ASP.NET Core development, which will help ensure you get the expected results from the code examples.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> This book describes ASP.NET Core development for Windows. It is possible to develop and run ASP.NET Core applications on Linux and macOS, but most readers use Windows, and that is what I have chosen to focus on. Almost all the examples in this book rely on LocalDB, which is a Windows-only feature provided by SQL Server that is not available on other platforms. If you want to follow this book on another platform, then you can contact me using the email address in chapter 1, and I will try to help you get started.<a id=\"calibre_link-1248\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-31\">2.1.1 Installing Visual Studio<\/h3>\n<p class=\"body\">ASP.NET Core 7 requires Visual Studio 2022. I use the free Visual Studio 2022 Community Edition, which can be downloaded from <a class=\"url\" href=\"https:\/\/www.visualstudio.com\">www.visualstudio.com<\/a>. Run the installer, and you will see the prompt shown in figure 2.1.<a id=\"calibre_link-1334\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre8\" src=\"\/images\/proaspnetcore7\/000001.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.1 Starting the Visual Studio installer<\/p>\n<\/div>\n<p class=\"body\">Click the Continue button, and the installer will download the installation files, as shown in figure 2.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre9\" src=\"\/images\/proaspnetcore7\/000002.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.2 Downloading the Visual Studio installer files<\/p>\n<\/div>\n<p class=\"body\"><a id=\"calibre_link-1017\"><\/a>When the installer files have been downloaded, you will be presented with a set of installation options, grouped into workloads. Ensure that the \u201cASP.NET and web development\u201d workload is checked, as shown in figure 2.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre10\" src=\"\/images\/proaspnetcore7\/000003.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.3 Selecting the workload<\/p>\n<\/div>\n<p class=\"body\">Select the \u201cIndividual components\u201d section at the top of the window and ensure the SQL Server Express 2019 LocalDB option is checked, as shown in figure 2.4. This is the database component that I will be using to store data in later chapters.<a id=\"calibre_link-1335\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre11\" src=\"\/images\/proaspnetcore7\/000004.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.4 Ensuring LocalDB is installed<\/p>\n<\/div>\n<p class=\"body\">Click the Install button, and the files required for the selected workload will be downloaded and installed. To complete the installation, a reboot may be required.<a id=\"calibre_link-1336\"><\/a><a id=\"calibre_link-1337\"><\/a><a id=\"calibre_link-1338\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> You must also install the SDK, as described in the following section.<\/p>\n<p class=\"fm-head2\">2.1.2 Installing the .NET SDK<\/p>\n<p class=\"body\">The<a id=\"calibre_link-1339\"><\/a> Visual Studio installer will install the .NET Software Development Kit (SDK), but it may not install the version required for the examples in this book. Go to<a id=\"calibre_link-1340\"><\/a> <a class=\"url\" href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet-core\/7.0\">https:\/\/dotnet.microsoft.com\/download\/dotnet-core\/7.0<\/a> and download the installer for version 7.0.0 of the .NET SDK, which is the current release at the time of writing. Run the installer; once the installation is complete, open a new PowerShell command prompt from the Windows Start menu and run the command shown in listing 2.1, which displays a list of the installed .NET SDKs.<a id=\"calibre_link-1081\"><\/a><a id=\"calibre_link-1341\"><\/a><a id=\"calibre_link-1342\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 2.1 Listing the Installed SDKs<\/p>\n<pre class=\"programlisting\">dotnet --list-sdks<\/pre>\n<p class=\"body\">Here is the output from a fresh installation on a Windows machine that has not been used for .NET:<\/p>\n<pre class=\"programlisting\">7.0.100 [C:\\Program Files\\dotnet\\sdk]<\/pre>\n<p class=\"body\">If you have been working with different versions of .NET, you may see a longer list, like this one:<\/p>\n<pre class=\"programlisting\">5.0.100 [C:\\Program Files\\dotnet\\sdk]\n6.0.100 [C:\\Program Files\\dotnet\\sdk]\n6.0.113 [C:\\Program Files\\dotnet\\sdk]\n6.0.202 [C:\\Program Files\\dotnet\\sdk]\n6.0.203 [C:\\Program Files\\dotnet\\sdk]\n7.0.100 [C:\\Program Files\\dotnet\\sdk]<\/pre>\n<p class=\"body\">Regardless of how many entries there are, you must ensure there is one for the 7.0.1xx version, where the last two digits may differ.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-32\">2.1.3 Installing Visual Studio Code<\/h3>\n<p class=\"body\">If you have chosen to use Visual Studio Code, download the installer from <a class=\"url\" href=\"https:\/\/code.visualstudio.com\">https:\/\/code.visualstudio.com<\/a>. No specific version is required, and you should select the current stable build. Run the installer and ensure you check the Add to PATH option, as shown in figure 2.5.<a id=\"calibre_link-1343\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre12\" src=\"\/images\/proaspnetcore7\/000005.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.5 Configuring the Visual Studio Code installation<\/p>\n<\/div>\n<p class=\"fm-head2\">Installing the .NET SDK<\/p>\n<p class=\"body\">The Visual Studio Code installer does not include the .NET SDK, which must be installed separately. Go to <a class=\"url\" href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet-core\/7.0\">https:\/\/dotnet.microsoft.com\/download\/dotnet-core\/7.0<\/a> and download the installer for version 7.0.0 of the .NET SDK. Run the installer; once the installation is complete, open a new PowerShell command prompt from the Windows Start menu and run the command shown in listing 2.2, which displays a list of the installed .NET SDKs.<a id=\"calibre_link-1018\"><\/a><a id=\"calibre_link-1344\"><\/a><a id=\"calibre_link-1345\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 2.2 Listing the Installed SDKs<\/p>\n<pre class=\"programlisting\">dotnet --list-sdks<\/pre>\n<p class=\"body\">Here is the output from a fresh installation on a Windows machine that has not been used for .NET:<\/p>\n<pre class=\"programlisting\">7.0.100 [C:\\Program Files\\dotnet\\sdk]<\/pre>\n<p class=\"body\">If you have been working with different versions of .NET, you may see a longer list, like this one:<\/p>\n<pre class=\"programlisting\">5.0.100 [C:\\Program Files\\dotnet\\sdk]\n6.0.100 [C:\\Program Files\\dotnet\\sdk]\n6.0.113 [C:\\Program Files\\dotnet\\sdk]\n6.0.202 [C:\\Program Files\\dotnet\\sdk]\n6.0.203 [C:\\Program Files\\dotnet\\sdk]\n7.0.100 [C:\\Program Files\\dotnet\\sdk]<\/pre>\n<p class=\"body\">Regardless of how many entries there are, you must ensure there is one for the 7.0.1xx version, where the last two digits may differ.<\/p>\n<p class=\"fm-head2\">Installing SQL Server LocalDB<\/p>\n<p class=\"body\">The database examples in this book require LocalDB, which is a zero-configuration version of SQL Server that can be installed as part of<a id=\"calibre_link-1346\"><\/a> the SQL Server Express edition, which is available for use without charge from <a class=\"url\" href=\"https:\/\/www.microsoft.com\/en-in\/sql-server\/sql-server-downloads\">https:\/\/www.microsoft.com\/en-in\/sql-server\/sql-server-downloads<\/a>. Download and run the Express edition installer and select the Custom option, as shown in figure 2.6.<a id=\"calibre_link-1347\"><\/a><a id=\"calibre_link-1348\"><\/a><a id=\"calibre_link-1349\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre13\" src=\"\/images\/proaspnetcore7\/000006.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.6 Selecting the installation option for SQL Server<\/p>\n<\/div>\n<p class=\"body\">Once you have selected the Custom option, you will be prompted to select a download location for the installation files. Click the Install button, and the download will begin.<\/p>\n<p class=\"body\">When prompted, select the option to create a new SQL Server installation, as shown in figure 2.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre14\" src=\"\/images\/proaspnetcore7\/000007.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.7 Selecting an installation option<\/p>\n<\/div>\n<p class=\"body\">Work through the installation process, selecting the default options as they are presented. When you reach the Feature Selection page, ensure that the LocalDB option is checked, as shown in figure&nbsp;2.8. (You may want to uncheck the Machine Learning Services option, which is not used in this book and takes a long time to download and install.)<a id=\"calibre_link-1350\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre15\" src=\"\/images\/proaspnetcore7\/000008.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.8 Selecting the LocalDB feature<\/p>\n<\/div>\n<p class=\"body\">On the Instance Configuration page, select the \u201cDefault instance\u201d option, as shown in figure&nbsp;2.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre16\" src=\"\/images\/proaspnetcore7\/000009.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.9 Configuring the database<\/p>\n<\/div>\n<p class=\"body\">Continue to work through the installation process, selecting the default values, and complete the installation.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-33\">2.2 Creating an ASP.NET Core project<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1351\"><\/a>The most direct way to create a project is to use the command line. Open a new PowerShell command prompt from the Windows Start menu, navigate to the folder where you want to create your ASP.NET Core projects, and run the commands shown in listing 2.3.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in th<a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">is book&mdash;from https:\/\/github.com\/manningbooks\/pro-a<\/a>sp.net-core-7. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 2.3 Creating a new project<\/p>\n<pre class=\"programlisting\">dotnet new globaljson --sdk-version 7.0.100 --output FirstProject \ndotnet new mvc --no-https --output FirstProject --framework net7.0\ndotnet new sln -o FirstProject\ndotnet sln FirstProject add FirstProject<\/pre>\n<p class=\"body\">The first command creates a folder named <code class=\"fm-code-in-text\">FirstProject<\/code> and adds to it a file named <code class=\"fm-code-in-text\">global.json<\/code>, which specifies the version of .NET that the project will use; this ensures you get the expected results when following the examples. The second command creates a new ASP.NET Core project. The .NET SDK includes a range of templates for starting new projects, and the <code class=\"fm-code-in-text\">mvc<\/code> template is one of the options available for ASP.NET Core applications. This project template creates a project that is configured for the MVC Framework, which is one of the application types supported by ASP.NET Core. Don\u2019t be intimidated by the idea of choosing a framework, and don\u2019t worry if you have not heard of MVC&mdash;by the end of the book, you will understand the features that each offers and how they fit together. The remaining commands create a solution file, which allows multiple projects to be used together.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> This is one of a small number of chapters in which I use a project template that contains placeholder content. I don\u2019t like using predefined project templates because they encourage developers to treat important features, such as authentication, as black boxes. My goal in this book is to give you the knowledge to understand and manage every aspect of your ASP.NET Core applications, and that\u2019s why I start with an empty ASP.NET Core project. This chapter is about getting started quickly, for which the <code class=\"fm-code-in-text1\">mvc<\/code> template is well-suited.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-34\">2.2.1 Opening the project using Visual Studio<\/h3>\n<p class=\"body\">Start Visual Studio and click the \u201cOpen a project or solution\u201d button, as shown in figure 2.10.<a id=\"calibre_link-1088\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre17\" src=\"\/images\/proaspnetcore7\/000010.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.10 Opening the ASP.NET Core project<\/p>\n<\/div>\n<p class=\"body\">Navigate to the <code class=\"fm-code-in-text\">FirstProject<\/code> folder, select the <code class=\"fm-code-in-text\">FirstProject.sln<\/code> file, and click the Open button. Visual Studio will open the project and display its contents in the Solution Explorer window, as shown in figure 2.11. The files in the project were created by the project template.<a id=\"calibre_link-1352\"><\/a><a id=\"calibre_link-1353\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre18\" src=\"\/images\/proaspnetcore7\/000011.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.11 Opening the project in Visual Studio<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-35\">2.2.2 Opening the project with Visual Studio Code<\/h3>\n<p class=\"body\">Start Visual Studio Code and select File &gt; Open Folder. Navigate to the <code class=\"fm-code-in-text\">FirstProject<\/code> folder and click the Select Folder button. Visual Studio Code will open the project and display its contents in the Explorer pane, as shown in figure 2.12. (The default dark theme used in Visual Studio Code doesn\u2019t show well on the page, so I have changed to the light theme for the screenshots in this book.)<a id=\"calibre_link-1354\"><\/a><a id=\"calibre_link-1089\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre19\" src=\"\/images\/proaspnetcore7\/000012.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.12 Opening the project in Visual Studio Code<\/p>\n<\/div>\n<p class=\"body\">Additional configuration is required the first time you open a .NET project in Visual Studio Code. The first step is to click the <code class=\"fm-code-in-text\">Program.cs<\/code> file in the Explorer pane. This will trigger a prompt from Visual Studio Code to install the features required for C# development, as shown in figure 2.13. If you have not opened a C# project before, you will see a prompt that offers to install the required assets, also shown in figure 2.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre20\" src=\"\/images\/proaspnetcore7\/000013.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.13 Installing Visual Studio Code C# features<\/p>\n<\/div>\n<p class=\"body\">Click the Install or Yes button, as appropriate, and Visual Studio Code will download and install the features required for .NET projects.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-36\">2.3 Running the ASP.NET Core application<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1355\"><\/a>Visual Studio and Visual Studio Code can both run projects directly, but I use the command line tools throughout this book because they are more reliable and work more consistently, helping to ensure you get the expected results from the examples.<\/p>\n<p class=\"body\">When the project is created, a file named <code class=\"fm-code-in-text\">launchSettings.json<\/code> is created in the <code class=\"fm-code-in-text\">Properties<\/code> folder, and it is this file that determines which HTTP port ASP.NET Core will use to listen for HTTP requests. Open this file in your chosen editor and change the ports in the URLs it contains to 5000, as shown in listing 2.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 2.4 Setting the Port in the launchSettings.json File in the Properties Folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"FirstProject\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<p class=\"body\">It is only the URL in the <code class=\"fm-code-in-text\">profiles<\/code> section that affects the .NET command-line tools, but I have changed both of them to avoid any problems. Open a new PowerShell command prompt from the Windows Start menu; navigate to the <code class=\"fm-code-in-text\">FirstProject<\/code> project folder, which is the folder that contains the <code class=\"fm-code-in-text\">FirstProject.csproj<\/code> file; and run the command shown in listing 2.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 2.5 Starting the example application<\/p>\n<pre class=\"programlisting\"><code class=\"fm-code-in-text1\">dotnet run<\/code><\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">dotnet run<\/code> command compiles and starts the project. Once the application has started, open a new browser window and request http:\/\/localhost:5000, which will produce the response shown in figure 2.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre21\" src=\"\/images\/proaspnetcore7\/000014.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.14 Running the example project<\/p>\n<\/div>\n<p class=\"body\">When you are finished, use Control+C to stop the ASP.NET Core application.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-37\">2.3.1 Understanding endpoints<\/h3>\n<p class=\"body\"><a id=\"calibre_link-930\"><\/a>In an ASP.NET Core application, incoming requests are handled by <i class=\"fm-italics\">endpoints<\/i>. The endpoint that produced the response in figure 2.14 is an <i class=\"fm-italics\">action<\/i>, which is a method that is written in C#. An action is defined in a <i class=\"fm-italics\">controller<\/i>, which is a C# class that is derived from the <code class=\"fm-code-in-text\">Microsoft.AspNetCore.Mvc.Controller<\/code> class, the built-in controller base class.<\/p>\n<p class=\"body\">Each public method defined by a controller is an action, which means you can invoke the action method to handle an HTTP request. The convention in ASP.NET Core projects is to put controller classes in a folder named <code class=\"fm-code-in-text\">Controllers<\/code>, which was created by the template used to set up the project.<a id=\"calibre_link-1356\"><\/a><\/p>\n<p class=\"body\">The project template added a controller to the <code class=\"fm-code-in-text\">Controllers<\/code> folder to help jump-start development. The controller is defined in the class file named <code class=\"fm-code-in-text\">HomeController.cs<\/code>. Controller classes contain a name followed by the word <code class=\"fm-code-in-text\">Controller<\/code>, which means that when you see a file called <code class=\"fm-code-in-text\">HomeController.cs<\/code>, you know that it contains a controller called <code class=\"fm-code-in-text\">Home<\/code>, which is the default controller that is used in ASP.NET Core applications.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Don\u2019t worry if the terms <i class=\"fm-italics\">controller<\/i> and <i class=\"fm-italics\">action<\/i> don\u2019t make immediate sense. Just keep following the example, and you will see how the HTTP request sent by the browser is handled by C# code.<\/p>\n<p class=\"body\">Find the <code class=\"fm-code-in-text\">HomeController.cs<\/code> file in the Solution Explorer or Explorer pane and click it to open it for editing. You will see the following code:<\/p>\n<pre class=\"programlisting\">using System.Diagnostics;\nusing Microsoft.AspNetCore.Mvc;\nusing FirstProject.Models;\n\nnamespace FirstProject.Controllers;\n\npublic class HomeController : Controller {\n    private readonly ILogger&lt;HomeController&gt; _logger;\n    public HomeController(ILogger&lt;HomeController&gt; logger) {\n        _logger = logger;\n    }\n        \n    public IActionResult Index() {\n        return View();\n    }\n        \n    public IActionResult Privacy() {\n        return View();\n    }\n        \n    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, \n        NoStore = true)]\n    public IActionResult Error() {\n        return View(new ErrorViewModel { RequestId = Activity.Current?.Id \n            ?? HttpContext.TraceIdentifier });\n    }\n}<\/pre>\n<p class=\"body\">Using the code editor, replace the contents of the <code class=\"fm-code-in-text\">HomeController.cs<\/code> file so that it matches listing 2.6. I have removed all but one of the methods, changed the result type and its implementation, and removed the <code class=\"fm-code-in-text\">using<\/code> statements for unused namespaces.<a id=\"calibre_link-1357\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 2.6 Changing the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace FirstProject.Controllers {\n\n    public class HomeController : Controller {\n        \n        public string Index() {\n            return \"Hello World\";\n        }\n    }\n}<\/pre>\n<p class=\"body\">The result is that the <code class=\"fm-code-in-text\">Home<\/code> controller defines a single action, named <code class=\"fm-code-in-text\">Index<\/code>. These changes don\u2019t produce a dramatic effect, but they make for a nice demonstration. I have changed the method named <code class=\"fm-code-in-text\">Index<\/code> so that it returns the string <code class=\"fm-code-in-text\">Hello<\/code> <code class=\"fm-code-in-text\">World<\/code>. Using the PowerShell prompt, run the <code class=\"fm-code-in-text\">dotnet<\/code> <code class=\"fm-code-in-text\">run<\/code> command in the <code class=\"fm-code-in-text\">FirstProject<\/code> folder again and use the browser to request http:\/\/localhost:5000. The configuration of the project created by the template in listing 2.3 means the HTTP request will be processed by the <code class=\"fm-code-in-text\">Index<\/code> action defined by the <code class=\"fm-code-in-text\">Home<\/code> controller. Put another way, the request will be processed by the <code class=\"fm-code-in-text\">Index<\/code> method defined by the <code class=\"fm-code-in-text\">HomeController<\/code> class. The <code class=\"fm-code-in-text\">string<\/code> produced by the <code class=\"fm-code-in-text\">Index<\/code> method is used as the response to the browser\u2019s HTTP request, as shown in figure 2.15.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre22\" src=\"\/images\/proaspnetcore7\/000015.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.15 The output from the action method<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-38\">2.3.2 Understanding routes<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1092\"><\/a>The ASP.NET Core <i class=\"fm-italics\">routing<\/i> system is responsible for selecting the endpoint that will handle an HTTP request. A <i class=\"fm-italics\">route<\/i> is a rule that is used to decide how a request is handled. When the project was created, a default rule was created to get started. You can request any of the following URLs, and they will be dispatched to the <code class=\"fm-code-in-text\">Index<\/code> action defined by the <code class=\"fm-code-in-text\">Home<\/code> controller:<a id=\"calibre_link-1358\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">\/<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">\/Home<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">\/Home\/Index<\/code><\/p>\n<\/li>\n<\/ul>\n<p class=\"body\">So, when a browser requests http:\/\/yoursite\/ or http:\/\/yoursite\/Home, it gets back the output from HomeController\u2019s Index method. You can try this yourself by changing the URL in the browser. At the moment, it will be http:\/\/localhost:5000\/. If you append <code class=\"fm-code-in-text\">\/Home<\/code> or <code class=\"fm-code-in-text\">\/Home\/Index<\/code> to the URL and press Return, you will see the same <code class=\"fm-code-in-text\">Hello<\/code> <code class=\"fm-code-in-text\">World<\/code> result from the application.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-39\">2.3.3 Understanding HTML rendering<\/h3>\n<p class=\"body\">The output from the previous example wasn\u2019t HTML&mdash;it was just the string <code class=\"fm-code-in-text\">Hello<\/code> <code class=\"fm-code-in-text\">World<\/code>. To produce an HTML response to a browser request, I need a <i class=\"fm-italics\">view<\/i>, which tells ASP.NET Core how to process the result produced by the <code class=\"fm-code-in-text\">Index<\/code> method into an HTML response that can be sent to the browser.<\/p>\n<p class=\"fm-head2\">Creating and rendering a view<\/p>\n<p class=\"body\">The first thing I need to do is modify my <code class=\"fm-code-in-text\">Index<\/code> action method, as shown in listing 2.7. The changes are shown in bold, which is a convention I follow throughout this book to make the examples easier to follow.<a id=\"calibre_link-1359\"><\/a><a id=\"calibre_link-1360\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 2.7 Rendering a view in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace FirstProject.Controllers {\n\n    public class HomeController : Controller {\n        <b class=\"fm-bold\">public ViewResult Index() {<\/b>\n            <b class=\"fm-bold\">return View(\"MyView\");<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">When I return a <code class=\"fm-code-in-text\">ViewResult<\/code> object from an action method, I am instructing ASP.NET Core to <i class=\"fm-italics\">render<\/i> a view. I create the <code class=\"fm-code-in-text\">ViewResult<\/code> by calling the <code class=\"fm-code-in-text\">View<\/code> method, specifying the name of the view that I want to use, which is <code class=\"fm-code-in-text\">MyView<\/code>.<\/p>\n<p class=\"body\">Use Control+C to stop ASP.NET Core and then use the <code class=\"fm-code-in-text\">dotnet run<\/code> command to compile and start it again. Use the browser to request http:\/\/localhost:5000, and you will see ASP.NET Core trying to find the view, as shown by the error message displayed in figure 2.16.<a id=\"calibre_link-1361\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre23\" src=\"\/images\/proaspnetcore7\/000016.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.16 Trying to find a view<\/p>\n<\/div>\n<p class=\"body\">This is a helpful error message. It explains that ASP.NET Core could not find the view I specified for the action method and explains where it looked. Views are stored in the <code class=\"fm-code-in-text\">Views<\/code> folder, organized into subfolders. Views that are associated with the <code class=\"fm-code-in-text\">Home<\/code> controller, for example, are stored in a folder called <code class=\"fm-code-in-text\">Views\/Home<\/code>. Views that are not specific to a single controller are stored in a folder called <code class=\"fm-code-in-text\">Views\/Shared<\/code>. The template used to create the project added the <code class=\"fm-code-in-text\">Home<\/code> and <code class=\"fm-code-in-text\">Shared<\/code> folders automatically and added some placeholder views to get the project started.<\/p>\n<p class=\"body\">If you are using Visual Studio, right-click the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder in the Solution Explorer and select Add &gt; New Item from <a id=\"calibre_link-1362\"><\/a>the pop-up menu. Visual Studio will present you with a list of templates for adding items to the project. Locate the Razor View - Empty item, which can be found in the ASP.NET Core &gt; Web &gt; ASP.NET section, as shown in figure 2.17.<\/p>\n<p class=\"body\">For Visual Studio, you may need to click the Show All Templates button before the list of templates is displayed. Set the name of the new file to <code class=\"fm-code-in-text\">MyView.cshtml<\/code> and click the Add button. Visual Studio will add a file named <code class=\"fm-code-in-text\">MyView.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder and will open it for editing. Replace the contents of the file with those shown in listing 2.8.<a id=\"calibre_link-1363\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre24\" src=\"\/images\/proaspnetcore7\/000017.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.17 Selecting a Visual Studio item template<\/p>\n<\/div>\n<p class=\"body\">Visual Studio Code doesn\u2019t provide item templates. Instead, right-click the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder in the file explorer pane and select New File from the pop-up menu. Set the name of the file to <code class=\"fm-code-in-text\">MyView.cshtml<\/code> and press Return. The file will be created and opened for editing. Add the content shown in listing 2.8.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> It is easy to end up creating the view file in the wrong folder. If you didn\u2019t end up with a file called <code class=\"fm-code-in-text1\">MyView.cshtml<\/code> in the <code class=\"fm-code-in-text1\">Views\/Home<\/code> folder, then either drag the file into the correct folder or delete the file and try again.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 2.8 The contents of the MyView.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;Index&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div&gt;\n        Hello World (from the view)\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The new contents of the view file are mostly HTML. The exception is the part that looks like this:<\/p>\n<pre class=\"programlisting\">...\n@{\n    Layout = null;\n}\n...<\/pre>\n<p class=\"body\">This is an expression that will be interpreted by Razor, which is the component that processes the contents of views and generates HTML that is sent to the browser. Razor is a <i class=\"fm-italics\">view engine<\/i>, and the expressions in views are known as <i class=\"fm-italics\">Razor expressions<\/i>.<\/p>\n<p class=\"body\"><a id=\"calibre_link-1364\"><\/a>The Razor expression in listing 2.8 tells Razor that I chose not to use a layout, which is like a template for the HTML that will be sent to the browser (and which I describe in chapter 22). To see the effect of creating the view, use Control+C to stop ASP.NET Core if it is running and use the <code class=\"fm-code-in-text\">dotnet run<\/code> command to compile and start the application again. Use a browser to request http:\/\/localhost:5000, and you will see the result shown in figure 2.18.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre25\" src=\"\/images\/proaspnetcore7\/000018.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.18 Rendering a view<\/p>\n<\/div>\n<p class=\"body\">When I first edited the <code class=\"fm-code-in-text\">Index<\/code> action method, it returned a <code class=\"fm-code-in-text\">string<\/code> value. This meant that ASP.NET Core did nothing except pass the string value as is to the browser. Now that the <code class=\"fm-code-in-text\">Index<\/code> method returns a <code class=\"fm-code-in-text\">ViewResult<\/code>, Razor is used to process a view and render an HTML response. Razor was able to locate the view because I followed the standard naming convention, which is to put view files in a folder whose name matched the controller that contains the action method. In this case, this meant putting the view file in the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder, since the action method is defined by the <code class=\"fm-code-in-text\">Home<\/code> controller.<\/p>\n<p class=\"body\">I can return other results from action methods besides strings and <code class=\"fm-code-in-text\">ViewResult<\/code> objects. For example, if I return a <code class=\"fm-code-in-text\">RedirectResult<\/code>, the browser will be redirected to another URL. If I return an <code class=\"fm-code-in-text\">HttpUnauthorizedResult<\/code>, I can prompt the user to log in. These objects are collectively known as <i class=\"fm-italics\">action results<\/i>. The action result system lets you encapsulate and reuse common responses in actions. I\u2019ll tell you more about them and explain the different ways they can be used in chapter 19.<\/p>\n<p class=\"fm-head2\">Adding dynamic output<\/p>\n<p class=\"body\">The whole point of a web application is to construct and display <i class=\"fm-italics\">dynamic<\/i> output. The job of the action method is to construct data and pass it to the view so it can be used to create HTML content based on the data values. Action methods provide data to views by passing arguments to the <code class=\"fm-code-in-text\">View<\/code> method, as shown in listing 2.9. The data provided to the view is known as the <i class=\"fm-italics\">view model<\/i>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 2.9 Using a view model in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace FirstProject.Controllers {\n\n    public class HomeController : Controller {\n \n        public ViewResult Index() {\n            <b class=\"fm-bold\">int hour = DateTime.Now.Hour;<\/b>\n            <b class=\"fm-bold\">string viewModel =<\/b> \n                <b class=\"fm-bold\">hour &lt; 12 ? \"Good Morning\" : \"Good Afternoon\";<\/b>\n            <b class=\"fm-bold\">return View(\"MyView\", viewModel);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The view model in this example is a <code class=\"fm-code-in-text\">string<\/code>, and it is provided to the view as the second argument to the <code class=\"fm-code-in-text\">View<\/code> method. Listing 2.10 updates the view so that it receives and uses the view model in the HTML it generates.<a id=\"calibre_link-1365\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 2.10 Using a view model in the MyView.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@model string<\/b>\n@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;Index&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div&gt;\n        <b class=\"fm-bold\">@Model World (from the view)<\/b>\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The type of the view model is specified using the <code class=\"fm-code-in-text\">@model<\/code> expression, with a lowercase <code class=\"fm-code-in-text\">m<\/code>. The view model value is included in the HTML output using the <code class=\"fm-code-in-text\">@Model<\/code> expression, with an uppercase <code class=\"fm-code-in-text\">M<\/code>. (It can be difficult at first to remember which is lowercase and which is uppercase, but it soon becomes second nature.)<\/p>\n<p class=\"body\">When the view is rendered, the view model data provided by the action method is inserted into the HTML response. Use Control+C to stop ASP.NET Core and use the <code class=\"fm-code-in-text\">dotnet run<\/code> command to build and start it again. Use a browser to request http:\/\/localhost:5000, and you will see the output shown in figure 2.19 (although you may see the morning greeting if you are following this example before midday).<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre25\" src=\"\/images\/proaspnetcore7\/000019.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 2.19 Generating dynamic content<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-40\">2.3.4 Putting the pieces together<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1366\"><\/a>It is a simple result, but this example reveals all the building blocks you need to create a simple ASP.NET Core web application and to generate a dynamic response. The ASP.NET Core platform receives an HTTP request and uses the routing system to match the request URL to an endpoint. The endpoint, in this case, is the <code class=\"fm-code-in-text\">Index<\/code> action method defined by the <code class=\"fm-code-in-text\">Home<\/code> controller. The method is invoked and produces a <code class=\"fm-code-in-text\">ViewResult<\/code> object that contains the name of a view and a view model object. The Razor view engine locates and processes the view, evaluating the <code class=\"fm-code-in-text\">@Model<\/code> expression to insert the data provided by the action method into the response, which is returned to the browser and displayed to the user. There are, of course, many other features available, but this is the essence of ASP.NET Core, and it is worth bearing this simple sequence in mind as you read the rest of the book.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-41\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core development can be done with Visual Studio or Visual Studio Code, or you can choose your own code editor.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Most code editors provide integrated code builds, but the most reliable way to get consistent results across tools and platforms is by using the <code class=\"fm-code-in-text\">dotnet<\/code> command.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core relies on endpoints to process HTTP requests.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Endpoints can be written entirely in C# or use HTML that has been annotated with code expressions.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-42\">\n<div class=\"calibre1\" id=\"calibre_link-1367\">\n<h1 class=\"tochead\" id=\"calibre_link-1368\">3 Your first ASP.NET Core application<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using ASP.NET Core to create an application that accepts RSVP responses<\/li>\n<li class=\"co-summary-bullet\">Creating a simple data model<\/li>\n<li class=\"co-summary-bullet\">Creating a controller and view that presents and processes a form<\/li>\n<li class=\"co-summary-bullet\">Validating user data and displaying validation errors<\/li>\n<li class=\"co-summary-bullet\">Applying CSS styles to the HTML generated by the application<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-1369\"><\/a>Now that you are set up for ASP.NET Core development, it is time to create a simple application. In this chapter, you\u2019ll create a data-entry application using ASP.NET Core. My goal is to demonstrate ASP.NET Core in action, so I will pick up the pace a little and skip over some of the explanations as to how things work behind the scenes. But don\u2019t worry; I\u2019ll revisit these topics in-depth in later chapters.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-43\">3.1 Setting the scene<\/h2>\n<p class=\"body\">Imagine that a friend has decided to host a New Year\u2019s Eve party and that she has asked me to create a web app that allows her invitees to electronically RSVP. She has asked for these four key features:<\/p>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">A home page that shows information about the party<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">A form that can be used to RSVP<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Validation for the RSVP form, which will display a thank-you page<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">A summary page that shows who is coming to the party<\/p>\n<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I create an ASP.NET Core project and use it to create a simple application that contains these features; once everything works, I\u2019ll apply some styling to improve the appearance of the finished application.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-44\">3.2 Creating the project<\/h2>\n<p class=\"body\">Open a PowerShell command prompt from the Windows Start menu, navigate to a convenient location, and run the commands in listing 3.1 to create a project named PartyInvites.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.1 Creating a new project<\/p>\n<pre class=\"programlisting\">dotnet new globaljson --sdk-version 7.0.100 --output PartyInvites\ndotnet new mvc --no-https --output PartyInvites --framework net7.0\ndotnet new sln -o PartyInvites\ndotnet sln PartyInvites add PartyInvites<\/pre>\n<p class=\"body\">These are the same commands I used to create the project in chapter 2. These commands ensure you get the right project starting point that uses the required version of .NET.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-45\">3.2.1 Preparing the project<\/h3>\n<p class=\"body\">Open the project (by opening the <code class=\"fm-code-in-text\">PartyInvites.sln<\/code> file with Visual Studio or the <code class=\"fm-code-in-text\">PartyInvites<\/code> folder in Visual Studio Code) and change the contents of the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file in the <code class=\"fm-code-in-text\">Properties<\/code> folder, as shown in listing 3.2, to set the port that will be used to listen for HTTP requests.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.2 Setting ports in the launchSettings.json file in the Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"PartyInvites\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<p class=\"body\">Replace the contents of the <code class=\"fm-code-in-text\">HomeController.cs<\/code> file in the <code class=\"fm-code-in-text\">Controllers<\/code> folder with the code shown in listing 3.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.3 The new contents of the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace PartyInvites.Controllers {\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View();\n        }\n    }\n}<\/pre>\n<p class=\"body\">This provides a clean starting point for the new application, defining a single action method that selects the default view for rendering. To provide a welcome message to party invitees, open the <code class=\"fm-code-in-text\">Index.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder and replace the contents with those shown in listing 3.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.4 Replacing the contents of the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;Party!&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div&gt;\n        &lt;div&gt;\n            We're going to have an exciting party.&lt;br \/&gt;\n            (To do: sell it better. Add pictures or something.)\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Run the command shown in listing 3.5 in the PartyInvites folder to compile and execute the project.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.5 Compiling and running the project<\/p>\n<pre class=\"programlisting\"><code class=\"fm-code-in-text1\">dotnet watch<\/code><\/pre>\n<p class=\"body\">Once the project has started, a new browser window will be opened, and you will see the details of the party (well, the placeholder for the details, but you get the idea), as shown in figure 3.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre26\" src=\"\/images\/proaspnetcore7\/000020.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.1 Adding to the view HTML<\/p>\n<\/div>\n<p class=\"body\">Leave the <code class=\"fm-code-in-text\">dotnet watch<\/code> command running. As you make changes to the project, you will see that the code is automatically recompiled and that changes are automatically displayed in the browser.<\/p>\n<p class=\"body\">If you make a mistake following the examples, you may find that the <code class=\"fm-code-in-text\">dotnet watch<\/code> command indicates that it can\u2019t automatically update the browser. If that happens, select the option to restart the application.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-46\">3.2.2 Adding a data model<\/h3>\n<p class=\"body\">The data model is the most important part of any ASP.NET Core application. The model is the representation of the real-world objects, processes, and rules that define the subject, known as the <i class=\"fm-italics\">domain<\/i>, of the application. The model, often referred to as a <i class=\"fm-italics\">domain model<\/i>, contains the C# objects (known as <i class=\"fm-italics\">domain objects<\/i>) that make up the universe of the application and the methods that manipulate them. In most projects, the job of the ASP.NET Core application is to provide the user with access to the data model and the features that allow the user to interact with it.<\/p>\n<p class=\"body\">The convention for an ASP.NET Core application is that the data model classes are defined in a folder named <code class=\"fm-code-in-text\">Models<\/code>, which was added to the project by the template used in listing 3.1.<\/p>\n<p class=\"body\">I don\u2019t need a complex model for the PartyInvites project because it is such a simple application. I need just one domain class that I will call <code class=\"fm-code-in-text\">GuestResponse<\/code>. This object will represent an RSVP from an invitee.<\/p>\n<p class=\"body\">If you are using Visual Studio, right-click the <code class=\"fm-code-in-text\">Models<\/code> folder and select Add &gt; Class from the pop-up menu. Set the name of the class to <code class=\"fm-code-in-text\">GuestResponse.cs<\/code> and click the Add button. If you are using Visual Studio Code, right-click the <code class=\"fm-code-in-text\">Models<\/code> folder, select New File, and enter <code class=\"fm-code-in-text\">GuestResponse.cs<\/code> as the file name. Use the new file to define the class shown in listing 3.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.6 The contents of the GuestResponse.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace PartyInvites.Models {\n\n    public class GuestResponse {\n \n        public string? Name { get; set; }\n        public string? Email { get; set; }\n        public string? Phone { get; set; }\n        public bool? WillAttend { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Notice that all the properties defined by the <code class=\"fm-code-in-text\">GuestResponse<\/code> class are nullable. I explain why this is important in the \u201cAdding Validation\u201d section later in the chapter.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Restarting the automatic build<\/p>\n<p class=\"fm-sidebar-text\">You may see a warning produced by the <code class=\"fm-code-in-text1\">dotnet watch<\/code> command telling you that a hot reload cannot be applied. The <code class=\"fm-code-in-text1\">dotnet watch<\/code> command can\u2019t cope with every type of change, and some changes cause the automatic rebuild process to fail. You will see this prompt at the command line:<\/p>\n<pre class=\"programlisting\">watch : Do you want to restart your app \n    - Yes (y) \/ No (n) \/ Always (a) \/ Never (v)?<\/pre>\n<p class=\"fm-sidebar-text\">Press <code class=\"fm-code-in-text1\">a<\/code> to always rebuild the project. Microsoft makes frequent improvements to the <code class=\"fm-code-in-text1\">dotnet watch<\/code> command and so the actions that trigger this problem change.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-47\">3.2.3 Creating a second action and view<\/h3>\n<p class=\"body\">One of my application goals is to include an RSVP form, which means I need to define an action method that can receive requests for that form. A single controller class can define multiple action methods, and the convention is to group related actions in the same controller. Listing 3.7 adds a new action method to the <code class=\"fm-code-in-text\">Home<\/code> controller. Controllers can return different result types, which are explained in later chapters.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.7 Adding an action in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace PartyInvites.Controllers {\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View();\n        }\n \n        <b class=\"fm-bold\">public ViewResult RsvpForm() {<\/b>\n            <b class=\"fm-bold\">return View();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Both action methods invoke the <code class=\"fm-code-in-text\">View<\/code> method without arguments, which may seem odd, but remember that the Razor view engine will use the name of the action method when looking for a view file, as explained in chapter 2. That means the result from the <code class=\"fm-code-in-text\">Index<\/code> action method tells Razor to look for a view called <code class=\"fm-code-in-text\">Index.cshtml<\/code>, while the result from the <code class=\"fm-code-in-text\">RsvpForm<\/code> action method tells Razor to look for a view called <code class=\"fm-code-in-text\">RsvpForm.cshtml<\/code>.<\/p>\n<p class=\"body\">If you are using Visual Studio, right-click the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder and select Add &gt; New Item from the pop-up menu. Select the Razor View &ndash; Empty item, set the name to <code class=\"fm-code-in-text\">RsvpForm.cshtml<\/code>, and click the Add button to create the file. Replace the contents with those shown in listing 3.8.<\/p>\n<p class=\"body\">If you are using Visual Studio Code, right-click the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder and select New File from the pop-up menu. Set the name of the file to <code class=\"fm-code-in-text\">RsvpForm.cshtml<\/code> and add the contents shown in listing 3.8.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.8 The contents of the RsvpForm.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;RsvpForm&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div&gt;\n        This is the RsvpForm.cshtml View\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">This content is just static HTML for the moment. Use the browser to request http:\/\/localhost:5000\/home\/rsvpform. The Razor view engine locates the <code class=\"fm-code-in-text\">RsvpForm.cshtml<\/code> file and uses it to produce a response, as shown in figure 3.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre27\" src=\"\/images\/proaspnetcore7\/000021.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.2 Rendering a second view<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-48\">3.2.4 Linking action methods<\/h3>\n<p class=\"body\">I want to be able to create a link from the <code class=\"fm-code-in-text\">Index<\/code> view so that guests can see the <code class=\"fm-code-in-text\">RsvpForm<\/code> view without having to know the URL that targets a specific action method, as shown in listing 3.9.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.9 Adding a link in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;Party!&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div&gt;\n        &lt;div&gt;\n            We're going to have an exciting party.&lt;br \/&gt;\n            (To do: sell it better. Add pictures or something.)\n        &lt;\/div&gt;\n        <b class=\"fm-bold\">&lt;a asp-action=\"RsvpForm\"&gt;RSVP Now&lt;\/a&gt;<\/b>\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The addition to the listing is an <code class=\"fm-code-in-text\">a<\/code> element that has an <code class=\"fm-code-in-text\">asp-action<\/code> attribute. The attribute is an example of a <i class=\"fm-italics\">tag helper<\/i> attribute, which is an instruction for Razor that will be performed when the view is rendered. The <code class=\"fm-code-in-text\">asp-action<\/code> attribute is an instruction to add an <code class=\"fm-code-in-text\">href<\/code> attribute to the <code class=\"fm-code-in-text\">a<\/code> element that contains a URL for an action method. I explain how tag helpers work in chapters 25&ndash;27, but this tag helper tells Razor to insert a URL for an action method defined by the same controller for which the current view is being rendered.<\/p>\n<p class=\"body\">Use the browser to request http:\/\/localhost:5000, and you will see the link that the helper has created, as shown in figure 3.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre27\" src=\"\/images\/proaspnetcore7\/000021.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.3 Linking between action methods<\/p>\n<\/div>\n<p class=\"body\">Roll the mouse over the RSVP Now link in the browser. You will see that the link points to http:\/\/localhost:5000\/Home\/RsvpForm.<\/p>\n<p class=\"body\">There is an important principle at work here, which is that you should use the features provided by ASP.NET Core to generate URLs, rather than hard-code them into your views. When the tag helper created the <code class=\"fm-code-in-text\">href<\/code> attribute for the <code class=\"fm-code-in-text\">a<\/code> element, it inspected the configuration of the application to figure out what the URL should be. This allows the configuration of the application to be changed to support different URL formats without needing to update any views.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-49\">3.2.5 Building the form<\/h3>\n<p class=\"body\">Now that I have created the view and can reach it from the <code class=\"fm-code-in-text\">Index<\/code> view, I am going to build out the contents of the <code class=\"fm-code-in-text\">RsvpForm.cshtml<\/code> file to turn it into an HTML form for editing <code class=\"fm-code-in-text\">GuestResponse<\/code> objects, as shown in listing 3.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.10 Creating a form view in the RsvpForm.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@model PartyInvites.Models.GuestResponse<\/b>\n@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;RsvpForm&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    <b class=\"fm-bold\">&lt;form asp-action=\"RsvpForm\" method=\"post\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label asp-for=\"Name\"&gt;Your name:&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;input asp-for=\"Name\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label asp-for=\"Email\"&gt;Your email:&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;input asp-for=\"Email\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label asp-for=\"Phone\"&gt;Your phone:&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;input asp-for=\"Phone\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label asp-for=\"WillAttend\"&gt;Will you attend?&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;select asp-for=\"WillAttend\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;option value=\"\"&gt;Choose an option&lt;\/option&gt;<\/b>\n                <b class=\"fm-bold\">&lt;option value=\"true\"&gt;Yes, I'll be there&lt;\/option&gt;<\/b>\n                <b class=\"fm-bold\">&lt;option value=\"false\"&gt;No, I can't come&lt;\/option&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/select&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;button type=\"submit\"&gt;Submit RSVP&lt;\/button&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/form&gt;<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@model<\/code> expression specifies that the view expects to receive a <code class=\"fm-code-in-text\">GuestResponse<\/code> object as its view model. I have defined a <code class=\"fm-code-in-text\">label<\/code> and <code class=\"fm-code-in-text\">input<\/code> element for each property of the <code class=\"fm-code-in-text\">GuestResponse<\/code> model class (or, in the case of the <code class=\"fm-code-in-text\">WillAttend<\/code> property, a <code class=\"fm-code-in-text\">select<\/code> element). Each element is associated with the model property using the <code class=\"fm-code-in-text\">asp-for<\/code> attribute, which is another tag helper attribute. The tag helper attributes configure the elements to tie them to the view model object. Here is an example of the HTML that the tag helpers produce:<\/p>\n<pre class=\"programlisting\">&lt;p&gt;\n    &lt;label for=\"Name\"&gt;Your name:&lt;\/label&gt;\n    &lt;input type=\"text\" id=\"Name\" name=\"Name\" value=\"\"&gt;\n&lt;\/p&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-for<\/code> attribute on the <code class=\"fm-code-in-text\">label<\/code> element sets the value of the <code class=\"fm-code-in-text\">for<\/code> attribute. The <code class=\"fm-code-in-text\">asp-for<\/code> attribute on the <code class=\"fm-code-in-text\">input<\/code> element sets the <code class=\"fm-code-in-text\">id<\/code> and <code class=\"fm-code-in-text\">name<\/code> elements. This may not look especially useful, but you will see that associating elements with a model property offers additional advantages as the application functionality is defined.<\/p>\n<p class=\"body\">Of more immediate use is the <code class=\"fm-code-in-text\">asp-action<\/code> attribute applied to the <code class=\"fm-code-in-text\">form<\/code> element, which uses the application\u2019s URL routing configuration to set the <code class=\"fm-code-in-text\">action<\/code> attribute to a URL that will target a specific action method, like this:<\/p>\n<pre class=\"programlisting\">&lt;form method=\"post\" action=\"\/Home\/RsvpForm\"&gt;<\/pre>\n<p class=\"body\">As with the helper attribute I applied to the <code class=\"fm-code-in-text\">a<\/code> element, the benefit of this approach is that when you change the system of URLs that the application uses, the content generated by the tag helpers will reflect the changes automatically.<\/p>\n<p class=\"body\">Use the browser to request http:\/\/localhost:5000 and click the RSVP Now link to see the form, as shown in figure 3.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre28\" src=\"\/images\/proaspnetcore7\/000022.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.4 Adding an HTML form to the application<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-50\">3.2.6 Receiving form data<\/h3>\n<p class=\"body\">I have not yet told ASP.NET Core what I want to do when the form is posted to the server. As things stand, clicking the Submit RSVP button just clears any values you have entered in the form. That is because the form posts back to the <code class=\"fm-code-in-text\">RsvpForm<\/code> action method in the <code class=\"fm-code-in-text\">Home<\/code> controller, which just renders the view again. To receive and process submitted form data, I am going to use an important feature of controllers. I will add a second <code class=\"fm-code-in-text\">RsvpForm<\/code> action method to create the following:<\/p>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">A method that responds to HTTP GET requests: A GET request is what a browser issues normally each time someone clicks a link. This version of the action will be responsible for displaying the initial blank form when someone first visits <code class=\"fm-code-in-text\">\/Home\/RsvpForm<\/code>.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">A method that responds to HTTP POST requests: The form element defined in listing 3.10 sets the method attribute to post, which causes the form data to be sent to the server as a POST request. This version of the action will be responsible for receiving submitted data and deciding what to do with it.<\/p>\n<\/li>\n<\/ul>\n<p class=\"body\">Handling <code class=\"fm-code-in-text\">GET<\/code> and <code class=\"fm-code-in-text\">POST<\/code> requests in separate C# methods helps to keep my controller code tidy since the two methods have different responsibilities. Both action methods are invoked by the same URL, but ASP.NET Core makes sure that the appropriate method is called, based on whether I am dealing with a <code class=\"fm-code-in-text\">GET<\/code> or <code class=\"fm-code-in-text\">POST<\/code> request. Listing 3.11 shows the changes to the <code class=\"fm-code-in-text\">HomeController<\/code> class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.11 Adding a method in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n<b class=\"fm-bold\">using PartyInvites.Models;<\/b>\n\nnamespace PartyInvites.Controllers {\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View();\n        }\n \n        <b class=\"fm-bold\">[HttpGet]<\/b>\n        public ViewResult RsvpForm() {\n            return View();\n        }\n \n        <b class=\"fm-bold\">[HttpPost]<\/b>\n        <b class=\"fm-bold\">public ViewResult RsvpForm(GuestResponse guestResponse) {<\/b>\n            <b class=\"fm-bold\">\/\/ TODO: store response from guest<\/b>\n            <b class=\"fm-bold\">return View();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">I have added the <code class=\"fm-code-in-text\">HttpGet<\/code> attribute to the existing <code class=\"fm-code-in-text\">RsvpForm<\/code> action method, which declares that this method should be used only for <code class=\"fm-code-in-text\">GET<\/code> requests. I then added an overloaded version of the <code class=\"fm-code-in-text\">RsvpForm<\/code> method, which accepts a <code class=\"fm-code-in-text\">GuestResponse<\/code> object. I applied the <code class=\"fm-code-in-text\">HttpPost<\/code> attribute to this method, which declares it will deal with <code class=\"fm-code-in-text\">POST<\/code> requests. I explain how these additions to the listing work in the following sections. I also imported the <code class=\"fm-code-in-text\">PartyInvites.Models<\/code> namespace&mdash;this is just so I can refer to the <code class=\"fm-code-in-text\">GuestResponse<\/code> model type without needing to qualify the class name.<\/p>\n<p class=\"fm-head2\">Understanding model binding<\/p>\n<p class=\"body\">The first overload of the <code class=\"fm-code-in-text\">RsvpForm<\/code> action method renders the same view as before&mdash;the <code class=\"fm-code-in-text\">RsvpForm.cshtml<\/code> file&mdash;to generate the form shown in figure 3.4. The second overload is more interesting because of the parameter, but given that the action method will be invoked in response to an HTTP <code class=\"fm-code-in-text\">POST<\/code> request and that the <code class=\"fm-code-in-text\">GuestResponse<\/code> type is a C# class, how are the two connected?<\/p>\n<p class=\"body\">The answer is <i class=\"fm-italics\">model binding<\/i>, a useful ASP.NET Core feature whereby incoming data is parsed and the key-<a id=\"calibre_link-1370\"><\/a>value pairs in the HTTP request are used to populate properties of domain model types.<\/p>\n<p class=\"body\">Model binding is a powerful and customizable feature that eliminates the grind of dealing with HTTP requests directly and lets you work with C# objects rather than dealing with individual data values sent by the browser. The <code class=\"fm-code-in-text\">GuestResponse<\/code> object that is passed as the parameter to the action method is automatically populated with the data from the form fields. I dive into the details of model binding in chapter 28.<\/p>\n<p class=\"body\">To demonstrate how model binding works, I need to do some preparatory work. One of the application goals is to present a summary page with details of who is attending the party, which means that I need to keep track of the responses that I receive. I am going to do this by creating an in-memory collection of objects. This isn\u2019t useful in a real application because the response data will be lost when the application is stopped or restarted, but this approach will allow me to keep the focus on ASP.NET Core and create an application that can easily be reset to its initial state. Later chapters will demonstrate persistent data storage.<\/p>\n<p class=\"body\">Add a class file named <code class=\"fm-code-in-text\">Repository.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the class shown in listing 3.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.12 The contents of the Repository.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace PartyInvites.Models {\n    public static class Repository {\n        private static List&lt;GuestResponse&gt; responses = new();\n \n        public static IEnumerable&lt;GuestResponse&gt; Responses =&gt; responses;\n \n        public static void AddResponse(GuestResponse response) {\n            Console.WriteLine(response);\n            responses.Add(response);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Repository<\/code> class and its members are <code class=\"fm-code-in-text\">static<\/code>, which will make it easy for me to store and retrieve data from different places in the application. ASP.NET Core provides a more sophisticated approach for defining common functionality, called <i class=\"fm-italics\">dependency injection<\/i>, which I describe in chapter 14, but a static class is a good way to get started for a simple application like this one.<\/p>\n<p class=\"body\">If you are using Visual Studio, saving the contents of the <code class=\"fm-code-in-text\">Repository.cs<\/code> file will trigger a warning produced by the <code class=\"fm-code-in-text\">dotnet watch<\/code> command telling you that a hot reload cannot be applied, which is the same warning described earlier in the chapter for Visual Studio Code users. You will see this prompt at the command line:<\/p>\n<pre class=\"programlisting\">watch : Do you want to restart your app \n    - Yes (y) \/ No (n) \/ Always (a) \/ Never (v)?<\/pre>\n<p class=\"body\">Press <code class=\"fm-code-in-text\">a<\/code> to always rebuild the project.<\/p>\n<p class=\"fm-head2\">Storing responses<\/p>\n<p class=\"body\">Now that I have somewhere to store the data, I can update the action method that receives the HTTP POST requests, as shown in listing 3.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.13 Updating an action in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing PartyInvites.Models;\n\nnamespace PartyInvites.Controllers {\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View();\n        }\n                \n        [HttpGet]\n        public ViewResult RsvpForm() {\n            return View();\n        }\n                \n        [HttpPost]\n        public ViewResult RsvpForm(GuestResponse guestResponse) {\n            <b class=\"fm-bold\">Repository.AddResponse(guestResponse);<\/b>\n            <b class=\"fm-bold\">return View(\"Thanks\", guestResponse);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">Before the POST version of the <code class=\"fm-code-in-text\">RsvpForm<\/code> method is invoked, the ASP.NET Core model binding feature extracts values from the HTML form and assigns them to the properties of the <code class=\"fm-code-in-text\">GuestResponse<\/code> object. The result is used as the argument when the method is invoked to handle the HTTP request, and all I have to do to deal with the form data sent in a request is to work with the <code class=\"fm-code-in-text\">GuestResponse<\/code> object that is passed to the action method&mdash;in this case, to pass it as an argument to the <code class=\"fm-code-in-text\">Repository.AddResponse<\/code> method so t hat the response can be stored.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-51\">3.2.7 Adding the Thanks view<\/h3>\n<p class=\"body\">The call to the <code class=\"fm-code-in-text\">View<\/code> method in the <code class=\"fm-code-in-text\">RsvpForm<\/code> action method creates a <code class=\"fm-code-in-text\">ViewResult<\/code> that selects a view called <code class=\"fm-code-in-text\">Thanks<\/code> and uses the <code class=\"fm-code-in-text\">GuestResponse<\/code> object created by the model binder as the view model. Add a Razor View named <code class=\"fm-code-in-text\">Thanks.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder with the content shown in listing 3.14 to present a response to the user.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.14 The contents of the Thanks.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model PartyInvites.Models.GuestResponse\n@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;Thanks&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div&gt;\n        &lt;h1&gt;Thank you, @Model?.Name!&lt;\/h1&gt;\n        @if (Model?.WillAttend == true) {\n            @:It's great that you're coming. \n            @:The drinks are already in the fridge!\n        } else {\n            @:Sorry to hear that you can't make it, \n            @:but thanks for letting us know.\n        }\n    &lt;\/div&gt;\n    Click &lt;a asp-action=\"ListResponses\"&gt;here&lt;\/a&gt; to see who is coming.\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The HTML produced by the <code class=\"fm-code-in-text\">Thanks.cshtml<\/code> view depends on the values assigned to the <code class=\"fm-code-in-text\">GuestResponse<\/code> view model provided by the <code class=\"fm-code-in-text\">RsvpForm<\/code> action method. To access the value of a property in the domain object, I use an <code class=\"fm-code-in-text\">@Model.&lt;PropertyName&gt;<\/code> expression. So, for example, to get the value of the <code class=\"fm-code-in-text\">Name<\/code> property, I use the <code class=\"fm-code-in-text\">@Model.Name<\/code> expression. Don\u2019t worry if the Razor syntax doesn\u2019t make sense&mdash;I explain it in more detail in chapter 21.<\/p>\n<p class=\"body\">Now that I have created the <code class=\"fm-code-in-text\">Thanks<\/code> view, I have a basic working example of handling a form. Use the browser to request http:\/\/localhost:5000, click the RSVP Now link, add some data to the form, and click the Submit RSVP button. You will see the response shown in figure 3.5 (although it will differ if your name is not Joe or you said you could not attend).<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre29\" src=\"\/images\/proaspnetcore7\/000023.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.5 The Thanks view<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-52\">3.2.8 Displaying responses<\/h3>\n<p class=\"body\">At the end of the <code class=\"fm-code-in-text\">Thanks.cshtml<\/code> view, I added an <code class=\"fm-code-in-text\">a<\/code> element to create a link to display the list of people who are coming to the party. I used the <code class=\"fm-code-in-text\">asp-action<\/code> tag helper attribute to create a URL that targets an action method called <code class=\"fm-code-in-text\">ListResponses<\/code>, like this:<\/p>\n<pre class=\"programlisting\">...\nClick &lt;a <b class=\"fm-bold\">asp-action=\"ListResponses\"<\/b>&gt;here&lt;\/a&gt; to see who is coming.\n...<\/pre>\n<p class=\"body\">If you hover the mouse over the link that is displayed by the browser, you will see that it targets the <code class=\"fm-code-in-text\">\/Home\/ListResponses<\/code> URL. This doesn\u2019t correspond to any of the action methods in the <code class=\"fm-code-in-text\">Home<\/code> controller, and if you click the link, you will see a 404 Not Found error response.<\/p>\n<p class=\"body\">To add an endpoint that will handle the URL, I need to add another action method to the <code class=\"fm-code-in-text\">Home<\/code> controller, as shown in listing 3.15.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.15 Adding an action in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing PartyInvites.Models;\n\nnamespace PartyInvites.Controllers {\n    public class HomeController : Controller {\n \n        public IActionResult Index() {\n            return View();\n        }\n \n        [HttpGet]\n        public ViewResult RsvpForm() {\n            return View();\n        }\n \n        [HttpPost]\n        public ViewResult RsvpForm(GuestResponse guestResponse) {\n            Repository.AddResponse(guestResponse);\n            return View(\"Thanks\", guestResponse);\n        }\n \n        <b class=\"fm-bold\">public ViewResult ListResponses() {<\/b>\n            <b class=\"fm-bold\">return View(Repository.Responses<\/b>\n                <b class=\"fm-bold\">.Where(r =&gt; r.WillAttend == true));<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The new action method is called <code class=\"fm-code-in-text\">ListResponses<\/code>, and it calls the <code class=\"fm-code-in-text\">View<\/code> method, using the <code class=\"fm-code-in-text\">Repository.Responses<\/code> property as the argument. This will cause Razor to render the default view, using the action method name as the name of the view file, and to use the data from the repository as the view model. The view model data is filtered using LINQ so that only positive responses are provided to the view.<\/p>\n<p class=\"body\">Add a Razor View named <code class=\"fm-code-in-text\">ListResponses.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder with the content shown in listing 3.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.16 Displaying data in the ListResponses.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;PartyInvites.Models.GuestResponse&gt;\n@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;Responses&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h2&gt;Here is the list of people attending the party&lt;\/h2&gt;\n    &lt;table&gt;\n        &lt;thead&gt;\n            &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Email&lt;\/th&gt;&lt;th&gt;Phone&lt;\/th&gt;&lt;\/tr&gt;\n        &lt;\/thead&gt;\n        &lt;tbody&gt;\n            @foreach (PartyInvites.Models.GuestResponse r in Model!) {\n                &lt;tr&gt;\n                    &lt;td&gt;@r.Name&lt;\/td&gt;\n                    &lt;td&gt;@r.Email&lt;\/td&gt;\n                    &lt;td&gt;@r.Phone&lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Razor view files have the <code class=\"fm-code-in-text\">.cshtml<\/code> file extension to denote a mix of C# code and HTML elements. You can see this in listing 3.16 where I have used an <code class=\"fm-code-in-text\">@foreach<\/code> expression to process each of the <code class=\"fm-code-in-text\">GuestResponse<\/code> objects that the action method passes to the view using the <code class=\"fm-code-in-text\">View<\/code> method. Unlike a normal C# <code class=\"fm-code-in-text\">foreach<\/code> loop, the body of a Razor <code class=\"fm-code-in-text\">@foreach<\/code> expression contains HTML elements that are added to the response that will be sent back to the browser. In this view, each <code class=\"fm-code-in-text\">GuestResponse<\/code> object generates a <code class=\"fm-code-in-text\">tr<\/code> element that contains <code class=\"fm-code-in-text\">td<\/code> elements populated with the value of an object property.<\/p>\n<p class=\"body\">Use the browser to request http:\/\/localhost:5000, click the RSVP Now link, and fill in the form. Submit the form and then click the link to see a summary of the data that has been entered since the application was first started, as shown in figure 3.6. The view does not present the data in an appealing way, but it is enough for the moment, and I will address the styling of the application later in this chapter.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre30\" src=\"\/images\/proaspnetcore7\/000024.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.6 Showing a list of party attendees<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-53\">3.2.9 Adding validation<\/h3>\n<p class=\"body\">I can now add data validation to the application. Without validation, users could enter nonsense data or even submit an empty form. In an ASP.NET Core application, validation rules are defined by applying attributes to model classes, which means the same validation rules can be applied in any form that uses that class. ASP.NET Core relies on attributes from the <code class=\"fm-code-in-text\">System.ComponentModel.DataAnnotations<\/code> namespace, which I have applied to the <code class=\"fm-code-in-text\">GuestResponse<\/code> class in listing 3.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.17 Applying validation in the GuestResponse.cs file in the Models folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using System.ComponentModel.DataAnnotations;<\/b>\n\nnamespace PartyInvites.Models {\n\n    public class GuestResponse {\n \n        <b class=\"fm-bold\">[Required(ErrorMessage = \"Please enter your name\")]<\/b>\n        public string? Name { get; set; }\n \n        <b class=\"fm-bold\">[Required(ErrorMessage = \"Please enter your email address\")]<\/b>\n        <b class=\"fm-bold\">[EmailAddress]<\/b>\n        public string? Email { get; set; }\n \n        <b class=\"fm-bold\">[Required(ErrorMessage = \"Please enter your phone number\")]<\/b>\n        public string? Phone { get; set; }\n \n        <b class=\"fm-bold\">[Required(ErrorMessage = \"Please specify whether you'll attend\")]<\/b>\n        public bool? WillAttend { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">ASP.NET Core detects the attributes and uses them to validate data during the model-binding process.<\/p>\n<p class=\"body\">As noted earlier, I used nullable types to define the <code class=\"fm-code-in-text\">GuestResponse<\/code> properties. This is useful for denoting properties that may not be assigned values, but it has a special value for the <code class=\"fm-code-in-text\">WillAttend<\/code> property because it allows the <code class=\"fm-code-in-text\">Required<\/code> validation attribute to work. If I had used a regular non-nullable <code class=\"fm-code-in-text\">bool<\/code>, the value I received through modelbinding could be only <code class=\"fm-code-in-text\">true<\/code> or <code class=\"fm-code-in-text\">false<\/code>, and I would not be able to tell whether the user had selected a value. A nullable <code class=\"fm-code-in-text\">bool<\/code> has three possible values: <code class=\"fm-code-in-text\">true<\/code>, <code class=\"fm-code-in-text\">false<\/code>, and <code class=\"fm-code-in-text\">null<\/code>. The value of the <code class=\"fm-code-in-text\">WillAttend<\/code> property will be <code class=\"fm-code-in-text\">null<\/code> if the user has not selected a value, and this causes the <code class=\"fm-code-in-text\">Required<\/code> attribute to report a validation error. This is a nice example of how ASP.NET Core elegantly blends C# features with HTML and HTTP.<\/p>\n<p class=\"body\">I check to see whether there has been a validation problem using the <code class=\"fm-code-in-text\">ModelState.IsValid<\/code> property in the action method that receives the form data, as shown in listing 3.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.18 Checking for errors in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing PartyInvites.Models;\n\nnamespace PartyInvites.Controllers {\n    public class HomeController : Controller {\n \n        public IActionResult Index() {\n            return View();\n        }\n \n        [HttpGet]\n        public ViewResult RsvpForm() {\n            return View();\n        }\n \n        [HttpPost]\n        public ViewResult RsvpForm(GuestResponse guestResponse) {\n            <b class=\"fm-bold\">if (ModelState.IsValid) {<\/b>\n                <b class=\"fm-bold\">Repository.AddResponse(guestResponse);<\/b>\n                <b class=\"fm-bold\">return View(\"Thanks\", guestResponse);<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return View();<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        }\n \n        public ViewResult ListResponses() {\n            return View(Repository.Responses\n                .Where(r =&gt; r.WillAttend == true));\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Controller<\/code> base class provides a property called <code class=\"fm-code-in-text\">ModelState<\/code> that provides details of the outcome of the model binding process. If the <code class=\"fm-code-in-text\">ModelState.IsValid<\/code> property returns <code class=\"fm-code-in-text\">true<\/code>, then I know that the model binder has been able to satisfy the validation constraints I specified through the attributes on the <code class=\"fm-code-in-text\">GuestResponse<\/code> class. When this happens, I render the <code class=\"fm-code-in-text\">Thanks<\/code> view, just as I did previously.<\/p>\n<p class=\"body\">If the <code class=\"fm-code-in-text\">ModelState.IsValid<\/code> property returns <code class=\"fm-code-in-text\">false<\/code>, then I know that there are validation errors. The object returned by the <code class=\"fm-code-in-text\">ModelState<\/code> property provides details of each problem that has been encountered, but I don\u2019t need to get into that level of detail because I can rely on a useful feature that automates the process of asking the user to address any problems by calling the <code class=\"fm-code-in-text\">View<\/code> method without any parameters.<\/p>\n<p class=\"body\">When it renders a view, Razor has access to the details of any validation errors associated with the request, and tag helpers can access the details to display validation errors to the user. Listing 3.19 shows the addition of validation tag helper attributes to the <code class=\"fm-code-in-text\">RsvpForm<\/code> view.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.19 Adding a summary to the RsvpForm.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model PartyInvites.Models.GuestResponse\n@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;RsvpForm&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;form asp-action=\"RsvpForm\" method=\"post\"&gt;\n        <b class=\"fm-bold\">&lt;div asp-validation-summary=\"All\"&gt;&lt;\/div&gt;<\/b>\n        &lt;div&gt;\n            &lt;label asp-for=\"Name\"&gt;Your name:&lt;\/label&gt;\n            &lt;input asp-for=\"Name\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div&gt;\n            &lt;label asp-for=\"Email\"&gt;Your email:&lt;\/label&gt;\n            &lt;input asp-for=\"Email\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div&gt;\n            &lt;label asp-for=\"Phone\"&gt;Your phone:&lt;\/label&gt;\n            &lt;input asp-for=\"Phone\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div&gt;\n            &lt;label asp-for=\"WillAttend\"&gt;Will you attend?&lt;\/label&gt;\n            &lt;select asp-for=\"WillAttend\"&gt;\n                &lt;option value=\"\"&gt;Choose an option&lt;\/option&gt;\n                &lt;option value=\"true\"&gt;Yes, I'll be there&lt;\/option&gt;\n                &lt;option value=\"false\"&gt;No, I can't come&lt;\/option&gt;\n            &lt;\/select&gt;\n        &lt;\/div&gt;\n        &lt;button type=\"submit\"&gt;Submit RSVP&lt;\/button&gt;\n    &lt;\/form&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-validation-summary<\/code> attribute is applied to a <code class=\"fm-code-in-text\">div<\/code> element, and it displays a list of validation errors when the view is rendered. The value for the <code class=\"fm-code-in-text\">asp-validation-summary<\/code> attribute is a value from an enumeration called <code class=\"fm-code-in-text\">ValidationSummary<\/code>, which specifies what types of validation errors the summary will contain. I specified <code class=\"fm-code-in-text\">All<\/code>, which is a good starting point for most applications, and I describe the other values and explain how they work in chapter 29.<\/p>\n<p class=\"body\">To see how the validation summary works, run the application, fill out the <code class=\"fm-code-in-text\">Name<\/code> field, and submit the form without entering any other data. You will see a summary of validation errors, as shown in figure 3.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre31\" src=\"\/images\/proaspnetcore7\/000025.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.7 Displaying validation errors<\/p>\n<\/div>\n<p class=\"body\">The <code class=\"fm-code-in-text\">RsvpForm<\/code> action method will not render the <code class=\"fm-code-in-text\">Thanks<\/code> view until all the validation constraints applied to the <code class=\"fm-code-in-text\">GuestResponse<\/code> class have been satisfied. Notice that the data entered in the <code class=\"fm-code-in-text\">Name<\/code> field was preserved and displayed again when Razor rendered the view with the validation summary. This is another benefit of model binding, and it simplifies working with form data.<\/p>\n<p class=\"fm-head2\">Highlighting invalid fields<\/p>\n<p class=\"body\">The tag helper attributes that associate model properties with elements have a handy feature that can be used in conjunction with model binding. When a model class property has failed validation, the helper attributes will generate slightly different HTML. Here is the <code class=\"fm-code-in-text\">input<\/code> element that is generated for the Phone field when there is no validation error:<\/p>\n<pre class=\"programlisting\">&lt;input type=\"text\" data-val=\"true\"\n     data-val-required=\"Please enter your phone number\" id=\"Phone\"\n     name=\"Phone\" value=\"\"&gt;<\/pre>\n<p class=\"body\">For comparison, here is the same HTML element after the user has submitted the form without entering data into the text field (which is a validation error because I applied the <code class=\"fm-code-in-text\">Required<\/code> attribute to the <code class=\"fm-code-in-text\">Phone<\/code> property of the <code class=\"fm-code-in-text\">GuestResponse<\/code> class):<\/p>\n<pre class=\"programlisting\">&lt;input type=\"text\" <b class=\"fm-bold\">class=\"input-validation-error\"<\/b>\n     data-val=\"true\" data-val-required=\"Please enter your phone number\" id=\"Phone\"\n     name=\"Phone\" value=\"\"&gt;<\/pre>\n<p class=\"body\">I have highlighted the difference: the <code class=\"fm-code-in-text\">asp-for<\/code> tag helper attribute added the <code class=\"fm-code-in-text\">input<\/code> element to a class called <code class=\"fm-code-in-text\">input-validation-error<\/code>. I can take advantage of this feature by creating a stylesheet that contains CSS styles for this class and the others that different HTML helper attributes use.<\/p>\n<p class=\"body\">The convention in ASP.NET Core projects is that static content is placed into the <code class=\"fm-code-in-text\">wwwroot<\/code> folder and organized by content type so that CSS stylesheets go into the <code class=\"fm-code-in-text\">wwwroot\/css<\/code> folder, JavaScript files go into the <code class=\"fm-code-in-text\">wwwroot\/js<\/code> folder, and so on.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The project template used in listing 3.1 creates a <code class=\"fm-code-in-text1\">site.css<\/code> file in the <code class=\"fm-code-in-text1\">wwwroot\/css<\/code> folder. You can ignore this file, which I don\u2019t use in this chapter.<\/p>\n<p class=\"body\">If you are using Visual Studio, right-click the <code class=\"fm-code-in-text\">wwwroot\/css<\/code> folder and select Add &gt; New Item from the pop-up menu. Locate the Style Sheet item template, as shown in figure 3.8; set the name of the file to <code class=\"fm-code-in-text\">styles.css<\/code>; and click the Add button.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre32\" src=\"\/images\/proaspnetcore7\/000026.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.8 Creating a CSS stylesheet<\/p>\n<\/div>\n<p class=\"body\">If you are using Visual Studio Code, right-click the <code class=\"fm-code-in-text\">wwwroot\/css<\/code> folder, select New File from the pop-up menu, and use <code class=\"fm-code-in-text\">styles.css<\/code> as the file name. Regardless of which editor you use, replace the contents of the file with the styles shown in listing 3.20.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.20 The contents of the styles.css file in the wwwroot\/css folder<\/p>\n<pre class=\"programlisting\">.field-validation-error {\n    color: #f00;\n}\n\n.field-validation-valid {\n    display: none;\n}\n\n.input-validation-error {\n    border: 1px solid #f00;\n    background-color: #fee;\n}\n\n.validation-summary-errors {\n    font-weight: bold;\n    color: #f00;\n}\n\n.validation-summary-valid {\n    display: none;\n}<\/pre>\n<p class=\"body\">To apply this stylesheet, I added a <code class=\"fm-code-in-text\">link<\/code> element to the <code class=\"fm-code-in-text\">head<\/code> section of the <code class=\"fm-code-in-text\">RsvpForm<\/code> view, as shown in listing 3.21.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.21 Applying a stylesheet in the RsvpForm.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">...\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;RsvpForm&lt;\/title&gt;\n    <b class=\"fm-bold\">&lt;link rel=\"stylesheet\" href=\"\/css\/styles.css\" \/&gt;<\/b>\n&lt;\/head&gt;\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">link<\/code> element uses the <code class=\"fm-code-in-text\">href<\/code> attribute to specify the location of the stylesheet. Notice that the <code class=\"fm-code-in-text\">wwwroot<\/code> folder is omitted from the URL. The default configuration for ASP.NET includes support for serving static content, such as images, CSS stylesheets, and JavaScript files, and it maps requests to the <code class=\"fm-code-in-text\">wwwroot<\/code> folder automatically. With the application of the stylesheet, a more obvious validation error will be displayed when data is submitted that causes a validation error, as shown in figure 3.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre33\" src=\"\/images\/proaspnetcore7\/000027.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.9 Automatically highlighted validation errors<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-54\">3.2.10 Styling the content<\/h3>\n<p class=\"body\">All the functional goals for the application are complete, but the overall appearance of the application is poor. When you create a project using the <code class=\"fm-code-in-text\">mvc<\/code> template, as I did for the example in this chapter, some common client-side development packages are installed. While I am not a fan of using template projects, I do like the client-side libraries that Microsoft has chosen. One of them is called Bootstrap, which is a good CSS framework originally developed by Twitter that has become a major open-source project and a mainstay of web application development.<\/p>\n<p class=\"fm-head2\">Styling the welcome view<\/p>\n<p class=\"body\">The basic Bootstrap features work by applying classes to elements that correspond to CSS selectors defined in the files added to the <code class=\"fm-code-in-text\">wwwroot\/lib\/bootstrap<\/code> folder. You can get full details of the classes that Bootstrap defines from <a class=\"url\" href=\"http:\/\/getbootstrap.com\">http:\/\/getbootstrap.com<\/a>, but you can see how I have applied some basic styling to the <code class=\"fm-code-in-text\">Index.cshtml<\/code> view file in listing 3.22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.22 Adding Bootstrap to the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    <b class=\"fm-bold\">&lt;link rel=\"stylesheet\" href=\"\/lib\/bootstrap\/dist\/css\/bootstrap.css\" \/&gt;<\/b>\n    &lt;title&gt;Index&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    <b class=\"fm-bold\">&lt;div class=\"text-center m-2\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;h3&gt; We're going to have an exciting party!&lt;\/h3&gt;<\/b>\n        <b class=\"fm-bold\">&lt;h4&gt;And YOU are invited!&lt;\/h4&gt;<\/b>\n        <b class=\"fm-bold\">&lt;a class=\"btn btn-primary\" asp-action=\"RsvpForm\"&gt;RSVP Now&lt;\/a&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">I have added a <code class=\"fm-code-in-text\">link<\/code> element whose <code class=\"fm-code-in-text\">href<\/code> attribute loads the <code class=\"fm-code-in-text\">bootstrap.css<\/code> file from the <code class=\"fm-code-in-text\">wwwroot\/lib\/bootstrap\/dist\/css<\/code> folder. The convention is that third-party CSS and JavaScript packages are installed into the <code class=\"fm-code-in-text\">wwwroot\/lib<\/code> folder, and I describe the tool that is used to manage these packages in chapter 4.<\/p>\n<p class=\"body\">Having imported the Bootstrap stylesheets, I need to style my elements. This is a simple example, so I need to use only a small number of Bootstrap CSS classes: <code class=\"fm-code-in-text\">text-center<\/code>, <code class=\"fm-code-in-text\">btn<\/code>, and <code class=\"fm-code-in-text\">btn-primary<\/code>.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">text-center<\/code> class centers the contents of an element and its children. The <code class=\"fm-code-in-text\">btn<\/code> class styles a <code class=\"fm-code-in-text\">button<\/code>, <code class=\"fm-code-in-text\">input<\/code>, or <code class=\"fm-code-in-text\">a<\/code> element as a pretty button, and the <code class=\"fm-code-in-text\">btn-primary class<\/code> specifies which of a range of colors I want the button to be. You can see the effect by running the application, as shown in figure 3.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre34\" src=\"\/images\/proaspnetcore7\/000028.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.10 Styling a view<\/p>\n<\/div>\n<p class=\"body\">It will be obvious to you that I am not a web designer. In fact, as a child, I was excused from art lessons on the basis that I had absolutely no talent whatsoever. This had the happy result of making more time for math lessons but meant that my artistic skills have not developed beyond those of the average 10-year-old. For a real project, I would seek a professional to help design and style the content, but for this example, I am going it alone, and that means applying Bootstrap with as much restraint and consistency as I can muster.<\/p>\n<p class=\"fm-head2\">Styling the form view<\/p>\n<p class=\"body\">Bootstrap defines classes that can be used to style forms. I am not going to go into detail, but you can see how I have applied these classes in listing 3.23.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.23 Adding styles to the RsvpForm.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model PartyInvites.Models.GuestResponse\n@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;RsvpForm&lt;\/title&gt;\n    <b class=\"fm-bold\">&lt;link rel=\"stylesheet\" href=\"\/lib\/bootstrap\/dist\/css\/bootstrap.css\" \/&gt;<\/b>\n    &lt;link rel=\"stylesheet\" href=\"\/css\/styles.css\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    <b class=\"fm-bold\">&lt;h5 class=\"bg-primary text-white text-center m-2 p-2\"&gt;RSVP&lt;\/h5&gt;<\/b>\n    <b class=\"fm-bold\">&lt;form asp-action=\"RsvpForm\" method=\"post\" class=\"m-2\"&gt;<\/b>\n        &lt;div asp-validation-summary=\"All\"&gt;&lt;\/div&gt;\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label asp-for=\"Name\" class=\"form-label\"&gt;Your name:&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;input asp-for=\"Name\" class=\"form-control\" \/&gt;<\/b>\n        &lt;\/div&gt;\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label asp-for=\"Email\" class=\"form-label\"&gt;Your email:&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;input asp-for=\"Email\"  class=\"form-control\" \/&gt;<\/b>\n        &lt;\/div&gt;\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label asp-for=\"Phone\" class=\"form-label\"&gt;Your phone:&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;input asp-for=\"Phone\" class=\"form-control\" \/&gt;<\/b>\n        &lt;\/div&gt;\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label asp-for=\"WillAttend\" class=\"form-label\"&gt;<\/b>\n                Will you attend?\n            &lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;select asp-for=\"WillAttend\" class=\"form-select\"&gt;<\/b>\n                &lt;option value=\"\"&gt;Choose an option&lt;\/option&gt;\n                &lt;option value=\"true\"&gt;Yes, I'll be there&lt;\/option&gt;\n                &lt;option value=\"false\"&gt;No, I can't come&lt;\/option&gt;\n            &lt;\/select&gt;\n        &lt;\/div&gt;\n        <b class=\"fm-bold\">&lt;button type=\"submit\" class=\"btn btn-primary mt-3\"&gt;<\/b>\n            <b class=\"fm-bold\">Submit RSVP<\/b>\n        <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n    &lt;\/form&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The Bootstrap classes in this example create a header, just to give structure to the layout. To style the form, I have used the <code class=\"fm-code-in-text\">form-group<\/code> class, which is used to style the element that contains the <code class=\"fm-code-in-text\">label<\/code> and the associated <code class=\"fm-code-in-text\">input<\/code> or <code class=\"fm-code-in-text\">select<\/code> element, which is assigned to the <code class=\"fm-code-in-text\">form-control<\/code> class. You can see the effect of the styles in figure 3.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre35\" src=\"\/images\/proaspnetcore7\/000029.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.11 Styling the RsvpForm view<\/p>\n<\/div>\n<p class=\"fm-head2\">Styling the thanks view<\/p>\n<p class=\"body\">The next view file to style is <code class=\"fm-code-in-text\">Thanks.cshtml<\/code>, and you can see how I have done this in listing 3.24, using CSS classes that are similar to the ones I used for the other views. To make an application easier to manage, it is a good principle to avoid duplicating code and markup wherever possible. ASP.NET Core provides several features to help reduce duplication, which I describe in later chapters. These features include Razor layouts (Chapter 22), partial views (Chapter 22), and view components (Chapter 24).<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.24 Applying styles to the Thanks.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model PartyInvites.Models.GuestResponse\n@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;Thanks&lt;\/title&gt;\n        <b class=\"fm-bold\">&lt;link rel=\"stylesheet\"<\/b> \n            <b class=\"fm-bold\">href=\"\/lib\/bootstrap\/dist\/css\/bootstrap.css\" \/&gt;<\/b>\n&lt;\/head&gt;\n<b class=\"fm-bold\">&lt;body class=\"text-center\"&gt;<\/b>\n    &lt;div&gt;\n        &lt;h1&gt;Thank you, @Model?.Name!&lt;\/h1&gt;\n        @if (Model?.WillAttend == true) {\n            @:It's great that you're coming. \n            @:The drinks are already in the fridge!\n        } else {\n            @:Sorry to hear that you can't make it, \n            @:but thanks for letting us know.\n        }\n    &lt;\/div&gt;\n    Click &lt;a asp-action=\"ListResponses\"&gt;here&lt;\/a&gt; to see who is coming.\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Figure 3.12 shows the effect of the styles.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre36\" src=\"\/images\/proaspnetcore7\/000030.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.12 Styling the Thanks view<\/p>\n<\/div>\n<p class=\"fm-head2\">Styling the list view<\/p>\n<p class=\"body\">The final view to style is <code class=\"fm-code-in-text\">ListResponses<\/code>, which presents the list of attendees. Styling the content follows the same approach as used for the other views, as shown in listing 3.25.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 3.25 Adding styles to the ListResponses.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;PartyInvites.Models.GuestResponse&gt;\n@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;Responses&lt;\/title&gt;\n    <b class=\"fm-bold\">&lt;link rel=\"stylesheet\" href=\"\/lib\/bootstrap\/dist\/css\/bootstrap.css\" \/&gt;<\/b>\n&lt;\/head&gt;\n&lt;body&gt;\n    <b class=\"fm-bold\">&lt;div class=\"text-center p-2\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;h2 class=\"text-center\"&gt;<\/b>\n            Here is the list of people attending the party\n        &lt;\/h2&gt;\n        <b class=\"fm-bold\">&lt;table class=\"table table-bordered table-striped table-sm\"&gt;<\/b>\n            &lt;thead&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Email&lt;\/th&gt;&lt;th&gt;Phone&lt;\/th&gt;&lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n                @foreach (PartyInvites.Models.GuestResponse r in Model!) {\n                    &lt;tr&gt;\n                        &lt;td&gt;@r.Name&lt;\/td&gt;\n                        &lt;td&gt;@r.Email&lt;\/td&gt;\n                        &lt;td&gt;@r.Phone&lt;\/td&gt;\n                    &lt;\/tr&gt;\n                }\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Figure 3.13 shows the way that the table of attendees is presented. Adding these styles to the view completes the example application, which now meets all the development goals and has an improved appearance.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre37\" src=\"\/images\/proaspnetcore7\/000031.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 3.13 Styling the ListResponses view<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-55\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core projects are created with the <code class=\"fm-code-in-text\">dotnet new<\/code> command.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Controllers define action methods that are used to handle HTTP requests.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Views generate HTML content that is used to respond to HTTP requests.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Views can contain HTML elements that are bound to data model properties.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Model binding is the process by which request data is parsed and assigned to the properties of objects that are passed to action methods for processing.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The data in the request can be subjected to validation and errors can be displayed to the user within the same HTML form that was used to submit the data.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The HTML content generated by views can be styled using the same CSS features that are applied to static HTML content.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-56\">\n<div class=\"calibre1\" id=\"calibre_link-1371\">\n<h1 class=\"tochead\" id=\"calibre_link-1372\">4 Using the development tools<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using command-line tools to create an ASP.NET Core project<\/li>\n<li class=\"co-summary-bullet\">Adding code and content to a project<\/li>\n<li class=\"co-summary-bullet\">Building and running an ASP.NET Core project<\/li>\n<li class=\"co-summary-bullet\">Using the hot reload feature<\/li>\n<li class=\"co-summary-bullet\">Installing NuGet packages<\/li>\n<li class=\"co-summary-bullet\">Installing tool packages<\/li>\n<li class=\"co-summary-bullet\">Installing client-side packages<\/li>\n<li class=\"co-summary-bullet\">Using the debugger<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-1373\"><\/a>In this chapter, I introduce the tools that Microsoft provides for ASP.NET Core development and that are used throughout this book.<\/p>\n<p class=\"body\">Unlike earlier editions of this book, I rely on the command-line tools provided by the .NET SDK and additional tool packages that Microsoft publishes. In part, I have done this to help ensure you get the expected results from the examples but also because the command-line tools provide access to all the features required for ASP.NET Core development, regardless of which editor\/IDE you have chosen.<\/p>\n<p class=\"body\">Visual Studio&mdash;and, to a lesser extent, Visual Studio Code&mdash;offers access to some of the tools through user interfaces, which I describe in this chapter, but Visual Studio and Visual Studio Code don\u2019t support all the features that are required for ASP.NET Core development, so there are times that using the command line is inevitable.<\/p>\n<p class=\"body\">As ASP.NET Core has evolved, I have gradually moved to using just the command-line tools, except for when I need to use a debugger (although, as I explain later in the chapter, this is a rare requirement). Your preferences may differ, especially if you are used to working entirely within an IDE, but my suggestion is to give the command-line tools a go. They are simple, concise, and predictable, which cannot be said for all the equivalent functionality provided by Visual Studio and Visual Studio Code.<a id=\"calibre_link-1374\"><\/a> Table 4.1 provides a guide to the chapter.<a id=\"calibre_link-877\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 4.1 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1375\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a project<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">dotnet new<\/code> commands.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">1&ndash;3<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Building and running projects<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">dotnet build<\/code> and <code class=\"fm-code-in-text1\">dotnet run<\/code> commands.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">4&ndash;10<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Adding packages to a project<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">dotnet add package<\/code> command.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">11, 12<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Installing tool commands<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">dotnet tool<\/code> command.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14, 15<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Managing client-side packages<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">libman<\/code> command or the Visual Studio client-side package manager.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">16&ndash;19<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-57\">4.1 Creating ASP.NET Core projects<\/h2>\n<p class=\"body\">The .NET SDK includes a set of command-line tools for creating, managing, building, and running projects. Visual Studio provides integrated support for some of these tasks, but if you are using Visual Studio Code, then the command line is the only option.<\/p>\n<p class=\"body\">I use the command-line tools throughout this book because they are simple and concise. The Visual Studio integrated support is awkward and makes it easy to unintentionally create a project with the wrong configuration, as the volume of emails from confused readers of earlier editions of this book has demonstrated.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-58\">4.1.1 Creating a project using the command line<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">dotnet<\/code> command provides access to the .NET command-line features. The <code class=\"fm-code-in-text\">dotnet new<\/code> command is used to create a new project, configuration file, or solution file. To see the list of templates available for creating new items, open a PowerShell command prompt and run the command shown in listing 4.1.<a id=\"calibre_link-1376\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.1 Listing the .NET templates<\/p>\n<pre class=\"programlisting\">dotnet new --list<\/pre>\n<p class=\"body\">Each template has a short name that makes it easier to use. There are many templates available, but table 4.2 describes the ones that are most useful for creating ASP.NET Core projects.<a id=\"calibre_link-1377\"><\/a><a id=\"calibre_link-1091\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 4.2 Useful ASP.NET Core project templates<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1378\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">web<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates a project that is set up with the minimum code and content required for ASP.NET Core development. This is the template I use for most of the chapters in this book.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">mvc<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates an ASP.NET Core project configured to use the MVC Framework.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">webapp<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates an ASP.NET Core project configured to use Razor Pages.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">blazorserver<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates an ASP.NET Core project configured to use Blazor Server.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">angular<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates an ASP.NET Core project that contains client-side features using the Angular JavaScript framework.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">react<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates an ASP.NET Core project that contains client-side features using the React JavaScript framework.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">reactredux<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates an ASP.NET Core project that contains client-side features using the React JavaScript framework and the popular Redux library.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">There are also templates that create commonly required files used to configure projects, as described in table 4.3.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding the limitations of project templates<\/p>\n<p class=\"fm-sidebar-text\">The project templates described in table 4.2 are intended to help jump-start development by taking care of basic configuration settings and adding placeholder content.<a id=\"calibre_link-1379\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">These templates can give you a sense of rapid progress, but they contain assumptions about how a project should be configured and developed. If you don\u2019t understand the impact of those assumptions, you won\u2019t be able to get the results you require for the specific demands of your project.<\/p>\n<p class=\"fm-sidebar-text\">The <code class=\"fm-code-in-text1\">web<\/code> template creates a project with the minimum configuration required for ASP.NET Core development. This is the project template I use for most of the examples in this book so that I can explain how each feature is configured and how the features can be used together.<\/p>\n<p class=\"fm-sidebar-text\">Once you understand how ASP.NET Core works, the other project templates can be useful because you will know how to adapt them to your needs. But, while you are learning, I recommend sticking to the <code class=\"fm-code-in-text1\">web<\/code> template, even though it can take a little more effort to get results.<\/p>\n<\/div>\n<p class=\"fm-table-caption\">Table 4.3 The configuration item templates<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1380\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">globaljson<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template adds a <code class=\"fm-code-in-text1\">global.json<\/code> file to a project, specifying the version of .NET that will be used.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">sln<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates a solution file, which is used to group multiple projects and is commonly used by Visual Studio. The solution file is populated with the <code class=\"fm-code-in-text1\">dotnet sln add<\/code> command, as shown in listing 4.2.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">gitignore<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates a <code class=\"fm-code-in-text1\">.gitignore<\/code> file that excludes unwanted items from Git source control.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">To create a project, open a new PowerShell command prompt and run the commands shown in listing 4.2.<a id=\"calibre_link-1381\"><\/a><a id=\"calibre_link-1382\"><\/a><a id=\"calibre_link-1383\"><\/a><a id=\"calibre_link-1384\"><\/a><a id=\"calibre_link-1385\"><\/a><a id=\"calibre_link-1386\"><\/a><a id=\"calibre_link-878\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.2 Creating a new project<\/p>\n<pre class=\"programlisting\">dotnet new globaljson --sdk-version 7.0.100 --output MySolution\/MyProject\ndotnet new web --no-https --output MySolution\/MyPr<a id=\"calibre_link-1387\"><\/a>oject --framework net7.0\ndotnet new sln -o MySolution\ndotnet sln MySolution add MySolution\/MyProject <\/pre>\n<p class=\"body\">The first command creates a <code class=\"fm-code-in-text\">MySolution\/MyProject<\/code> folder that contains a <code class=\"fm-code-in-text\">global.json<\/code> file, which specifies that the project will use .NET version 7. The top-level folder, named <code class=\"fm-code-in-text\">MySolution<\/code>, is used to group multiple projects. The nested <code class=\"fm-code-in-text\">MyProject<\/code> folder will contain a single project.<\/p>\n<p class=\"body\">I use the <code class=\"fm-code-in-text\">globaljson<\/code> template to help ensure you get the expected results when following the examples in this book. Microsoft is good at ensuring backward compatibility with .NET releases, but breaking changes do occur, and it is a good idea to add a <code class=\"fm-code-in-text\">global.json<\/code> file to projects so that everyone in the development team is using the same version.<\/p>\n<p class=\"body\">The second command creates the project using the <code class=\"fm-code-in-text\">web<\/code> template, which I use for most of the examples in this book. As noted in table 4.3, this template creates a project with the minimum content required for ASP.NET Core development. Each template has its own set of arguments that influence the project that is created. The <code class=\"fm-code-in-text\">--no-https<\/code> argument creates a project without support for HTTPS. (I explain how to use HTTPS in chapter 16.) The <code class=\"fm-code-in-text\">--framework<\/code> argument selects the .NET runtime that will be used for the project.<\/p>\n<p class=\"body\">The other commands create a solution file that references the new project. Solution files are a convenient way of opening multiple related files at the same time. A <code class=\"fm-code-in-text\">MySolution.sln<\/code> file is created in the <code class=\"fm-code-in-text\">MySolution<\/code> folder, and opening this file in Visual Studio will load the project created with the web template. This is not essential, but it stops Visual Studio from prompting you to create the file when you exit the code editor.<\/p>\n<p class=\"fm-head2\">Opening the project<\/p>\n<p class=\"body\">To open the project, start Visual Studio, select Open a Project or Solution, and open the <code class=\"fm-code-in-text\">MySolution.sln<\/code> file in the <code class=\"fm-code-in-text\">MySolution<\/code> folder. Visual Studio will open the solution file, discover the reference to the project that was added by the final command in listing 4.2, and open the project as well.<a id=\"calibre_link-1388\"><\/a><\/p>\n<p class=\"body\">Visual Studio Code works differently. Start Visual Studio Code, select File &gt; Open Folder, and navigate to the <code class=\"fm-code-in-text\">MySolution<\/code> folder. Click Select Folder, and Visual Studio Code will open the project.<\/p>\n<p class=\"body\"><a id=\"calibre_link-1090\"><\/a>Although Visual Studio Code and Visual Studio are working with the same project, each displays the contents differently. Visual Studio Code shows you a simple list of files, ordered alphabetically, as shown on the left of figure 4.1. Visual Studio hides some files and nests others within related file items, as shown on the right of figure 4.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre38\" src=\"\/images\/proaspnetcore7\/000032.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 4.1 Opening a project in Visual Studio Code and Visual Studio<\/p>\n<\/div>\n<p class=\"body\">There are buttons at the top of the Visual Studio Solution Explorer that disable file nesting and show the hidden items in the project. When you open a project for the first time in Visual Studio Code, you may be prompted to add assets for building and debugging the project. Click the Yes button.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-59\">4.2 Adding code and content to projects<\/h2>\n<p class=\"body\">If you are using Visual Studio Code, then you add items to the project by right-clicking the folder that should contain the file and selecting New File from the pop-up menu (or selecting New Folder if you are adding a folder).<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> You are responsible for ensuring that the file extension matches the type of item you want to add; for example, an HTML file must be added with the <code class=\"fm-code-in-text1\">.xhtml<\/code> extension. I give the complete file name and the name of the containing folder for every item added to a project throughout this book, so you will always know exactly what files you need to add.<\/p>\n<p class=\"body\">Right-click the My Project item in the list of files and select New Folder from the pop-up menu. Set the name to <code class=\"fm-code-in-text\">wwwroot<\/code>, which is where static content is stored in ASP.NET Core projects. Press Enter, and a folder named <code class=\"fm-code-in-text\">wwwroot<\/code> will be added to the project. Right-click the new <code class=\"fm-code-in-text\">wwwroot<\/code> folder, select New File, and set the name to <code class=\"fm-code-in-text\">demo.xhtml<\/code>. Press Enter to create the HTML file and add the content shown in listing 4.3.<a id=\"calibre_link-1389\"><\/a><a id=\"calibre_link-1085\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.3 The contents of the demo.xhtml file in the wwwroot folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta charset=\"utf-8\" \/&gt;\n    &lt;title&gt;&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h3&gt;HTML File from MyProject&lt;\/h3&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Visual Studio provides a more comprehensive approach that can be helpful, but only when used selectively. To create a folder, right-click the MyProject item in the Solution Explorer and select Add &gt; New Folder from the pop-up menu. Set the name of the new item to <code class=\"fm-code-in-text\">wwwroot<\/code> and press Enter; Visual Studio will create the folder.<\/p>\n<p class=\"body\">Right-click the new wwwroot item in the Solution Explorer and select Add &gt; New Item from the pop-up menu. Visual Studio will present you with an extensive selection of templates for adding items to the project. These templates can be searched using the text field in the top-right corner of the window or filtered using the categories on the left of the window. The item template for an HTML file is named HTML Page, as shown in figure 4.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre39\" src=\"\/images\/proaspnetcore7\/000033.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 4.2 Adding an item to the example project<\/p>\n<\/div>\n<p class=\"body\">Enter <code class=\"fm-code-in-text\">demo.xhtml<\/code> in the Name field, click the Add button to create the new file, and replace the contents with the element shown in listing 4.3. (If you omit the file extension, Visual Studio will add it for you based on the item template you have selected. If you entered just <code class=\"fm-code-in-text\">demo<\/code> into the Name field when you created the file, Visual Studio would have created a file with the <code class=\"fm-code-in-text\">.xhtml<\/code> extension because you had selected the HTML Page item template.)<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-60\">4.2.1 Understanding item scaffolding<\/h3>\n<p class=\"body\">The item templates presented by Visual Studio can be useful, especially for C# classes where it sets the namespace and class name automatically. But Visual Studio also provides <i class=\"fm-italics\">scaffolded items<\/i>, which I recommend against using. The Add &gt; New Scaffolded Item leads to a selection of items that guide you through a process to add more complex items. Visual Studio will also offer individual scaffolded items based on the name of the folder that you are adding an item to. For example, if you right-click a folder named <code class=\"fm-code-in-text\">Views<\/code>, Visual Studio will helpfully add scaffolded items to the top of the menu, as shown in figure 4.3.<a id=\"calibre_link-1390\"><\/a><a id=\"calibre_link-1391\"><\/a><a id=\"calibre_link-1087\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre40\" src=\"\/images\/proaspnetcore7\/000034.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 4.3 Scaffolded items in the Add menu<\/p>\n<\/div>\n<p class=\"body\">The <code class=\"fm-code-in-text\">View<\/code> and <code class=\"fm-code-in-text\">Controller<\/code> items are scaffolded, and selecting them will present you with choices that determine the content of the items you create.<\/p>\n<p class=\"body\">Just like the project templates, I recommend against using scaffolded items, at least until you understand the content they create. In this book, I use only the Add &gt; New Item menu for the examples and change the placeholder content immediately.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-61\">4.3 Building and running projects<\/h2>\n<p class=\"body\">The simplest way to build and run a project is to use the command-line tools. To prepare, add the statement shown in listing 4.4 to the <code class=\"fm-code-in-text\">Program.cs<\/code> class file in the <code class=\"fm-code-in-text\">MyProject<\/code> folder.<a id=\"calibre_link-1392\"><\/a><a id=\"calibre_link-1393\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.4 Adding a statement in the Program.cs file in the MyProject folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\n<b class=\"fm-bold\">app.UseStaticFiles();<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">This statement adds support for responding to HTTP requests with static content in the <code class=\"fm-code-in-text\">wwwroot<\/code> folder, such as the HTML file created in the previous section. (I explain this feature in more detail in chapter 15.)<a id=\"calibre_link-1394\"><\/a><\/p>\n<p class=\"body\">Next, set the HTTP port that ASP.NET Core will use to receive HTTP requests, as shown in listing 4.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.5 Setting the HTTP port in the launchSettings.json file in the Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"MyProject\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<p class=\"body\">To build the example project, run the command shown in listing 4.6 in the <code class=\"fm-code-in-text\">MyProject<\/code> folder<a id=\"calibre_link-1395\"><\/a>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.6 Building the project<\/p>\n<pre class=\"programlisting\">dotnet build<\/pre>\n<p class=\"body\">You can build and run the project in a single step by running the command shown in listing 4.7 in the <code class=\"fm-code-in-text\">MyProject<\/code> folder.<a id=\"calibre_link-1396\"><\/a><a id=\"calibre_link-879\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.7 Building and running the project<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">The compiler will build the project and then start the integrated ASP.NET Core HTTP server to listen for HTTP requests on port 5000. You can see the contents of the static HTML file added to the project earlier in the chapter by opening a new browser window and requesting http:\/\/localhost:5000\/demo.xhtml, which produces the response shown in figure 4.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre41\" src=\"\/images\/proaspnetcore7\/000035.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 4.4 Running the example application<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-62\">4.3.1 Using the hot reload feature<\/h3>\n<p class=\"body\">.NET has an integrated hot reload feature, which compiles and applies updates to applications on the fly. For ASP.NET Core applications, this means that changes to the project are automatically reflected in the browser without having to manually stop the ASP.NET Core application and use the <code class=\"fm-code-in-text\">dotnet run<\/code> command. Use Control+C to stop ASP.NET Core if the application is still running from the previous section and run the command shown in listing 4.8 in the <code class=\"fm-code-in-text\">MyProject<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.8 Starting the application with hot reload<\/p>\n<pre class=\"programlisting\">dotnet watch<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">dotnet watch<\/code> command opens a new browser window, which it does to ensure that the browser loads a small piece of JavaScript that opens an HTTP connection to the server that is used to handle reloading. (The new browser window can be disabled by setting the <code class=\"fm-code-in-text\">launchBrowser<\/code> property shown in listing 4.5 to <code class=\"fm-code-in-text\">false<\/code>, but you will have to perform a manual reload the first time you start or restart ASP.NET Core.) Use the browser to request http:\/\/localhost:5000\/demo.xhtml, and you will see the output shown on the left of figure 4.5.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">dotnet watch<\/code> command monitors the project for changes. When a change is detected, the project is automatically recompiled, and the browser is reloaded. To see this process in action, make the change shown in listing 4.9 to the <code class=\"fm-code-in-text\">demo.xhtml<\/code> file in the <code class=\"fm-code-in-text\">wwwroot<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.9 Changing the message in the demo.xhtml file in the wwwroot folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta charset=\"utf-8\" \/&gt;\n    &lt;title&gt;&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    <b class=\"fm-bold\">&lt;h3&gt;New Message&lt;\/h3&gt;<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">When you save the changes to the HTML file, the <code class=\"fm-code-in-text\">dotnet watch<\/code> tool will detect the change and automatically update the browser, as shown in figure 4.5.<a id=\"calibre_link-1397\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre42\" src=\"\/images\/proaspnetcore7\/000036.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 4.5 The hot reload feature<\/p>\n<\/div>\n<p class=\"body\">The <code class=\"fm-code-in-text\">dotnet watch<\/code> command is a clever feat of engineering, and it has good support for ASP.NET Core applications, allowing changes to be easily applied. But not all changes can be handled with a hot reload.<\/p>\n<p class=\"body\">If you are using Visual Studio, right-click the MyProject item in the Solution Explorer, select Add &gt; Class from the pop-up menu, and set the name of the new class file to <code class=\"fm-code-in-text\">MyClass.cs<\/code>. When Visual Studio opens the file for editing, change the namespace as shown in listing 4.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.10 Changing a namespace in the MyClass.cs file in the MyProject folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">namespace MyProject.MyNamespace {<\/b>\n    public class MyClass {\n    }\n}<\/pre>\n<p class=\"body\">If you are using Visual Studio Code, add a file named <code class=\"fm-code-in-text\">MyClass.cs<\/code> to the <code class=\"fm-code-in-text\">MyProject<\/code> folder with the content shown in listing 4.10.<\/p>\n<p class=\"body\">Regardless of which editor you use, you will see output similar to the following when you save the class file:<\/p>\n<pre class=\"programlisting\">watch : File changed: C:\\MySolution\\MyProject\\MyClass.cs.\nwatch : Unable to apply hot reload because of a rude edit.<\/pre>\n<p class=\"body\">There are some changes that the <code class=\"fm-code-in-text\">dotnet watch<\/code> command can\u2019t handle with a hot reload and the application is restarted instead. You may be prompted to accept the restart. The restart has little effect on the example application, but it means that the application state is lost, which can be frustrating when working on real projects.<\/p>\n<p class=\"body\">But even though it isn\u2019t perfect, the hot reload feature is useful, especially when it comes to iterative adjustments to the HTML an application produces. I don\u2019t use it in most of the chapters in this book because the examples require many changes that are not handled with hot reloads and that can prevent changes from taking effect, but I do use it for my own non-book related development projects.<a id=\"calibre_link-869\"><\/a><\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-63\">4.4 Managing packages<\/h2>\n<p class=\"body\">Most projects require additional features beyond those set up by the project templates, such as support for accessing databases or for making HTTP requests, neither of which is included in the standard ASP.NET Core packages added to the project by the template used to create the example project. In the sections that follow, I describe the tools available to manage the different types of packages that are used in ASP.NET Core development.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-64\">4.4.1 Managing NuGet packages<\/h3>\n<p class=\"body\">.NET packages are added to a project with the <code class=\"fm-code-in-text\">dotnet add package<\/code> command. Use a PowerShell command prompt to run the command shown in listing 4.11 in the <code class=\"fm-code-in-text\">MyProject<\/code> folder to add a package to the example project.<a id=\"calibre_link-1398\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.11 Adding a package to the example project<\/p>\n<pre class=\"programlisting\">dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 7.0.0<\/pre>\n<p class=\"body\">This command installs version 7.0.0 of the <code class=\"fm-code-in-text\">Microsoft.EntityFrameworkCore.SqlServer<\/code> package. The package repository for .NET projects is <code class=\"fm-code-in-text\">nuget.org<\/code>, where you can search for the package and see the versions available. The package installed in listing 4.11, for example, is described at <a class=\"url\" href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.SqlServer\/7.0.0\">https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.SqlServer\/7.0.0<\/a>. You can see the packages installed in a project by running the command shown in listing 4.12<a id=\"calibre_link-1399\"><\/a><a id=\"calibre_link-1400\"><\/a>.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The project file&mdash;which is the file with the <code class=\"fm-code-in-text1\">.csproj<\/code> extension&mdash;is used to keep track of the packages added to a project. You can examine this file by opening it for editing in Visual Studio Code or by right-clicking the project item in the Visual Studio Solution Explorer and selecting Edit Project File from the pop-up menu.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.12 Listing the packages in a project<\/p>\n<pre class=\"programlisting\">dotnet list package <\/pre>\n<p class=\"body\">This command produces the following output when it is run in the <code class=\"fm-code-in-text\">MyProject<\/code> folder, showing the package added in listing 4.11:<\/p>\n<pre class=\"programlisting\">Project 'MyProject' has the following package references\n   [net7.0]:\n   Top-level Package                              Requested   Resolved\n   &gt; Microsoft.EntityFrameworkCore.SqlServer      7.0.0       7.0.0<\/pre>\n<p class=\"body\">Packages are removed with the <code class=\"fm-code-in-text\">dotnet remove package<\/code> command. To remove the package from the example project, run the command shown in listing 4.13 in the <code class=\"fm-code-in-text\">MyProject<\/code> folder.<a id=\"calibre_link-1401\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.13 Removing a package from the example project<\/p>\n<pre class=\"programlisting\">dotnet remove package Microsoft.EntityFrameworkCore.SqlServer<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-65\">4.4.2 Managing tool packages<\/h3>\n<p class=\"body\">Tool packages install commands that can be used from the command line to perform operations on .NET projects. One common example is the Entity Framework Core tools package that installs commands that are used to manage databases in ASP.NET Core projects. Tool packages are managed using the <code class=\"fm-code-in-text\">dotnet tool<\/code> command. To install the Entity Framework Core tools package, run the commands shown in listing 4.14.<a id=\"calibre_link-1402\"><\/a><a id=\"calibre_link-1403\"><\/a><a id=\"calibre_link-874\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.14 Installing a tool package<\/p>\n<pre class=\"programlisting\">dotnet tool uninstall --global dotnet-ef\ndotnet tool install --global dotnet-ef --version 7.0.0<\/pre>\n<p class=\"body\">The first command removes the <code class=\"fm-code-in-text\">dotnet-ef<\/code> package, which is named <code class=\"fm-code-in-text\">dotnet-ef<\/code>. This command will produce an error if the package has not already been installed, but it is a good idea to remove existing versions before installing a package. The <code class=\"fm-code-in-text\">dotnet tool install<\/code> command installs version 7.0.0 of the <code class=\"fm-code-in-text\">dotnet-ef<\/code> package, which is the version I use in this book. The commands installed by tool packages are used through the <code class=\"fm-code-in-text\">dotnet<\/code> command. To test the package installed in listing 4.14, run the command shown in listing 4.15 in the <code class=\"fm-code-in-text\">MyProject<\/code> folder.<a id=\"calibre_link-1404\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">--global<\/code> arguments in listing 4.14 mean the package is installed for global use and not just for a specific project. You can install tool packages into just one project, in which case the command is accessed with <code class=\"fm-code-in-text1\">dotnet tool run &lt;command&gt;<\/code>. The tools I use in this book are all installed globally.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.15 Running a tool package command<\/p>\n<pre class=\"programlisting\">dotnet ef --help<\/pre>\n<p class=\"body\">The commands added by this tool package are accessed using <code class=\"fm-code-in-text\">dotnet ef<\/code>, and you will see examples in later chapters that rely on these commands.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-66\">4.4.3 Managing client-side packages<\/h3>\n<p class=\"body\">Client-side packages contain content that is delivered to the client, such as images, CSS stylesheets, JavaScript files, and static HTML. Client-side packages are added to ASP.NET Core using the Library Manager (LibMan) tool. To install the LibMan tool package, run the commands shown in listing 4.16.<a id=\"calibre_link-1405\"><\/a><a id=\"calibre_link-1406\"><\/a><a id=\"calibre_link-1407\"><\/a><a id=\"calibre_link-1408\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.16 Installing the LibMan tool package<\/p>\n<pre class=\"programlisting\">dotnet tool uninstall --global Microsoft.Web.LibraryManager.Cli\ndotnet tool install --global Microsoft.Web.LibraryManager.Cli --version 2.1.175<\/pre>\n<p class=\"body\">These commands remove any existing LibMan package and install the version that is used throughout this book. The next step is to initialize the project, which creates the file that LibMan uses to keep track of the client packages it installs. Run the command shown in listing 4.17 in the <code class=\"fm-code-in-text\">MyProject<\/code> folder to initialize the example project.<a id=\"calibre_link-1409\"><\/a><a id=\"calibre_link-883\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.17 Initializing the example project<\/p>\n<pre class=\"programlisting\">libman init -p cdnjs<\/pre>\n<p class=\"body\">LibMan can download packages from different repositories. The <code class=\"fm-code-in-text\">-p<\/code> argument in listing 4.17 specifies the repository at <a class=\"url\" href=\"https:\/\/cdnjs.com\">https:\/\/cdnjs.com<\/a>, which is the most widely used. Once the project is initialized, client-side packages can be installed. To install the Bootstrap CSS framework that I use to style HTML content throughout this book, run the command shown in listing 4.18 in the <code class=\"fm-code-in-text\">MyProject<\/code> folder.<a id=\"calibre_link-1410\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.18 Installing the Bootstrap CSS framework<\/p>\n<pre class=\"programlisting\">libman install bootstrap@5.2.3 -d wwwroot\/lib\/bootstrap<\/pre>\n<p class=\"body\">The command installs version 5.2.3 of the Bootstrap package, which is known by the name <code class=\"fm-code-in-text\">bootstrap<\/code> on the CDNJS repository. The <code class=\"fm-code-in-text\">-d<\/code> argument specifies the location into which the package is installed. The convention in ASP.NET Core projects is to install client-side packages into the <code class=\"fm-code-in-text\">wwwroot\/lib<\/code> folder.<\/p>\n<p class=\"body\">Once the package has been installed, add the classes shown in listing 4.19 to the elements in the <code class=\"fm-code-in-text\">demo.xhtml<\/code> file. This is how the features provided by the Bootstrap package are applied.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> I don\u2019t get into the details of using the Bootstrap CSS framework in this book. See <a class=\"url\" href=\"https:\/\/getbootstrap.com\">https:\/\/getbootstrap.com<\/a> for the Bootstrap documentation.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 4.19 Applying Bootstrap classes in the demo.xhtml file in the wwwroot folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta charset=\"utf-8\" \/&gt;\n    &lt;title&gt;&lt;\/title&gt;\n    <b class=\"fm-bold\">&lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;<\/b>\n&lt;\/head&gt;\n&lt;body&gt;\n    <b class=\"fm-bold\">&lt;h3 class=\"bg-primary text-white text-center p-2\"&gt;New Message&lt;\/h3&gt;<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Start ASP.NET Core and request http:\/\/localhost:5000\/demo.xhtml, and you will see the styled content shown in figure 4.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre41\" src=\"\/images\/proaspnetcore7\/000037.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 4.6 Using a client-side package<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-67\">4.5 Debugging projects<\/h2>\n<p class=\"body\">Visual Studio and Visual Studio Code both provide debuggers that can be used to control and inspect the execution of an ASP.NET Core application. Open the <code class=\"fm-code-in-text\">Program.cs<\/code> file in the <code class=\"fm-code-in-text\">MyProject<\/code> folder, and click this statement in the code editor:<a id=\"calibre_link-1411\"><\/a><a id=\"calibre_link-1412\"><\/a><a id=\"calibre_link-902\"><\/a><\/p>\n<pre class=\"programlisting\">...\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n...<\/pre>\n<p class=\"body\">Select Debug &gt; Toggle Breakpoint in Visual Studio or select Run &gt; Toggle Breakpoint in Visual Studio Code. A breakpoint is shown as a red dot alongside the code statement, as shown in figure 4.7, and will interrupt execution and pass control to the user.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre43\" src=\"\/images\/proaspnetcore7\/000038.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 4.7 Setting a breakpoint<\/p>\n<\/div>\n<p class=\"body\">Start the project by selecting Debug &gt; Start Debugging in Visual Studio or selecting Run &gt; Start Debugging in Visual Studio Code. (Choose .NET if Visual Studio Code prompts you to select an environment and then select the Start Debugging menu item again.)<\/p>\n<p class=\"body\">The application will be started and continue normally until the statement to which the breakpoint is reached, at which point execution is halted. Execution can be controlled using the Debug or Run menu or the controls that Visual Studio and Visual Studio Code display. Both debuggers are packed with features&mdash;more so if you have a paid-for version of Visual Studio&mdash;and I don\u2019t describe them in depth in this book. The Visual Studio debugger is described at <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/debugger\">https:\/\/docs.microsoft.com\/en-us\/visualstudio\/debugger<\/a>, and the Visual Studio Code debugger is described at <a class=\"url\" href=\"https:\/\/code.visualstudio.com\/docs\/editor\/debugging\">https:\/\/code.visualstudio.com\/docs\/editor\/debugging<\/a>.<a id=\"calibre_link-1413\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">How I debug my code<\/p>\n<p class=\"fm-sidebar-text\">Debuggers are powerful tools, but I rarely use them. In most situations, I prefer to add <code class=\"fm-code-in-text1\">Console.WriteLine<\/code> statements to my code to figure out what is going on, which I can easily do because I use the <code class=\"fm-code-in-text1\">dotnet run<\/code> command to run my projects from the command line. This is a rudimentary approach that works for me, not least because most of the errors in my code tend to be where statements are not being called because a condition in an <code class=\"fm-code-in-text1\">if<\/code> statement isn\u2019t effective. If I want to examine an object in detail, I tend to serialize it to JSON and pass the result to the <code class=\"fm-code-in-text1\">WriteLine<\/code> method.<\/p>\n<p class=\"fm-sidebar-text\">This may seem like madness if you are a dedicated user of the debugger, but it has the advantage of being quick and simple. When I am trying to figure out why code isn\u2019t working, I want to explore and iterate quickly, and I find the amount of time taken to start the debugger to be a barrier. My approach is also reliable. The Visual Studio and Visual Studio Code debuggers are sophisticated, but they are not always entirely predictable, and .NET and ASP.NET Core change too quickly for the debugger features to have entirely settled down. When I am utterly confused by the behavior of some code, I want the simplest possible diagnostic tool, and that, for me, is a message written to the console.<\/p>\n<p class=\"fm-sidebar-text\">I am not suggesting that this is the approach you should use, but it can be a good place to start when you are not getting the results you expect and you don\u2019t want to battle with the debugger to figure out why.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-68\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core projects are created with the <code class=\"fm-code-in-text\">dotnet new<\/code> command.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">There are templates to jumpstart popular project types and to create common project items.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">dotnet build<\/code> command compiles a project.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">dotnet run<\/code> command builds and executes a project.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">dotnet watch<\/code> command builds and executes a project, and performs hot reloading when changes are detected.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Packages are added to a project with the <code class=\"fm-code-in-text\">dotnet add package<\/code> command.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Tool packages are installing using the <code class=\"fm-code-in-text\">dotnet tool install<\/code> command.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Client-side packages are managed with the <code class=\"fm-code-in-text\">libman<\/code> tool package.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-69\">\n<div class=\"calibre1\" id=\"calibre_link-1414\">\n<h1 class=\"tochead\" id=\"calibre_link-1415\"><a id=\"calibre_link-1416\"><\/a>5 Essential C# features<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using C# language features for ASP.NET Core development<\/li>\n<li class=\"co-summary-bullet\">Dealing with null values and the null state analysis feature<\/li>\n<li class=\"co-summary-bullet\">Creating objects concisely<\/li>\n<li class=\"co-summary-bullet\">Adding features to classes without directly modifying them<\/li>\n<li class=\"co-summary-bullet\">Expressing functions concisely<\/li>\n<li class=\"co-summary-bullet\">Modifying interfaces without breaking implementation classes<\/li>\n<li class=\"co-summary-bullet\">Defining asynchronous methods<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-1417\"><\/a>In this chapter, I describe C# features used in web application development that are not widely understood or that often cause confusion. This is not a book about C#, however, so I provide only a brief example for each feature so that you can follow the examples in the rest of the book and take advantage of these features in your projects. Table 5.1 provides a guide to this chapter.<\/p>\n<p class=\"fm-table-caption\"><a id=\"calibre_link-1418\"><\/a>Table 5.1 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1419\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Reducing duplication in <code class=\"fm-code-in-text1\">using<\/code> statements<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use global or implicit <code class=\"fm-code-in-text1\">using<\/code> statements.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">8&ndash;10<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Managing null values<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use nullable and non-nullable types, which are managed with the null management operators.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">11&ndash;20<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Mixing static and dynamic values in strings<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use string interpolation.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">21<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Initializing and populate objects<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the object and collection initializers and target-typed <code class=\"fm-code-in-text1\">new<\/code> expressions.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">22&ndash;26<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Assigning a value for specific types<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use pattern matching.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">27, 28<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Extending the functionality of a class without modifying it<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define an extension method.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">29&ndash;36<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Expressing functions and methods concisely<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use lambda expressions.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">37&ndash;44<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Defining a variable without explicitly declaring its type<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">var<\/code> keyword.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">45&ndash;47<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Modifying an interface without requiring changes in its implementation classes<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define a default implementation.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">48&ndash;52<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Performing work asynchronously<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use tasks or the <code class=\"fm-code-in-text1\">async<\/code>\/<code class=\"fm-code-in-text1\">await<\/code> keywords.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">53&ndash;55<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Producing a sequence of values over time<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use an asynchronous enumerable.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">56&ndash;59<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Getting the name of a class or member<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a <code class=\"fm-code-in-text1\">nameof<\/code> expression.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">60, 61<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-70\">5.1 Preparing for this chapter<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1420\"><\/a>To create the example project for this chapter, open a new PowerShell command prompt and run the commands shown in listing 5.1. If you are using Visual Studio and prefer not to use the command line, you can create the project using the process described in chapter 4.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.1 Creating the example project<\/p>\n<pre class=\"programlisting\">dotnet new globaljson --sdk-version 7.0.100 --output LanguageFeatures\ndotnet new web --no-https --output LanguageFeatures <a id=\"calibre_link-1421\"><\/a>--framework net7.0\ndotnet new sln -o LanguageFeatures\ndotnet sln LanguageFeatures add LanguageFeatures<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-71\">5.1.1 Opening the project<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1422\"><\/a>If you are using Visual Studio, select File &gt; Open &gt; Project\/Solution, select the <code class=\"fm-code-in-text\">LanguageFeatures.sln<\/code> file in the <code class=\"fm-code-in-text\">LanguageFeatures<\/code> folder, and click the Open button to open the solution file and the project it references. If you are using Visual Studio Code, select File &gt; Open Folder, navigate to the <code class=\"fm-code-in-text\">LanguageFeatures<\/code> folder, and click the Select Folder button.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-72\">5.1.2 Enabling the MVC Framework<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">web<\/code> project template creates a project that contains a minimal ASP.NET Core configuration. This means the placeholder content that is added by the <code class=\"fm-code-in-text\">mvc<\/code> template used in chapter 3 is not available and that extra steps are required to reach the point where the application can produce useful output. In this section, I make the changes required to set up the MVC Framework, which is one of the application frameworks supported by ASP.NET Core, as I explained in chapter 1. First, to enable the MVC framework, make the changes shown in listing 5.2 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.2 Enabling MVC in the Program.cs file in the LanguageFeatures folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddControllersWithViews();<\/b>\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">\/\/app.MapGet(\"\/\", () =&gt; \"Hello World!\");<\/b>\n<b class=\"fm-bold\">app.MapDefaultControllerRoute();<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">I explain how to configure ASP.NET Core applications in part 2, but the two statements added in listing 5.2 provide a basic MVC framework setup using a default configuration.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-73\">5.1.3 Creating the application components<\/h3>\n<p class=\"body\">Now that the MVC framework is set up, I can add the application components that I will use to demonstrate important C# language features. As you create these components, you will see that the code editor underlines some expressions to warn you of potential problems. These are safe to ignore until the \u201cUnderstanding Null State Analysis\u201d section, where I explain their significance.<\/p>\n<p class=\"fm-head2\">Creating the data model<\/p>\n<p class=\"body\">I started by creating a simple model class so that I can have some data to work with. I added a folder called <code class=\"fm-code-in-text\">Models<\/code> and created a class file called <code class=\"fm-code-in-text\">Product.cs<\/code> within it, which I used to define the class shown in listing 5.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.3 The contents of the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n    public class Product {\n \n        public string Name { get; set; }\n        public decimal? Price { get; set; }\n \n        public static Product[] GetProducts() {\n \n            Product kayak = new Product { \n                Name = \"Kayak\", Price = 275M \n            };\n \n            Product lifejacket = new Product { \n                Name = \"Lifejacket\", Price = 48.95M \n            };\n \n            return new Product[] { kayak, lifejacket, null };\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1423\"><\/a>The <code class=\"fm-code-in-text\">Product<\/code> class defines <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Price<\/code> properties, and there is a <code class=\"fm-code-in-text\">static<\/code> method called <code class=\"fm-code-in-text\">GetProducts<\/code> that returns a <code class=\"fm-code-in-text\">Product<\/code> array. One of the elements contained in the array returned by the <code class=\"fm-code-in-text\">GetProducts<\/code> method is set to <code class=\"fm-code-in-text\">null<\/code>, which I will use to demonstrate some useful language features later in the chapter.<\/p>\n<p class=\"body\">The Visual Studio and Visual Studio Code editors will highlight a problem with the <code class=\"fm-code-in-text\">Name<\/code> property. This is a deliberate error that I explain later in the chapter and which should be ignored for now.<\/p>\n<p class=\"fm-head2\">Creating the controller and view<\/p>\n<p class=\"body\">For the examples in this chapter, I use a simple controller class to demonstrate different language features. I created a <code class=\"fm-code-in-text\">Controllers<\/code> folder and added to it a class file called <code class=\"fm-code-in-text\">HomeController.cs<\/code>, the contents of which are shown in listing 5.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.4 The contents of the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n \n        public ViewResult Index() {\n            return View(new string[] { \"C#\", \"Language\", \"Features\" });\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Index<\/code> action method tells ASP.NET Core to render the default view and provides it with an array of strings as its view model, which will be included in the HTML sent to the client. To create the view, I added a <code class=\"fm-code-in-text\">Views\/Home<\/code> folder (by creating a <code class=\"fm-code-in-text\">Views<\/code> folder and then adding a <code class=\"fm-code-in-text\">Home<\/code> folder within it) and added a Razor View called <code class=\"fm-code-in-text\">Index.cshtml<\/code>, the contents of which are shown in listing 5.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.5 The contents of the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;string&gt;\n@{ Layout = null; }\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;Language Features&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;ul&gt;\n        @foreach (string s in Model) {\n            &lt;li&gt;@s&lt;\/li&gt;\n        }\n    &lt;\/ul&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The code editor will highlight part of this file to denote a warning, which I explain shortly.<a id=\"calibre_link-1424\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-74\">5.1.4 Selecting the HTTP port<\/h3>\n<p class=\"body\">Change the HTTP port that ASP.NET Core uses to receive requests, as shown in listing 5.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.6 Setting the HTTP port in the launchSettings.json file in the Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"LanguageFeatures\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-75\">5.1.5 Running the example application<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1425\"><\/a>Start ASP.NET Core by running the command shown in listing 5.7 in the <code class=\"fm-code-in-text\">LanguageFeatures<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.7 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">The output from the <code class=\"fm-code-in-text\">dotnet run<\/code> command will include two build warnings, which I explain in the \u201cUnderstanding Null State Analysis\u201d section. Once ASP.NET Core has started, use a web browser to request http:\/\/localhost:5000, and you will see the output shown in figure 5.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre44\" src=\"\/images\/proaspnetcore7\/000039.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 5.1 Running the example application<\/p>\n<\/div>\n<p class=\"body\">Since the output from all the examples in this chapter is text, I will show the messages displayed by the browser like this:<\/p>\n<pre class=\"programlisting\">C# \nLanguage \nFeatures<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-76\">5.2 Understanding top-level statements<\/h2>\n<p class=\"body\">Top-level statements are intended to remove unnecessary code structure from class files. A project can contain one file that defines code statements outside of a namespace or a file. For ASP.NET Core applications, this feature is used to configure the application in the <code class=\"fm-code-in-text\">Program.cs<\/code> file. Here is the content of the <code class=\"fm-code-in-text\">Program.cs<\/code> file in the example application for this chapter:<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nvar app = builder.Build();\n\n\/\/app.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.MapDefaultControllerRoute();\n\napp.Run();<\/pre>\n<p class=\"body\">If you have used earlier versions of ASP.NET Core, you will be familiar with the <code class=\"fm-code-in-text\">Startup<\/code> class, which was used to configure the application. Top-level statements have allowed this process to be simplified, and all of the configuration statements are now defined in the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"body\">The compiler will report an error if there is more than one file in a project with top-level statements, so the <code class=\"fm-code-in-text\">Program.cs<\/code> file is the only place you will find them in an ASP.NET Core project.<a id=\"calibre_link-1426\"><\/a><a id=\"calibre_link-1427\"><\/a><a id=\"calibre_link-856\"><\/a><\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-77\">5.3 Understanding global using statements<\/h2>\n<p class=\"body\">C# supports global <code class=\"fm-code-in-text\">using<\/code> statements, which allow a <code class=\"fm-code-in-text\">using<\/code> statement to be defined once but take effect throughout a project. Traditionally, each code file contains a series of <code class=\"fm-code-in-text\">using<\/code> statements that declare dependencies on the namespaces that it requires. Listing 5.8 adds a <code class=\"fm-code-in-text\">using<\/code> statement that provides access to the types defined in the <code class=\"fm-code-in-text\">Models<\/code> namespace. (The code editor will highlight part of this code listing, which I explain in the \u201cUnderstanding Null State Analysis\u201d section.)<a id=\"calibre_link-1428\"><\/a><a id=\"calibre_link-1429\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.8 Adding a statement in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n<b class=\"fm-bold\">using LanguageFeatures.Models;<\/b>\n\nnamespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            <b class=\"fm-bold\">Product[] products = Product.GetProducts();<\/b>\n            <b class=\"fm-bold\">return View(new string[] { products[0].Name });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">To access the <code class=\"fm-code-in-text\">Product<\/code> class, I added a <code class=\"fm-code-in-text\">using<\/code> statement for the namespace that contains it, which is <code class=\"fm-code-in-text\">LanguageFeatures.Models<\/code>. The code file already contains a <code class=\"fm-code-in-text\">using<\/code> statement for the <code class=\"fm-code-in-text\">Microsoft.AspNetCore.Mvc<\/code> namespace, which provides access to the <code class=\"fm-code-in-text\">Controller<\/code> class, from which the <code class=\"fm-code-in-text\">HomeController<\/code> class is derived.<\/p>\n<p class=\"body\">In most projects, some namespaces are required throughout the application, such as those containing data model classes. This can result in a long list of <code class=\"fm-code-in-text\">using<\/code> statements, duplicated in every code file. Global <code class=\"fm-code-in-text\">using<\/code> statements address this problem by allowing <code class=\"fm-code-in-text\">using<\/code> statements for commonly required namespaces to be defined in a single location. Add a code file named <code class=\"fm-code-in-text\">GlobalUsings.cs<\/code> to the <code class=\"fm-code-in-text\">LanguageFeatures<\/code> project with the content shown in listing 5.9.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.9 The contents of the GlobalUsings.cs file in the LanguageFeatures folder<\/p>\n<pre class=\"programlisting\">global using LanguageFeatures.Models;\nglobal using Microsoft.AspNetCore.Mvc;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">global<\/code> keyword is used to denote a global <code class=\"fm-code-in-text\">using<\/code>. The statements in listing 5.9 make the <code class=\"fm-code-in-text\">LanguageFeatures.Models<\/code> and <code class=\"fm-code-in-text\">Microsoft.AspNetCore.Mvc<\/code> namespaces available throughout the application, which means they can be removed from the <code class=\"fm-code-in-text\">HomeController.cs<\/code> file, as shown in listing 5.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.10 Removing statements in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">\/\/using Microsoft.AspNetCore.Mvc;<\/b>\n<b class=\"fm-bold\">\/\/using LanguageFeatures.Models;<\/b>\n\nnamespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            Product[] products = Product.GetProducts();\n            return View(new string[] { products[0].Name });\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1430\"><\/a>If you run the example, you will see the following results displayed in the browser window:<\/p>\n<pre class=\"programlisting\">Kayak<\/pre>\n<p class=\"body\">You will receive warnings when compiling the project, which I explain in the \u201cUnderstanding Null State Analysis\u201d section.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Global <code class=\"fm-code-in-text1\">using<\/code> statements are a good idea, but I have not used them in this book because I want to make it obvious when I add a dependency to a new namespace.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-78\">5.3.1 Understanding implicit using statements<\/h3>\n<p class=\"body\">The ASP.NET Core project templates enable a feature named <i class=\"fm-italics\">implicit usings<\/i>, which define global <code class=\"fm-code-in-text\">using<\/code> statements for these commonly required namespaces:<\/p>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">System<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">System.Collections.Generic<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">System.IO<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">System.Linq<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">System.Net.Http<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">System.Net.Http.Json<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">System.Threading<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">System.Threading.Tasks<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">Microsoft.AspNetCore.Builder<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">Microsoft.AspNetCore.Hosting<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">Microsoft.AspNetCore.Http<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">Microsoft.AspNetCore.Routing<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">Microsoft.Extensions.Configuration<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">Microsoft.Extensions.DependencyInjection<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">Microsoft.Extensions.Hosting<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">Microsoft.Extensions.Logging<\/code><\/p>\n<\/li>\n<\/ul>\n<p class=\"body\"><code class=\"fm-code-in-text\">using<\/code> statements are not required for these namespaces, which are available throughout the application. These namespaces don\u2019t cover all of the ASP.NET Core features, but they do cover the basics, which is why no explicit <code class=\"fm-code-in-text\">using<\/code> statements are required in the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<a id=\"calibre_link-1431\"><\/a><\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-79\">5.4 Understanding null state analysis<\/h2>\n<p class=\"body\">The editor and compiler warnings shown in earlier sections are produced because ASP.NET Core project templates enable <i class=\"fm-italics\">null state analysis<\/i>, in which the compiler identifies attempts to access references that may be unintentionally null, preventing null reference exceptions at runtime.<\/p>\n<p class=\"body\">Open the <code class=\"fm-code-in-text\">Product.cs<\/code> file, and the editor will display two warnings, as shown in figure 5.2. The figure shows how Visual Studio displays a warning, but Visual Studio Code is similar.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre45\" src=\"\/images\/proaspnetcore7\/000040.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 5.2 A null state analysis warning<\/p>\n<\/div>\n<p class=\"body\">When null state analysis is enabled, C# variables are divided into two groups: nullable and non-nullable. As their name suggests, nullable variables can be assigned the special value <code class=\"fm-code-in-text\">null<\/code>. This is the behavior that most programmers are familiar with, and it is entirely up to the developer to guard against trying to use <code class=\"fm-code-in-text\">null<\/code> references, which will trigger a <code class=\"fm-code-in-text\">NullReferenceException<\/code>.<\/p>\n<p class=\"body\">By contrast, non-nullable variables can never be <code class=\"fm-code-in-text\">null<\/code>. When you receive a non-nullable variable, you don\u2019t have to guard against a <code class=\"fm-code-in-text\">null<\/code> value because that is not a value that can ever be assigned.<a id=\"calibre_link-1432\"><\/a><\/p>\n<p class=\"body\">A question mark (the <code class=\"fm-code-in-text\">?<\/code> character) is appended to a type to denote a nullable type. So, if a variable\u2019s type is <code class=\"fm-code-in-text\">string?<\/code>, for example, then it can be assigned any value <code class=\"fm-code-in-text\">string<\/code> value or <code class=\"fm-code-in-text\">null<\/code>. When attempting to access this variable, you should check to ensure that it isn\u2019t <code class=\"fm-code-in-text\">null<\/code> before attempting to access any of the fields, properties, or methods defined by the <code class=\"fm-code-in-text\">string<\/code> type.<\/p>\n<p class=\"body\">If a variable\u2019s type is <code class=\"fm-code-in-text\">string<\/code>, then it cannot be assigned <code class=\"fm-code-in-text\">null<\/code> values, which means you can confidently access the features it provides without needing to guard again<a id=\"calibre_link-1433\"><\/a>st <code class=\"fm-code-in-text\">null<\/code> references.<\/p>\n<p class=\"body\">The compiler examines the code in the project and warns you when it finds statements that might break these rules. The most common issues are attempting to assign <code class=\"fm-code-in-text\">null<\/code> to non-nullable variables and attempting to access members defined by nullable variables without checking to see if they are <code class=\"fm-code-in-text\">null<\/code>. In the sections that follow, I explain the different ways that the warnings raised by the compiler in the example application can be addressed.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Getting to grips with nullable and non-nullable types can be frustrating. A change in one code file can simply move a warning to another part of the application, and it can feel like you are chasing problems through a project. But it is worth sticking with null state analysis because null reference exceptions are the most common runtime error, and few programmers are disciplined enough to guard against null values without the compiler analysis feature.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-80\">5.4.1 Ensuring fields and properties are assigned values<\/h3>\n<p class=\"body\"><a id=\"calibre_link-863\"><\/a>The first warning in the <code class=\"fm-code-in-text\">Product.cs<\/code> file is for the <code class=\"fm-code-in-text\">Name<\/code> field, whose type is <code class=\"fm-code-in-text\">string<\/code>, which is a non-nullable type (because it hasn\u2019t been annotated with a question mark).<\/p>\n<pre class=\"programlisting\">...\npublic <b class=\"fm-bold\">string<\/b> Name { get; set; }\n...<\/pre>\n<p class=\"body\">One consequence of using non-nullable types is that properties like <code class=\"fm-code-in-text\">Name<\/code> must be assigned a value when a new instance of the enclosing class is created. If this were not the case, then the <code class=\"fm-code-in-text\">Name<\/code> property would not be initialized and would be <code class=\"fm-code-in-text\">null<\/code>. And this is a problem because we can\u2019t assign <code class=\"fm-code-in-text\">null<\/code> to a non-nullable property, even indirectly.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">required<\/code> keyword can be used to indicate that a value is required for a non-nullable type, as shown in listing 5.11.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.11 Using the required keyword in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n    public class Product {\n        \n        <b class=\"fm-bold\">public required string Name { get; set; }<\/b>\n        public decimal? Price { get; set; }\n                \n        public static Product[] GetProducts() {\n                \n            Product kayak = new Product {\n                Name = \"Kayak\", Price = 275M\n            };\n                        \n            Product lifejacket = new Product {\n                Name = \"Lifejacket\", Price = 48.95M\n            };\n                        \n            return new Product[] { kayak, lifejacket, null };\n        }\n    }\n}<\/pre>\n<p class=\"body\">The compiler will check to make sure that a value is assigned to the property when a new instance of the containing type is created. The two <code class=\"fm-code-in-text\">Product<\/code> objects used in the listing are created with a value for the <code class=\"fm-code-in-text\">Name<\/code> field, which satisfies the demands of the <code class=\"fm-code-in-text\">required<\/code> keyword. Listing 5.12 omits the <code class=\"fm-code-in-text\">Name<\/code> value from one of <code class=\"fm-code-in-text\">Product<\/code> objects.<a id=\"calibre_link-1434\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.12 Omitting a value in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n    public class Product {\n        \n        public required string Name { get; set; }\n        public decimal? Price { get; set; }\n                \n        public static Product[] GetProducts() {\n                \n            Product kayak = new Product {\n                Name = \"Kayak\", Price = 275M\n            };\n                        \n            Product lifejacket = new Product {\n                <b class=\"fm-bold\">\/\/Name = \"Lifejacket\",<\/b> \n                Price = 48.95M\n            };\n                        \n            return new Product[] { kayak, lifejacket, null };\n        }\n    }\n}<\/pre>\n<p class=\"body\">If you run the example, the build process will fail with this error:<\/p>\n<pre class=\"programlisting\">Required member 'Product.Name' must be set in the object initializer or attribute constructor.<\/pre>\n<p class=\"body\">This error&mdash;and the corresponding red line in the code editor&mdash;tell you that a value for the <code class=\"fm-code-in-text\">Name<\/code> property is required but has not been provided.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-81\">5.4.2 Providing a default value for non-nullable types<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1435\"><\/a>The <code class=\"fm-code-in-text\">required<\/code> keyword is a good way to denote a property that cannot be <code class=\"fm-code-in-text\">null<\/code>, and which requires a value when an object is created. This approach can become cumbersome in situations where there may not always be a suitable data value available, because it requires the code wants to create the object to provide a fallback value and there is no good way to enforce consistency.<\/p>\n<p class=\"body\">For these situations a default value can be used instead of the <code class=\"fm-code-in-text\">required<\/code> keyword, as shown in listing 5.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.13 Providing a default value in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n    public class Product {\n        \n        <b class=\"fm-bold\">public string Name { get; set; } = string.Empty;<\/b>\n        public decimal? Price { get; set; }\n                \n        public static Product[] GetProducts() {\n                \n            Product kayak = new Product {\n                Name = \"Kayak\", Price = 275M\n            };\n                        \n            Product lifejacket = new Product {\n                \/\/Name = \"Lifejacket\", \n                Price = 48.95M\n            };\n                        \n            return new Product[] { kayak, lifejacket, null };\n        }\n    }\n}<\/pre>\n<p class=\"body\">The default value in this example is the empty string. This value will be replaced for <code class=\"fm-code-in-text\">Product<\/code> objects that are created with a <code class=\"fm-code-in-text\">Name<\/code> value and ensures consistency for objects that are created without one.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-82\">5.4.3 Using nullable types<\/h3>\n<p class=\"body\">The remaining warning in the <code class=\"fm-code-in-text\">Product.cs<\/code> file occurs because there is a mismatch between the type used for the result of the <code class=\"fm-code-in-text\">GetProducts<\/code> method and the values that are used to initialize it:<\/p>\n<pre class=\"programlisting\">...\nreturn new <b class=\"fm-bold\">Product[]<\/b> { kayak, lifejacket, <b class=\"fm-bold\">null<\/b> };\n...<\/pre>\n<p class=\"body\">The type of the array that is created is <code class=\"fm-code-in-text\">Product[]<\/code>, which contains non-nullable <code class=\"fm-code-in-text\">Product<\/code> references. But one of the values used to populate the array is <code class=\"fm-code-in-text\">null<\/code>, which isn\u2019t allowed. Listing 5.14 changes the array type so that nullable values are allowed.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.14 Using a nullable type in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n    public class Product {\n        \n        public string Name { get; set; } = string.Empty;\n        public decimal? Price { get; set; }\n                \n        <b class=\"fm-bold\">public static Product?[] GetProducts() {<\/b>\n                \n            Product kayak = new Product {\n                Name = \"Kayak\", Price = 275M\n            };\n                        \n            Product lifejacket = new Product {\n                \/\/Name = \"Lifejacket\", \n                Price = 48.95M\n            };\n                        \n            <b class=\"fm-bold\">return new Product?[] { kayak, lifejacket, null };<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The type <code class=\"fm-code-in-text\">Product?[]<\/code> denotes an array of <code class=\"fm-code-in-text\">Product?<\/code> references, which means the result can include <code class=\"fm-code-in-text\">null<\/code>. Notice that I had to make the same change to the result type declared by the <code class=\"fm-code-in-text\">GetProducts<\/code> method because a <code class=\"fm-code-in-text\">Product?[]<\/code> array cannot be used where a <code class=\"fm-code-in-text\">Product[]<\/code> is expected.<a id=\"calibre_link-1436\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Selecting the right nullable type<\/p>\n<p class=\"fm-sidebar-text\">Care must be taken to apply the question mark correctly, especially when dealing with arrays and collections. A variable of type <code class=\"fm-code-in-text1\">Product?[]<\/code> denotes an array that can contain <code class=\"fm-code-in-text1\">Product<\/code> or <code class=\"fm-code-in-text1\">null<\/code> values but that won\u2019t be <code class=\"fm-code-in-text1\">null<\/code> itself:<\/p>\n<pre class=\"programlisting\">...\nProduct?[] arr1 = new Product?[] { kayak, lifejacket, null };  \/\/ OK\nProduct?[] arr2 = null;                                        \/\/ Not OK\n...<\/pre>\n<p class=\"fm-sidebar-text\">A variable of type <code class=\"fm-code-in-text1\">Product[]?<\/code> is an array that can hold only <code class=\"fm-code-in-text1\">Product<\/code> values and not <code class=\"fm-code-in-text1\">null<\/code> values, but the array itself may be <code class=\"fm-code-in-text1\">null<\/code>:<\/p>\n<pre class=\"programlisting\">...\nProduct[]? arr1 = new Product?[] { kayak, lifejacket, null }; \/\/ Not OK\nProduct[]? arr2 = null;                                       \/\/ OK\n...<\/pre>\n<p class=\"fm-sidebar-text\">A variable of type <code class=\"fm-code-in-text1\">Product?[]?<\/code> is an array that can contain <code class=\"fm-code-in-text1\">Product<\/code> or <code class=\"fm-code-in-text1\">null<\/code> values and that can itself be <code class=\"fm-code-in-text1\">null<\/code>:<\/p>\n<pre class=\"programlisting\">...\nProduct?[]? arr1 = new Product?[] { kayak, lifejacket, null }; \/\/ OK\nProduct?[]? arr2 = null;                                          \/\/ Also OK\n...<\/pre>\n<p class=\"fm-sidebar-text\">Null state analysis is a useful feature, but that doesn\u2019t mean it is always easy to understand.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-83\">5.4.4 Checking for null values<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1437\"><\/a>I explained that dealing with null state analysis warnings can feel like chasing a problem through code, and you can see a simple example of this in the <code class=\"fm-code-in-text\">HomeController.cs<\/code> file in the <code class=\"fm-code-in-text\">Controllers<\/code> folder. In listing 5.14, I changed the type returned by the <code class=\"fm-code-in-text\">GetProducts<\/code> method to allow <code class=\"fm-code-in-text\">null<\/code> values, but that has created a mismatch in the <code class=\"fm-code-in-text\">HomeController<\/code> class, which invokes that method and assigns the result to an array of non-nullable <code class=\"fm-code-in-text\">Product<\/code> values:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">Product[]<\/b> products = Product.GetProducts();\n...<\/pre>\n<p class=\"body\">This is easily resolved by changing the type of the <code class=\"fm-code-in-text\">products<\/code> variable to match the type returned by the <code class=\"fm-code-in-text\">GetProducts<\/code> method, as shown in listing 5.15.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.15 Changing Type in the HomeController.cs File in the Controllers Folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            <b class=\"fm-bold\">Product?[] products = Product.GetProducts();<\/b>\n            return View(new string[] { products[0].Name });\n        }\n    }\n}<\/pre>\n<p class=\"body\">This resolves one warning and introduces another, as shown in figure 5.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre46\" src=\"\/images\/proaspnetcore7\/000041.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 5.3 An additional null state analysis warning<\/p>\n<\/div>\n<p class=\"body\">The statement flagged by the compiler attempts to access the <code class=\"fm-code-in-text\">Name<\/code> field of the element at index zero in the array, which might be <code class=\"fm-code-in-text\">null<\/code> since the array type is <code class=\"fm-code-in-text\">Product?[]<\/code>. Addressing this issue requires a check for <code class=\"fm-code-in-text\">null<\/code> values, as shown in listing 5.16.<a id=\"calibre_link-1438\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.16 Guarding against null in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            Product?[] products = Product.GetProducts();\n            <b class=\"fm-bold\">Product? p = products[0];<\/b>\n            <b class=\"fm-bold\">string val;<\/b>\n            <b class=\"fm-bold\">if (p != null) {<\/b>\n                <b class=\"fm-bold\">val = p.Name;<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">val = \"No value\";<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return View(new string[] { val });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">This is an especially verbose way of avoiding a null, which I will refine shortly. But it demonstrates an important point, which is that the compiler can understand the effect of C# expressions when checking for a <code class=\"fm-code-in-text\">null<\/code> reference. In listing 5.16, I use an <code class=\"fm-code-in-text\">if<\/code> statement to see if a <code class=\"fm-code-in-text\">Product?<\/code> variable is not <code class=\"fm-code-in-text\">null<\/code>, and the compiler understands that the variable cannot be <code class=\"fm-code-in-text\">null<\/code> within the scope of the <code class=\"fm-code-in-text\">if<\/code> clause and doesn\u2019t generate a warning when I read the name field:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">if (p != null) {<\/b>\n    <b class=\"fm-bold\">val = p.Name;<\/b>\n} else {\n    val = \"No value\";\n}\n...<\/pre>\n<p class=\"body\">The compiler has a sophisticated understanding of C# but doesn\u2019t always get it right, and I explain what to do when the compiler isn\u2019t able to accurately determine whether a variable is <code class=\"fm-code-in-text\">null<\/code> in the \u201cOverriding Null State Analysis\u201d section.<\/p>\n<p class=\"fm-head2\">Using the null conditional operator<\/p>\n<p class=\"body\">The null conditional operator is a more concise way of avoiding member access for <code class=\"fm-code-in-text\">null<\/code> values, as shown in listing 5.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.17 Null conditional operator in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n                \n            Product?[] products = Product.GetProducts();\n                        \n            <b class=\"fm-bold\">string? val = products[0]?.Name;<\/b>\n            <b class=\"fm-bold\">if (val != null) {<\/b>\n                <b class=\"fm-bold\">return View(new string[] { val });<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return View(new string[] { \"No Value\" });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The null conditional operator is a question mark applied before a member is accessed, like this:<\/p>\n<pre class=\"programlisting\">...\nstring? val = products[0]<b class=\"fm-bold\">?<\/b>.Name;\n...<\/pre>\n<p class=\"body\">The operator returns <code class=\"fm-code-in-text\">null<\/code> if it is applied to a variable that is <code class=\"fm-code-in-text\">null<\/code>. In this case, if the element at index zero of the products array is <code class=\"fm-code-in-text\">null<\/code>, then the operator will return <code class=\"fm-code-in-text\">null<\/code> and prevent an attempt to access the <code class=\"fm-code-in-text\">Name<\/code> property, which would cause an exception. If <code class=\"fm-code-in-text\">products[0]<\/code> isn\u2019t <code class=\"fm-code-in-text\">null<\/code>, then the operator does nothing, and the expression returns the value assigned to the <code class=\"fm-code-in-text\">Name<\/code> property. Applying the null conditional operator can return <code class=\"fm-code-in-text\">null<\/code>, and its result must always be assigned to a nullable variable, such as the <code class=\"fm-code-in-text\">string?<\/code> used in this example.<a id=\"calibre_link-1439\"><\/a><\/p>\n<p class=\"fm-head2\">Using the null-coalescing operator<\/p>\n<p class=\"body\">The null-coalescing operator is two question mark characters (<code class=\"fm-code-in-text\">??<\/code>) and is used to provide a fallback value, often used in conjunction with the null conditional operator, as shown in listing 5.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.18 Using the null-coalescing operator in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            Product?[] products = Product.GetProducts();\n            <b class=\"fm-bold\">return View(new string[] { products[0]?.Name ?? \"No Value\" });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">??<\/code> operator returns the value of its left-hand operand if it isn\u2019t <code class=\"fm-code-in-text\">null<\/code>. If the left-hand operand is <code class=\"fm-code-in-text\">null<\/code>, then the <code class=\"fm-code-in-text\">??<\/code> operator returns the value of its right-hand operand. This behavior works well with the null conditional operator. If <code class=\"fm-code-in-text\">products[0]<\/code> is <code class=\"fm-code-in-text\">null<\/code>, then the <code class=\"fm-code-in-text\">?<\/code> operator will return <code class=\"fm-code-in-text\">null<\/code>, and the <code class=\"fm-code-in-text\">??<\/code> operator will return <code class=\"fm-code-in-text\">\"No Value\"<\/code>. If <code class=\"fm-code-in-text\">products[0]<\/code> isn\u2019t <code class=\"fm-code-in-text\">null<\/code>, then the result will be the value of its <code class=\"fm-code-in-text\">Name<\/code> property. This is a more concise way of performing the same null checks shown in earlier examples.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The <code class=\"fm-code-in-text1\">?<\/code> and <code class=\"fm-code-in-text1\">??<\/code> operators cannot always be used, and you will see examples in later chapters where I use an <code class=\"fm-code-in-text1\">if<\/code> statement to check for <code class=\"fm-code-in-text1\">null<\/code> values. One common example is when using the <code class=\"fm-code-in-text1\">await<\/code>\/<code class=\"fm-code-in-text1\">async<\/code> keywords, which are described later in this chapter, and which do not integrate well with the null conditional operator.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-84\">5.4.5 Overriding null state analysis<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1440\"><\/a>The C# compiler has a sophisticated understanding of when a variable can be <code class=\"fm-code-in-text\">null<\/code>, but it doesn\u2019t always get it right, and there are times when you have a better understanding of whether a <code class=\"fm-code-in-text\">null<\/code> value can arise than the compiler. In these situations, the null-forgiving operator can be used to tell the compiler that a variable isn\u2019t <code class=\"fm-code-in-text\">null<\/code>, regardless of what the null state analysis suggests, as shown in listing 5.19.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.19 Using the null-forgiving operator in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            Product?[] products = Product.GetProducts();\n            <b class=\"fm-bold\">return View(new string[] { products[0]!.Name });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The null-forgiving operator is an exclamation mark and is used in this example to tell the compiler that <code class=\"fm-code-in-text\">products[0]<\/code> isn\u2019t <code class=\"fm-code-in-text\">null<\/code>, even though null state analysis has identified that it might be.<\/p>\n<p class=\"body\">When using the <code class=\"fm-code-in-text\">!<\/code> operator, you are telling the compiler that you have better insight into whether a variable can be <code class=\"fm-code-in-text\">null<\/code>, and, naturally, this should be done only when you are entirely confident that you are right.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-85\">5.4.6 Disabling null state analysis warnings<\/h3>\n<p class=\"body\">An alternative to the null-forgiving operator is to disable null state analysis warnings for a particular section of code or a complete code file, as shown in listing 5.20.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.20 Disabling warnings in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            Product?[] products = Product.GetProducts();\n            <b class=\"fm-bold\">#pragma warning disable CS8602<\/b>\n            <b class=\"fm-bold\">return View(new string[] { products[0].Name });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">This listing uses a #pragma directive to suppress warning CS8602 (you can identify warnings in the output from the build process).<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> .NET includes a set of advanced attributes that can be used to provide the compiler with guidance for null state analysis. These are not widely used and are encountered only in chapter 36 of this book because they are used by one part of the ASP.NET Core API. See <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/attributes\/nullable-analysis\">https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/attributes\/nullable-analysis<\/a> for details.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-86\">5.5 Using string interpolation<\/h2>\n<p class=\"body\"><a id=\"calibre_link-853\"><\/a>C# supports <i class=\"fm-italics\">string interpolation<\/i> to create formatted strings, which uses templates with variable names that are resolved and inserted into the output, as shown in listing 5.21.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.21 Using string interpolation in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            Product?[] products = Product.GetProducts();\n                        \n            return View(new string[] { \n                <b class=\"fm-bold\">$\"Name: {products[0]?.Name}, Price: { products[0]?.Price }\"<\/b>\n            });\n        }\n    }\n}<\/pre>\n<p class=\"body\">Interpolated strings are prefixed with the <code class=\"fm-code-in-text\">$<\/code> character and contain <i class=\"fm-italics\">holes<\/i>, which are references to values contained within the <code class=\"fm-code-in-text\">{<\/code> and <code class=\"fm-code-in-text\">}<\/code> characters. When the string is evaluated, the holes are filled in with the current values of the variables or constants that are specified.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> String interpolation supports the string format specifiers, which can be applied within holes, so <code class=\"fm-code-in-text1\">$\"Price:<\/code> <code class=\"fm-code-in-text1\">{price:C2}\"<\/code> would format the <code class=\"fm-code-in-text1\">price<\/code> value as a currency value with two decimal digits, for example.<\/p>\n<p class=\"body\">Start ASP.NET Core and request http:\/\/localhost:5000, and you will see a formatted string:<\/p>\n<pre class=\"programlisting\">Name: Kayak, Price: 275<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-87\">5.6 Using object and collection initializers<\/h2>\n<p class=\"body\">When I create an object in the static <code class=\"fm-code-in-text\">GetProducts<\/code> method of the <code class=\"fm-code-in-text\">Product<\/code> class, I use an <i class=\"fm-italics\">object initializer<\/i>, which allows me to create an object and specify its property values in a single step, like this:<a id=\"calibre_link-1441\"><\/a><a id=\"calibre_link-1442\"><\/a><\/p>\n<pre class=\"programlisting\">...\nProduct kayak = new Product {\n    <b class=\"fm-bold\">Name = \"Kayak\", Price = 275M<\/b>\n};\n...<\/pre>\n<p class=\"body\">This is another syntactic sugar feature that makes C# easier to use. Without this feature, I would have to call the <code class=\"fm-code-in-text\">Product<\/code> constructor and then use the newly created object to set each of the properties, like this:<\/p>\n<pre class=\"programlisting\">...\nProduct kayak = new Product();\nkayak.Name = \"Kayak\";\nkayak.Price = 275M;\n...<\/pre>\n<p class=\"body\">A related feature is the <i class=\"fm-italics\">collection initializer<\/i>, which allows the creation of a collection and its contents to be specified in a single step. Without an initializer, creating a string array, for example, requires the size of the array and the array elements to be specified separately, as shown in listing 5.22.<a id=\"calibre_link-1443\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.22 Initializing an object in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            <b class=\"fm-bold\">string[] names = new string[3];<\/b>\n            <b class=\"fm-bold\">names[0] = \"Bob\";<\/b>\n            <b class=\"fm-bold\">names[1] = \"Joe\";<\/b>\n            <b class=\"fm-bold\">names[2] = \"Alice\";<\/b>\n            <b class=\"fm-bold\">return View(\"Index\", names);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">Using a collection initializer allows the contents of the array to be specified as part of the construction, which implicitly provides the compiler with the size of the array, as shown in listing 5.23.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.23 A collection initializer in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            <b class=\"fm-bold\">return View(\"Index\", new string[] { \"Bob\", \"Joe\", \"Alice\" });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The array elements are specified between the <code class=\"fm-code-in-text\">{<\/code> and <code class=\"fm-code-in-text\">}<\/code> characters, which allows for a more concise definition of the collection and makes it possible to define a collection inline within a method call. The code in listing 5.23 has the same effect as the code in listing 5.22. Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the following output in the browser window:<\/p>\n<pre class=\"programlisting\">Bob\nJoe\nAlice<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-88\">5.6.1 Using an index initializer<\/h3>\n<p class=\"body\"><a id=\"calibre_link-857\"><\/a>Recent versions of C# tidy up the way collections that use indexes, such as dictionaries, are initialized. Listing 5.24 shows the <code class=\"fm-code-in-text\">Index<\/code> action rewritten to define a collection using the traditional C# approach to initializing a dictionary.<a id=\"calibre_link-1444\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.24 Initializing a dictionary in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            <b class=\"fm-bold\">Dictionary&lt;string, Product&gt; products<\/b> \n                    <b class=\"fm-bold\">= new Dictionary&lt;string, Product&gt; {<\/b>\n                <b class=\"fm-bold\">{<\/b> \n                    <b class=\"fm-bold\">\"Kayak\",<\/b> \n                    <b class=\"fm-bold\">new Product { Name = \"Kayak\", Price = 275M }<\/b> \n                <b class=\"fm-bold\">},<\/b>\n                <b class=\"fm-bold\">{<\/b> \n                    <b class=\"fm-bold\">\"Lifejacket\",<\/b>  \n                    <b class=\"fm-bold\">new Product{ Name = \"Lifejacket\", Price = 48.95M }<\/b> \n                <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">};<\/b>\n            <b class=\"fm-bold\">return View(\"Index\", products.Keys);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The syntax for initializing this type of collection relies too much on the <code class=\"fm-code-in-text\">{<\/code> and <code class=\"fm-code-in-text\">}<\/code> characters, especially when the collection values are created using object initializers. The latest versions of C# support a more natural approach to initializing indexed collections that is consistent with the way that values are retrieved or modified once the collection has been initialized, as shown in listing 5.25.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.25 Using collection initializer syntax in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            Dictionary&lt;string, Product&gt; products \n                    = new Dictionary&lt;string, Product&gt; {\n                <b class=\"fm-bold\">[\"Kayak\"] = new Product { Name = \"Kayak\", Price = 275M },<\/b>\n                <b class=\"fm-bold\">[\"Lifejacket\"] = new Product { Name = \"Lifejacket\",<\/b> \n                    <b class=\"fm-bold\">Price = 48.95M }<\/b>\n            };\n            return View(\"Index\", products.Keys);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The effect is the same&mdash;to create a dictionary whose keys are <code class=\"fm-code-in-text\">Kayak<\/code> and <code class=\"fm-code-in-text\">Lifejacket<\/code> and whose values are <code class=\"fm-code-in-text\">Product<\/code> objects&mdash;but the elements are created using the index notation that is used for other collection operations. Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the following results in the browser:<\/p>\n<pre class=\"programlisting\">Kayak\nLifejacket<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-89\">5.7 Using target-typed new expressions<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1445\"><\/a>The example in listing 5.25 is still verbose and declares the collection type when defining the variable and creating an instance with the <code class=\"fm-code-in-text\">new<\/code> keyword:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">Dictionary&lt;string, Product&gt;<\/b> products = new <b class=\"fm-bold\">Dictionary&lt;string, Product&gt;<\/b> {\n    [\"Kayak\"] = new Product { Name = \"Kayak\", Price = 275M },\n    [\"Lifejacket\"] = new Product { Name = \"Lifejacket\", Price = 48.95M }\n};\n...<\/pre>\n<p class=\"body\">This can be simplified using a target-typed <code class=\"fm-code-in-text\">new<\/code> expression, as shown in listing 5.26.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.26 Using a target-typed new expression in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            <b class=\"fm-bold\">Dictionary&lt;string, Product&gt; products = new () {<\/b>\n                [\"Kayak\"] = new Product { Name = \"Kayak\", Price = 275M },\n                [\"Lifejacket\"] = new Product { Name = \"Lifejacket\", \n                    Price = 48.95M }\n            };\n            return View(\"Index\", products.Keys);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The type can be replaced with <code class=\"fm-code-in-text\">new()<\/code> when the compiler can determine which type is required and constructor arguments are provided as arguments to the <code class=\"fm-code-in-text\">new<\/code> method. Creating instances with the <code class=\"fm-code-in-text\">new()<\/code> expression works only when the compiler can determine which type is required. Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the following results in the browser:<\/p>\n<pre class=\"programlisting\">Kayak\nLifejacket<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-90\">5.8 Pattern Matching<\/h2>\n<p class=\"body\"><a id=\"calibre_link-864\"><\/a>One of the most useful recent additions to C# is support for pattern matching, which can be used to test that an object is of a specific type or has specific characteristics. This is another form of syntactic sugar, and it can dramatically simplify complex blocks of conditional statements. The <code class=\"fm-code-in-text\">is<\/code> keyword is used to perform a type test, as demonstrated in listing 5.27.<a id=\"calibre_link-1446\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.27 Testing a type in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n                \n            <b class=\"fm-bold\">object[] data = new object[] { 275M, 29.95M,<\/b>\n                <b class=\"fm-bold\">\"apple\", \"orange\", 100, 10 };<\/b>\n            <b class=\"fm-bold\">decimal total = 0;<\/b>\n            <b class=\"fm-bold\">for (int i = 0; i &lt; data.Length; i++) {<\/b>\n                <b class=\"fm-bold\">if (data[i] is decimal d) {<\/b>\n                    <b class=\"fm-bold\">total += d;<\/b>\n                <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">}<\/b>\n                        \n            <b class=\"fm-bold\">return View(\"Index\", new string[] { $\"Total: {total:C2}\" });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">is<\/code> keyword performs a type check and, if a value is of the specified type, will assign the value to a new variable, like this:<\/p>\n<pre class=\"programlisting\">...\nif (data[i] <b class=\"fm-bold\">is<\/b> decimal d) {\n...<\/pre>\n<p class=\"body\">This expression evaluates as <code class=\"fm-code-in-text\">true<\/code> if the value stored in <code class=\"fm-code-in-text\">data[i]<\/code> is a <code class=\"fm-code-in-text\">decimal<\/code>. The value of <code class=\"fm-code-in-text\">data[i]<\/code> will be assigned to the variable <code class=\"fm-code-in-text\">d<\/code>, which allows it to be used in subsequent statements without needing to perform any type conversions. The <code class=\"fm-code-in-text\">is<\/code> keyword will match only the specified type, which means that only two of the values in the data array will be processed (the other items in the array are <code class=\"fm-code-in-text\">string<\/code> and <code class=\"fm-code-in-text\">int<\/code> values). If you run the application, you will see the following output in the browser window:<\/p>\n<pre class=\"programlisting\">Total: $304.95<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-91\">5.8.1 Pattern matching in switch statements<\/h3>\n<p class=\"body\">Pattern matching can also be used in <code class=\"fm-code-in-text\">switch<\/code> statements, which support the <code class=\"fm-code-in-text\">when<\/code> keyword for restricting when a value is matched by a <code class=\"fm-code-in-text\">case<\/code> statement, as shown in listing 5.28.<a id=\"calibre_link-1447\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.28 Pattern matching in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n                \n            object[] data = new object[] { 275M, 29.95M,\n                \"apple\", \"orange\", 100, 10 };\n            decimal total = 0;\n            <b class=\"fm-bold\">for (int i = 0; i &lt; data.Length; i++) {<\/b>\n                <b class=\"fm-bold\">switch (data[i]) {<\/b>\n                    <b class=\"fm-bold\">case decimal decimalValue:<\/b>\n                        <b class=\"fm-bold\">total += decimalValue;<\/b>\n                        <b class=\"fm-bold\">break;<\/b>\n                    <b class=\"fm-bold\">case int intValue when intValue &gt; 50:<\/b>\n                        <b class=\"fm-bold\">total += intValue;<\/b>\n                        <b class=\"fm-bold\">break;<\/b>\n                <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">}<\/b>\n                        \n            return View(\"Index\", new string[] { $\"Total: {total:C2}\" });\n        }\n    }\n}<\/pre>\n<p class=\"body\">To match any value of a specific type, use the type and variable name in the <code class=\"fm-code-in-text\">case<\/code> statement, like this:<\/p>\n<pre class=\"programlisting\">...\ncase decimal decimalValue:\n...<\/pre>\n<p class=\"body\"><a id=\"calibre_link-854\"><\/a>This <code class=\"fm-code-in-text\">case<\/code> statement matches any <code class=\"fm-code-in-text\">decimal<\/code> value and assigns it to a new variable called <code class=\"fm-code-in-text\">decimalValue<\/code>. To be more selective, the <code class=\"fm-code-in-text\">when<\/code> keyword can be included, like this:<\/p>\n<pre class=\"programlisting\">...\ncase int intValue when intValue &gt; 50:\n...<\/pre>\n<p class=\"body\">This <code class=\"fm-code-in-text\">case<\/code> statement matches <code class=\"fm-code-in-text\">int<\/code> values and assigns them to a variable called <code class=\"fm-code-in-text\">intValue<\/code>, but only when the value is greater than 50. Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the following output in the browser window:<\/p>\n<pre class=\"programlisting\">Total: $404.95<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-92\">5.9 Using extension methods<\/h2>\n<p class=\"body\"><i class=\"fm-italics\">Extension methods<\/i> are a convenient way of adding methods to classes that you cannot modify directly, typically because they are provided by Microsoft or a third-party package. Listing 5.29 shows the definition of the <code class=\"fm-code-in-text\">ShoppingCart<\/code> class, which I added to the <code class=\"fm-code-in-text\">Models<\/code> folder in a class file called <code class=\"fm-code-in-text\">ShoppingCart.cs<\/code> and which represents a collection of <code class=\"fm-code-in-text\">Product<\/code> objects.<a id=\"calibre_link-1448\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.29 The contents of the ShoppingCart.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public class ShoppingCart {\n        public IEnumerable&lt;Product?&gt;? Products { get; set; }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1449\"><\/a>This is a simple class that acts as a wrapper around a sequence of <code class=\"fm-code-in-text\">Product<\/code> objects (I only need a basic class for this example). Note the type of the <code class=\"fm-code-in-text\">Products<\/code> property, which denotes a nullable enumerable of nullable <code class=\"fm-code-in-text\">Products<\/code>, meaning that the <code class=\"fm-code-in-text\">Products<\/code> property may be <code class=\"fm-code-in-text\">null<\/code> and that any sequence of elements assigned to the property may contain <code class=\"fm-code-in-text\">null<\/code> values.<\/p>\n<p class=\"body\">Suppose I need to be able to determine the total value of the <code class=\"fm-code-in-text\">Product<\/code> objects in the <code class=\"fm-code-in-text\">ShoppingCart<\/code> class, but I cannot modify the class because it comes from a third party, and I do not have the source code. I can use an extension method to add the functionality I need.<\/p>\n<p class=\"body\">Add a class file named <code class=\"fm-code-in-text\">MyExtensionMethods.cs<\/code> in the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the class shown in listing 5.30.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.30 The contents of the MyExtensionMethods.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public static class MyExtensionMethods {\n        \n        public static decimal TotalPrices(this ShoppingCart cartParam) {\n            decimal total = 0;\n            if (cartParam.Products != null) {\n                foreach (Product? prod in cartParam.Products) {\n                    total += prod?.Price ?? 0;\n                }\n            }\n            return total;\n        }\n    }\n}<\/pre>\n<p class=\"body\">Extension methods are <code class=\"fm-code-in-text\">static<\/code> and are defined in <code class=\"fm-code-in-text\">static<\/code> classes. Listing 5.30 defines a single extension method named <code class=\"fm-code-in-text\">TotalPrices<\/code>. The <code class=\"fm-code-in-text\">this<\/code> keyword in front of the first parameter marks <code class=\"fm-code-in-text\">TotalPrices<\/code> as an extension method. The first parameter tells .NET which class the extension method can be applied to&mdash;<code class=\"fm-code-in-text\">ShoppingCart<\/code> in this case. I can refer to the instance of the <code class=\"fm-code-in-text\">ShoppingCart<\/code> that the extension method has been applied to by using the <code class=\"fm-code-in-text\">cartParam<\/code> parameter. This extension method enumerates the <code class=\"fm-code-in-text\">Product<\/code> objects in the <code class=\"fm-code-in-text\">ShoppingCart<\/code> and returns the sum of the <code class=\"fm-code-in-text\">Product.Price<\/code> property values. Listing 5.31 shows how I apply the extension method in the <code class=\"fm-code-in-text\">Home<\/code> controller\u2019s action method.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Extension methods do not let you break through the access rules that classes define for methods, fields, and properties. You can extend the functionality of a class by using an extension method but only using the class members that you had access to anyway.<a id=\"calibre_link-855\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.31 Applying an extension method in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            <b class=\"fm-bold\">ShoppingCart cart<\/b> \n                <b class=\"fm-bold\">= new ShoppingCart { Products = Product.GetProducts()};<\/b>\n            <b class=\"fm-bold\">decimal cartTotal = cart.TotalPrices();<\/b>\n            <b class=\"fm-bold\">return View(\"Index\",<\/b> \n                <b class=\"fm-bold\">new string[] { $\"Total: {cartTotal:C2}\" });<\/b>\n        }\n    }\n}\nThe key statement is this one:\n...\ndecimal cartTotal = cart.TotalPrices();\n...<\/pre>\n<p class=\"body\">I call the <code class=\"fm-code-in-text\">TotalPrices<\/code> method on a <code class=\"fm-code-in-text\">ShoppingCart<\/code> object as though it were part of the <code class=\"fm-code-in-text\">ShoppingCart<\/code> class, even though it is an extension method defined by a different class altogether. .NET will find extension classes if they are in the scope of the current class, meaning that they are part of the same namespace or in a namespace that is the subject of a <code class=\"fm-code-in-text\">using<\/code> statement. Restart ASP.NET Core and request http:\/\/localhost:5000, which will produce the following output in the browser window:<\/p>\n<pre class=\"programlisting\">Total: $323.95<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-93\">5.9.1 Applying extension methods to an interface<\/h3>\n<p class=\"body\">Extension methods can also be applied to an interface, which allows me to call the extension method on all the classes that implement the interface. Listing 5.32 shows the <code class=\"fm-code-in-text\">ShoppingCart<\/code> class updated to implement the <code class=\"fm-code-in-text\">IEnumerable&lt;Product&gt;<\/code> interface.<a id=\"calibre_link-1450\"><\/a><a id=\"calibre_link-1451\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.32 Implementing an interface in the ShoppingCart.cs file in the Models folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using System.Collections;<\/b>\n\nnamespace LanguageFeatures.Models {\n\n    <b class=\"fm-bold\">public class ShoppingCart : IEnumerable&lt;Product?&gt; {<\/b>\n        public IEnumerable&lt;Product?&gt;? Products { get; set; }\n                \n        <b class=\"fm-bold\">public IEnumerator&lt;Product?&gt; GetEnumerator() =&gt;<\/b> \n                <b class=\"fm-bold\">Products?.GetEnumerator()<\/b>\n                    <b class=\"fm-bold\">?? Enumerable.Empty&lt;Product?&gt;().GetEnumerator();<\/b>\n        <b class=\"fm-bold\">IEnumerator IEnumerable.GetEnumerator() =&gt; GetEnumerator();<\/b>\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1452\"><\/a>I can now update the extension method so that it deals with <code class=\"fm-code-in-text\">IEnumerable&lt;Product?&gt;<\/code>, as shown in listing 5.33.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.33 Updating an extension method in the MyExtensionMethods.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public static class MyExtensionMethods {\n        \n        <b class=\"fm-bold\">public static decimal TotalPrices(<\/b>\n                <b class=\"fm-bold\">this IEnumerable&lt;Product?&gt; products) {<\/b>\n            decimal total = 0;\n            <b class=\"fm-bold\">foreach (Product? prod in products) {<\/b>\n                <b class=\"fm-bold\">total += prod?.Price ?? 0;<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            return total;\n        }\n    }\n}<\/pre>\n<p class=\"body\">The first parameter type has changed to <code class=\"fm-code-in-text\">IEnumerable&lt;Product?&gt;<\/code>, which means the <code class=\"fm-code-in-text\">foreach<\/code> loop in the method body works directly on <code class=\"fm-code-in-text\">Product?<\/code> objects. The change to using the interface means that I can calculate the total value of the <code class=\"fm-code-in-text\">Product<\/code> objects enumerated by any <code class=\"fm-code-in-text\">IEnumerable&lt;Product?&gt;<\/code>, which includes instances of <code class=\"fm-code-in-text\">ShoppingCart<\/code> but also arrays of <code class=\"fm-code-in-text\">Product<\/code> objects, as shown in listing 5.34.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.34 Applying an extension method in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            ShoppingCart cart \n                = new ShoppingCart { Products = Product.GetProducts()};\n                                \n            <b class=\"fm-bold\">Product[] productArray = {<\/b>\n                <b class=\"fm-bold\">new Product {Name = \"Kayak\", Price = 275M},<\/b>\n                <b class=\"fm-bold\">new Product {Name = \"Lifejacket\", Price = 48.95M}<\/b>\n            <b class=\"fm-bold\">};<\/b>\n                        \n            <b class=\"fm-bold\">decimal cartTotal = cart.TotalPrices();<\/b>\n            <b class=\"fm-bold\">decimal arrayTotal = productArray.TotalPrices();<\/b>\n                        \n            <b class=\"fm-bold\">return View(\"Index\", new string[] {<\/b>\n                <b class=\"fm-bold\">$\"Cart Total: {cartTotal:C2}\",<\/b>\n                <b class=\"fm-bold\">$\"Array Total: {arrayTotal:C2}\" });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000, which will produce the following output in the browser, demonstrating that I get the same result from the extension method, irrespective of how the <code class=\"fm-code-in-text\">Product<\/code> objects are collected:<\/p>\n<pre class=\"programlisting\">Cart Total: $323.95\nArray Total: $323.95 <\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-94\">5.9.2 Creating filtering extension methods<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1453\"><\/a>The last thing I want to show you about extension methods is that they can be used to filter collections of objects. An extension method that operates on an <code class=\"fm-code-in-text\">IEnumerable&lt;T&gt;<\/code> and that also returns an <code class=\"fm-code-in-text\">IEnumerable&lt;T&gt;<\/code> can use the <code class=\"fm-code-in-text\">yield<\/code> keyword to apply selection criteria to items in the source data to produce a reduced set of results. Listing 5.35 demonstrates such a method, which I have added to the <code class=\"fm-code-in-text\">MyExtensionMethods<\/code> class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.35 A filtering extension method in the MyExtensionMethods.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public static class MyExtensionMethods {\n        \n        public static decimal TotalPrices(\n                this IEnumerable&lt;Product?&gt; products) {\n            decimal total = 0;\n            foreach (Product? prod in products) {\n                total += prod?.Price ?? 0;\n            }\n            return total;\n        }\n                \n        <b class=\"fm-bold\">public static IEnumerable&lt;Product?&gt; FilterByPrice(<\/b>\n                <b class=\"fm-bold\">this IEnumerable&lt;Product?&gt; productEnum,<\/b> \n                <b class=\"fm-bold\">decimal minimumPrice) {<\/b>\n            <b class=\"fm-bold\">foreach (Product? prod in productEnum) {<\/b>\n                <b class=\"fm-bold\">if ((prod?.Price ?? 0) &gt;= minimumPrice) {<\/b>\n                    <b class=\"fm-bold\">yield return prod;<\/b>\n                <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">This extension method, called <code class=\"fm-code-in-text\">FilterByPrice<\/code>, takes an additional parameter that allows me to filter products so that <code class=\"fm-code-in-text\">Product<\/code> objects whose <code class=\"fm-code-in-text\">Price<\/code> property matches or exceeds the parameter are returned in the result. Listing 5.36 shows this method being used.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.36 Using the filtering extension method in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            ShoppingCart cart \n                = new ShoppingCart { Products = Product.GetProducts()};\n                                \n            <b class=\"fm-bold\">Product[] productArray = {<\/b>\n                <b class=\"fm-bold\">new Product {Name = \"Kayak\", Price = 275M},<\/b>\n                <b class=\"fm-bold\">new Product {Name = \"Lifejacket\", Price = 48.95M},<\/b>\n                <b class=\"fm-bold\">new Product {Name = \"Soccer ball\", Price = 19.50M},<\/b>\n                <b class=\"fm-bold\">new Product {Name = \"Corner flag\", Price = 34.95M}<\/b>\n            <b class=\"fm-bold\">};<\/b>\n                        \n            <b class=\"fm-bold\">decimal arrayTotal =<\/b> \n                <b class=\"fm-bold\">productArray.FilterByPrice(20).TotalPrices();<\/b>\n                                \n            <b class=\"fm-bold\">return View(\"Index\",<\/b> \n                <b class=\"fm-bold\">new string[] { $\"Array Total: {arrayTotal:C2}\" });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-859\"><\/a>When I call the <code class=\"fm-code-in-text\">FilterByPrice<\/code> method on the array of <code class=\"fm-code-in-text\">Product<\/code> objects, only those that cost more than $20 are received by the <code class=\"fm-code-in-text\">TotalPrices<\/code> method and used to calculate the total. If you run the application, you will see the following output in the browser window:<\/p>\n<pre class=\"programlisting\">Total: $358.90<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-95\">5.10 Using lambda expressions<\/h2>\n<p class=\"body\">Lambda expressions are a feature that causes a lot of confusion, not least because the feature they simplify is also confusing. To understand the problem that is being solved, consider the <code class=\"fm-code-in-text\">FilterByPrice<\/code> extension method that I defined in the previous section. This method is written so that it can filter <code class=\"fm-code-in-text\">Product<\/code> objects by price, which means I must create a second method if I want to filter by name, as shown in listing 5.37.<a id=\"calibre_link-1454\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.37 Adding a filter method in the MyExtensionMethods.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public static class MyExtensionMethods {\n        \n        public static decimal TotalPrices(\n                this IEnumerable&lt;Product?&gt; products) {\n            decimal total = 0;\n            foreach (Product? prod in products) {\n                total += prod?.Price ?? 0;\n            }\n            return total;\n        }\n                \n        public static IEnumerable&lt;Product?&gt; FilterByPrice(\n                this IEnumerable&lt;Product?&gt; productEnum, \n                decimal minimumPrice) {\n            foreach (Product? prod in productEnum) {\n                if ((prod?.Price ?? 0) &gt;= minimumPrice) {\n                    yield return prod;\n                }\n            }\n        }\n                \n        <b class=\"fm-bold\">public static IEnumerable&lt;Product?&gt; FilterByName(<\/b>\n                <b class=\"fm-bold\">this IEnumerable&lt;Product?&gt; productEnum,<\/b> \n                <b class=\"fm-bold\">char firstLetter) {<\/b>\n                                \n            <b class=\"fm-bold\">foreach (Product? prod in productEnum) {<\/b>\n                <b class=\"fm-bold\">if (prod?.Name?[0] == firstLetter) {<\/b>\n                    <b class=\"fm-bold\">yield return prod;<\/b>\n                <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1455\"><\/a>Listing 5.38 shows the use of both filter methods applied in the controller to create two different totals.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.38 Using two filter methods in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            ShoppingCart cart \n                = new ShoppingCart { Products = Product.GetProducts()};\n                                \n            Product[] productArray = {\n                new Product {Name = \"Kayak\", Price = 275M},\n                new Product {Name = \"Lifejacket\", Price = 48.95M},\n                new Product {Name = \"Soccer ball\", Price = 19.50M},\n                new Product {Name = \"Corner flag\", Price = 34.95M}\n            };\n                        \n            <b class=\"fm-bold\">decimal priceFilterTotal =<\/b> \n                <b class=\"fm-bold\">productArray.FilterByPrice(20).TotalPrices();<\/b>\n            <b class=\"fm-bold\">decimal nameFilterTotal =<\/b> \n                <b class=\"fm-bold\">productArray.FilterByName('S').TotalPrices();<\/b>\n                                \n            <b class=\"fm-bold\">return View(\"Index\", new string[] {<\/b>\n                <b class=\"fm-bold\">$\"Price Total: {priceFilterTotal:C2}\",<\/b>\n                <b class=\"fm-bold\">$\"Name Total: {nameFilterTotal:C2}\" });<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The first filter selects all the products with a price of $20 or more, and the second filter selects products whose name starts with the letter <i class=\"fm-italics\">S<\/i>. You will see the following output in the browser window if you run the example application:<\/p>\n<pre class=\"programlisting\">Price Total: $358.90\nName Total: $19.50<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-96\">5.10.1 Defining functions<\/h3>\n<p class=\"body\"><a id=\"calibre_link-861\"><\/a>I can repeat this process indefinitely to create filter methods for every property and every combination of properties that I am interested in. A more elegant approach is to separate the code that processes the enumeration from the selection criteria. C# makes this easy by allowing functions to be passed around as objects. Listing 5.39 shows a single extension method that filters an enumeration of <code class=\"fm-code-in-text\">Product<\/code> objects but that delegates the decision about which ones are included in the results to a separate function.<a id=\"calibre_link-1456\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.39 Creating a general filter method in the MyExtensionMethods.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public static class MyExtensionMethods {\n        \n        public static decimal TotalPrices(\n                this IEnumerable&lt;Product?&gt; products) {\n            decimal total = 0;\n            foreach (Product? prod in products) {\n                total += prod?.Price ?? 0;\n            }\n            return total;\n        }\n                \n        public static IEnumerable&lt;Product?&gt; FilterByPrice(\n                this IEnumerable&lt;Product?&gt; productEnum, \n                decimal minimumPrice) {\n            foreach (Product? prod in productEnum) {\n                if ((prod?.Price ?? 0) &gt;= minimumPrice) {\n                    yield return prod;\n                }\n            }\n        }\n                \n        <b class=\"fm-bold\">public static IEnumerable&lt;Product?&gt; Filter(<\/b>\n                <b class=\"fm-bold\">this IEnumerable&lt;Product?&gt; productEnum,<\/b>\n                <b class=\"fm-bold\">Func&lt;Product?, bool&gt; selector) {<\/b>\n                                \n            <b class=\"fm-bold\">foreach (Product? prod in productEnum) {<\/b>\n                <b class=\"fm-bold\">if (selector(prod)) {<\/b>\n                    <b class=\"fm-bold\">yield return prod;<\/b>\n                <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The second argument to the <code class=\"fm-code-in-text\">Filter<\/code> method is a function that accepts a <code class=\"fm-code-in-text\">Product?<\/code> object and that returns a <code class=\"fm-code-in-text\">bool<\/code> value. The <code class=\"fm-code-in-text\">Filter<\/code> method calls the function for each <code class=\"fm-code-in-text\">Product?<\/code> object and includes it in the result if the function returns <code class=\"fm-code-in-text\">true<\/code>. To use the <code class=\"fm-code-in-text\">Filter<\/code> method, I can specify a method or create a stand-alone function, as shown in listing 5.40.<a id=\"calibre_link-1457\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.40 Using a function to filter objects in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        <b class=\"fm-bold\">bool FilterByPrice(Product? p) {<\/b>\n            <b class=\"fm-bold\">return (p?.Price ?? 0) &gt;= 20;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        public ViewResult Index() {\n            ShoppingCart cart \n                = new ShoppingCart { Products = Product.GetProducts()};\n                                \n            Product[] productArray = {\n                new Product {Name = \"Kayak\", Price = 275M},\n                new Product {Name = \"Lifejacket\", Price = 48.95M},\n                new Product {Name = \"Soccer ball\", Price = 19.50M},\n                new Product {Name = \"Corner flag\", Price = 34.95M}\n            };\n                        \n            <b class=\"fm-bold\">Func&lt;Product?, bool&gt; nameFilter = delegate (Product? prod) {<\/b>\n                <b class=\"fm-bold\">return prod?.Name?[0] == 'S';<\/b>\n            <b class=\"fm-bold\">};<\/b>\n                        \n            <b class=\"fm-bold\">decimal priceFilterTotal = productArray<\/b>\n                <b class=\"fm-bold\">.Filter(FilterByPrice)<\/b>\n                <b class=\"fm-bold\">.TotalPrices();<\/b>\n            <b class=\"fm-bold\">decimal nameFilterTotal = productArray<\/b>\n                <b class=\"fm-bold\">.Filter(nameFilter)<\/b>\n                <b class=\"fm-bold\">.TotalPrices();<\/b>\n                                \n            return View(\"Index\", new string[] {\n                $\"Price Total: {priceFilterTotal:C2}\",\n                $\"Name Total: {nameFilterTotal:C2}\" });\n        }\n    }\n}<\/pre>\n<p class=\"body\">Neither approach is ideal. Defining methods like <code class=\"fm-code-in-text\">FilterByPrice<\/code> clutters up a class definition. Creating a <code class=\"fm-code-in-text\">Func&lt;Product?,<\/code> <code class=\"fm-code-in-text\">bool&gt;<\/code> object avoids this problem but uses an awkward syntax that is hard to read and hard to maintain. It is this issue that lambda expressions address by allowing functions to be defined in a more elegant and expressive way, as shown in listing 5.41.<a id=\"calibre_link-1458\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.41 Using a lambda expression in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        <b class=\"fm-bold\">\/\/bool FilterByPrice(Product? p) {<\/b>\n        <b class=\"fm-bold\">\/\/    return (p?.Price ?? 0) &gt;= 20;<\/b>\n        <b class=\"fm-bold\">\/\/}<\/b>\n                \n        public ViewResult Index() {\n            ShoppingCart cart \n                = new ShoppingCart { Products = Product.GetProducts()};\n                                \n            Product[] productArray = {\n                new Product {Name = \"Kayak\", Price = 275M},\n                new Product {Name = \"Lifejacket\", Price = 48.95M},\n                new Product {Name = \"Soccer ball\", Price = 19.50M},\n                new Product {Name = \"Corner flag\", Price = 34.95M}\n            };\n                        \n            <b class=\"fm-bold\">\/\/Func&lt;Product?, bool&gt; nameFilter = delegate (Product? prod) {<\/b>\n            <b class=\"fm-bold\">\/\/    return prod?.Name?[0] == 'S';<\/b>\n            <b class=\"fm-bold\">\/\/};<\/b>\n                        \n            decimal priceFilterTotal = productArray\n                <b class=\"fm-bold\">.Filter(p =&gt; (p?.Price ?? 0) &gt;= 20)<\/b>\n                .TotalPrices();\n                                \n            decimal nameFilterTotal = productArray\n                <b class=\"fm-bold\">.Filter(p =&gt; p?.Name?[0] ==<\/b> <b class=\"fm-bold\">'S')<\/b>\n                .TotalPrices();\n                                \n            return View(\"Index\", new string[] {\n                $\"Price Total: {priceFilterTotal:C2}\",\n                $\"Name Total: {nameFilterTotal:C2}\" });\n        }\n    }\n}<\/pre>\n<p class=\"body\">The lambda expressions are shown in bold. The parameters are expressed without specifying a type, which will be inferred automatically. The <code class=\"fm-code-in-text\">=&gt;<\/code> characters are read aloud as \u201cgoes to\u201d and link the parameter to the result of the lambda expression. In my examples, a <code class=\"fm-code-in-text\">Product?<\/code> parameter called <code class=\"fm-code-in-text\">p<\/code> goes to a <code class=\"fm-code-in-text\">bool<\/code> result, which will be <code class=\"fm-code-in-text\">true<\/code> if the <code class=\"fm-code-in-text\">Price<\/code> property is equal or greater than 20 in the first expression or if the <code class=\"fm-code-in-text\">Name<\/code> property starts with <i class=\"fm-italics\">S<\/i> in the second expression. This code works in the same way as the separate method and the function delegate but is more concise and is&mdash;for most people&mdash;easier to read.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Other Forms for Lambda Expressions<\/p>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-860\"><\/a>I don\u2019t need to express the logic of my delegate in the lambda expression. I can as easily call a method, like this:<a id=\"calibre_link-1459\"><\/a><\/p>\n<pre class=\"programlisting\">...\nprod =&gt; EvaluateProduct(prod)\n...<\/pre>\n<p class=\"fm-sidebar-text\">If I need a lambda expression for a delegate that has multiple parameters, I must wrap the parameters in parentheses, like this:<\/p>\n<pre class=\"programlisting\">...\n(prod, count) =&gt; prod.Price &gt; 20 &amp;&amp; count &gt; 0\n...<\/pre>\n<p class=\"fm-sidebar-text\">Finally, if I need logic in the lambda expression that requires more than one statement, I can do so by using braces (<code class=\"fm-code-in-text1\">{}<\/code>) and finishing with a <code class=\"fm-code-in-text1\">return<\/code> statement, like this:<\/p>\n<pre class=\"programlisting\">...\n(prod, count) =&gt; {\n    \/\/ ...multiple code statements...\n    return result;\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">You do not need to use lambda expressions in your code, but they are a neat way of expressing complex functions simply and in a manner that is readable and clear. I like them a lot, and you will see them used throughout this book.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-97\">5.10.2 Using lambda expression methods and properties<\/h3>\n<p class=\"body\">Lambda expressions can be used to implement constructors, methods, and properties. In ASP.NET Core development, you will often end up with methods that contain a single statement that selects the data to display and the view to render. In listing 5.42, I have rewritten the <code class=\"fm-code-in-text\">Index<\/code> action method so that it follows this common pattern.<a id=\"calibre_link-1460\"><\/a><a id=\"calibre_link-1461\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.42 Creating a common action pattern in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            <b class=\"fm-bold\">return View(Product.GetProducts().Select(p =&gt; p?.Name));<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The action method gets a collection of <code class=\"fm-code-in-text\">Product<\/code> objects from the static <code class=\"fm-code-in-text\">Product.GetProducts<\/code> method and uses LINQ to project the values of the <code class=\"fm-code-in-text\">Name<\/code> properties, which are then used as the view model for the default view. If you run the application, you will see the following output displayed in the browser window:<\/p>\n<pre class=\"programlisting\">Kayak<\/pre>\n<p class=\"body\">There will be empty list items in the browser window as well because the <code class=\"fm-code-in-text\">GetProducts<\/code> method includes a <code class=\"fm-code-in-text\">null<\/code> reference in its results and one of the Product objects is created without a <code class=\"fm-code-in-text\">Name<\/code> value, but that doesn\u2019t matter for this section of the chapter.<a id=\"calibre_link-1462\"><\/a><\/p>\n<p class=\"body\">When a constructor or method body consists of a single statement, it can be rewritten as a lambda expression, as shown in listing 5.43.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.43 A lambda action method in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        <b class=\"fm-bold\">public ViewResult Index() =&gt;<\/b> \n            <b class=\"fm-bold\">View(Product.GetProducts().Select(p =&gt; p?.Name));<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Lambda expressions for methods omit the <code class=\"fm-code-in-text\">return<\/code> keyword and use <code class=\"fm-code-in-text\">=&gt;<\/code> (goes to) to associate the method signature (including its arguments) with its implementation. The <code class=\"fm-code-in-text\">Index<\/code> method shown in listing 5.43 works in the same way as the one shown in listing 5.42 but is expressed more concisely. The same basic approach can also be used to define properties. Listing 5.44 shows the addition of a property that uses a lambda expression to the <code class=\"fm-code-in-text\">Product<\/code> class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.44 A lambda property in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n    public class Product {\n        \n        public string Name { get; set; } = string.Empty;\n        public decimal? Price { get; set; }\n                \n        <b class=\"fm-bold\">public bool NameBeginsWithS =&gt;  Name.Length &gt; 0 &amp;&amp; Name[0] == 'S';<\/b>\n                \n        public static Product?[] GetProducts() {\n            Product kayak = new Product {\n                Name = \"Kayak\", Price = 275M\n            };\n                        \n            Product lifejacket = new Product {\n                \/\/Name = \"Lifejacket\", \n                Price = 48.95M\n            };\n                        \n            return new Product?[] { kayak, lifejacket, null };\n        }\n    }\n}<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-98\">5.11 Using type inference and anonymous types<\/h2>\n<p class=\"body\"><a id=\"calibre_link-849\"><\/a>The <code class=\"fm-code-in-text\">var<\/code> keyword allows you to define a local variable without explicitly specifying the variable type, as demonstrated by listing 5.45. This is called <i class=\"fm-italics\">type inference<\/i>, or <i class=\"fm-italics\">implicit typing<\/i>.<a id=\"calibre_link-1463\"><\/a><a id=\"calibre_link-1464\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.45 Using type inference in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        <b class=\"fm-bold\">public ViewResult Index() {<\/b>\n            <b class=\"fm-bold\">var names = new[] { \"Kayak\", \"Lifejacket\", \"Soccer ball\" };<\/b>\n            <b class=\"fm-bold\">return View(names);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">It is not that the <code class=\"fm-code-in-text\">names<\/code> variable does not have a type; instead, I am asking the compiler to infer the type from the code. The compiler examines the array declaration and works out that it is a string array. Running the example produces the following output:<\/p>\n<pre class=\"programlisting\">Kayak\nLifejacket\nSoccer ball<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-99\">5.11.1 Using anonymous types<\/h3>\n<p class=\"body\">By combining object initializers and type inference, I can create simple view model objects that are useful for transferring data between a controller and a view without having to define a class or struct, as shown in listing 5.46.<a id=\"calibre_link-1465\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.46 An anonymous type in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        public ViewResult Index() {\n                \n            <b class=\"fm-bold\">var products = new[] {<\/b>\n                <b class=\"fm-bold\">new { Name = \"Kayak\", Price = 275M },<\/b>\n                <b class=\"fm-bold\">new { Name = \"Lifejacket\", Price = 48.95M },<\/b>\n                <b class=\"fm-bold\">new { Name = \"Soccer ball\", Price = 19.50M },<\/b>\n                <b class=\"fm-bold\">new { Name = \"Corner flag\", Price = 34.95M }<\/b>\n            <b class=\"fm-bold\">};<\/b>\n                        \n            <b class=\"fm-bold\">return View(products.Select(p =&gt; p.Name));<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">Each of the objects in the <code class=\"fm-code-in-text\">products<\/code> array is an anonymously typed object. This does not mean that it is dynamic in the sense that JavaScript variables are dynamic. It just means that the type definition will be created automatically by the compiler. Strong typing is still enforced. You can get and set only the properties that have been defined in the initializer, for example. Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the following output in the browser window:<\/p>\n<pre class=\"programlisting\">Kayak\nLifejacket\nSoccer ball\nCorner flag<\/pre>\n<p class=\"body\">The C# compiler generates the class based on the name and type of the parameters in the initializer. Two anonymously typed objects that have the same property names and types defined in the same order will be assigned to the same automatically generated class. This means that all the objects in the <code class=\"fm-code-in-text\">products<\/code> array will have the same type because they define the same properties.<a id=\"calibre_link-1466\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> I have to use the <code class=\"fm-code-in-text1\">var<\/code> keyword to define the array of anonymously typed objects because the type isn\u2019t created until the code is compiled, so I don\u2019t know the name of the type to use. The elements in an array of anonymously typed objects must all define the same properties; otherwise, the compiler can\u2019t work out what the array type should be.<\/p>\n<p class=\"body\">To demonstrate this, I have changed the output from the example in listing 5.47 so that it shows the type name rather than the value of the <code class=\"fm-code-in-text\">Name<\/code> property.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.47 Displaying the type name in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            var products = new[] {\n                new { Name = \"Kayak\", Price = 275M },\n                new { Name = \"Lifejacket\", Price = 48.95M },\n                new { Name = \"Soccer ball\", Price = 19.50M },\n                new { Name = \"Corner flag\", Price = 34.95M }\n            };\n                        \n            <b class=\"fm-bold\">return View(products.Select(p =&gt; p.GetType().Name));<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">All the objects in the array have been assigned the same type, which you can see if you run the example. The type name isn\u2019t user-friendly but isn\u2019t intended to be used directly, and you may see a different name than the one shown in the following output:<\/p>\n<pre class=\"programlisting\">&lt;&gt;f__AnonymousType0`2\n&lt;&gt;f__AnonymousType0`2\n&lt;&gt;f__AnonymousType0`2\n&lt;&gt;f__AnonymousType0`2<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-100\">5.12 Using default implementations in interfaces<\/h2>\n<p class=\"body\"><a id=\"calibre_link-858\"><\/a>C# provides the ability to define default implementations for properties and methods defined by interfaces. This may seem like an odd feature because an interface is intended to be a description of features without specifying an implementation, but this addition to C# makes it possible to update interfaces without breaking the existing implementations of them.<a id=\"calibre_link-1467\"><\/a><\/p>\n<p class=\"body\">Add a class file named <code class=\"fm-code-in-text\">IProductSelection.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the interface shown in listing 5.48.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.48 The contents of the IProductSelection.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public interface IProductSelection {\n        \n        IEnumerable&lt;Product&gt;? Products { get; }\n    }\n}<\/pre>\n<p class=\"body\">Update the <code class=\"fm-code-in-text\">ShoppingCart<\/code> class to implement the new interface, as shown in listing 5.49.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.49 Implementing an interface in the ShoppingCart.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    <b class=\"fm-bold\">public class ShoppingCart : IProductSelection {<\/b>\n        <b class=\"fm-bold\">private List&lt;Product&gt; products = new();<\/b>\n                \n        <b class=\"fm-bold\">public ShoppingCart(params Product[] prods) {<\/b>\n            <b class=\"fm-bold\">products.AddRange(prods);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">public IEnumerable&lt;Product&gt;? Products { get =&gt; products; }<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Listing 5.50 updates the <code class=\"fm-code-in-text\">Home<\/code> controller so that it uses the <code class=\"fm-code-in-text\">ShoppingCart<\/code> class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.50 Using an interface in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            <b class=\"fm-bold\">IProductSelection cart = new ShoppingCart(<\/b>\n                <b class=\"fm-bold\">new Product { Name = \"Kayak\", Price = 275M },<\/b>\n                <b class=\"fm-bold\">new Product { Name = \"Lifejacket\", Price = 48.95M },<\/b>\n                <b class=\"fm-bold\">new Product { Name = \"Soccer ball\", Price = 19.50M },<\/b>\n                <b class=\"fm-bold\">new Product { Name = \"Corner flag\", Price = 34.95M }<\/b>\n            <b class=\"fm-bold\">);<\/b>\n            <b class=\"fm-bold\">return View(cart.Products?.Select(p =&gt; p.Name));<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">This is the familiar use of an interface, and if you restart ASP.NET Core and request http:\/\/localhost:5000, you will see the following output in the browser:<\/p>\n<pre class=\"programlisting\">Kayak\nLifejacket\nSoccer ball\nCorner flag<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1468\"><\/a>If I want to add a new feature to the interface, I must locate and update all the classes that implement it, which can be difficult, especially if an interface is used by other development teams in their projects. This is where the default implementation feature can be used, allowing new features to be added to an interface, as shown in listing 5.51.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.51 Adding a feature in the IProductSelection.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public interface IProductSelection {\n        \n        IEnumerable&lt;Product&gt;? Products { get; }\n                \n        <b class=\"fm-bold\">IEnumerable&lt;string&gt;? Names =&gt; Products?.Select(p =&gt; p.Name);<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The listing defines a <code class=\"fm-code-in-text\">Names<\/code> property and provides a default implementation, which means that consumers of the <code class=\"fm-code-in-text\">IProductSelection<\/code> interface can use the <code class=\"fm-code-in-text\">Names<\/code> property even if it isn\u2019t defined by implementation classes, as shown in listing 5.52.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.52 Using a default implementation in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            IProductSelection cart = new ShoppingCart(\n                new Product { Name = \"Kayak\", Price = 275M },\n                new Product { Name = \"Lifejacket\", Price = 48.95M },\n                new Product { Name = \"Soccer ball\", Price = 19.50M },\n                new Product { Name = \"Corner flag\", Price = 34.95M }\n            );\n            <b class=\"fm-bold\">return View(cart.Names);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ShoppingCart<\/code> class has not been modified, but the <code class=\"fm-code-in-text\">Index<\/code> method can use the default implementation of the <code class=\"fm-code-in-text\">Names<\/code> property. Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the following output in the browser:<\/p>\n<pre class=\"programlisting\">Kayak\nLifejacket\nSoccer ball\nCorner flag<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-101\">5.13 Using asynchronous methods<\/h2>\n<p class=\"body\"><a id=\"calibre_link-850\"><\/a>Asynchronous methods perform work in the background and notify you when they are complete, allowing your code to take care of other business while the background work is performed. Asynchronous methods are an important tool in removing bottlenecks from code and allow applications to take advantage of multiple processors and processor cores to perform work in parallel.<\/p>\n<p class=\"body\">In ASP.NET Core, asynchronous methods can be used to improve the overall performance of an application by allowing the server more flexibility in the way that requests are scheduled and executed. Two C# keywords&mdash;<code class=\"fm-code-in-text\">async<\/code> and <code class=\"fm-code-in-text\">await<\/code>&mdash;are used to perform work asynchronously.<a id=\"calibre_link-1469\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-102\">5.13.1 Working with tasks directly<\/h3>\n<p class=\"body\">C# and .NET have excellent support for asynchronous methods, but the code has tended to be verbose, and developers who are not used to parallel programming often get bogged down by the unusual syntax. To create an example, add a class file called <code class=\"fm-code-in-text\">MyAsyncMethods.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and add the code shown in listing 5.53.<a id=\"calibre_link-1470\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.53 The contents of the MyAsyncMethods.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public class MyAsyncMethods {\n        \n        public static Task&lt;long?&gt; GetPageLength() {\n            HttpClient client = new HttpClient();\n            var httpTask = client.GetAsync(\"http:\/\/manning.com\");\n            return httpTask.ContinueWith((Task&lt;HttpResponseMessage&gt; \n                    antecedent) =&gt; {\n                return antecedent.Result.Content.Headers.ContentLength;\n            });\n        }\n    }\n}<\/pre>\n<p class=\"body\">This method uses a <code class=\"fm-code-in-text\">System.Net.Http.HttpClient<\/code> object to request the contents of the Manning home page and returns its length. .NET represents work that will be done asynchronously as a <code class=\"fm-code-in-text\">Task<\/code>. <code class=\"fm-code-in-text\">Task<\/code> objects are strongly typed based on the result that the background work produces. So, when I call the <code class=\"fm-code-in-text\">HttpClient.GetAsync<\/code> method, what I get back is a <code class=\"fm-code-in-text\">Task&lt;HttpResponseMessage&gt;<\/code>. This tells me that the request will be performed in the background and that the result of the request will be an <code class=\"fm-code-in-text\">Http<\/code><code class=\"fm-code-in-text\">ResponseMessage<\/code> object.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> When I use words like <i class=\"fm-italics\">background<\/i>, I am skipping over a lot of detail to make just the key points that are important to the world of ASP.NET Core. The .NET support for asynchronous methods and parallel programming is excellent, and I encourage you to learn more about it if you want to create truly high-performing applications that can take advantage of multicore and multiprocessor hardware. You will see how ASP.NET Core makes it easy to create asynchronous web applications throughout this book as I introduce different features.<\/p>\n<p class=\"body\"><a id=\"calibre_link-852\"><\/a>The part that most programmers get bogged down with is the <i class=\"fm-italics\">continuation<\/i>, which is the mechanism by which you specify what you want to happen when the task is complete. In the example, I have used the <code class=\"fm-code-in-text\">ContinueWith<\/code> method to process the <code class=\"fm-code-in-text\">HttpResponseMessage<\/code> object I get from the <code class=\"fm-code-in-text\">HttpClient.GetAsync<\/code> method, which I do with a lambda expression that returns the value of a property that contains the length of the content I get from the Manning web server. Here is the continuation code:<\/p>\n<pre class=\"programlisting\">...\nreturn httpTask.ContinueWith((Task&lt;HttpResponseMessage&gt; antecedent) =&gt; {\n    return antecedent.Result.Content.Headers.ContentLength;\n});\n...<\/pre>\n<p class=\"body\">Notice that I use the <code class=\"fm-code-in-text\">return<\/code> keyword twice. This is the part that causes confusion. The first use of the <code class=\"fm-code-in-text\">return<\/code> keyword specifies that I am returning a <code class=\"fm-code-in-text\">Task&lt;HttpResponseMessage&gt;<\/code> object, which, when the task is complete, will <code class=\"fm-code-in-text\">return<\/code> the length of the <code class=\"fm-code-in-text\">ContentLength<\/code> header. The <code class=\"fm-code-in-text\">ContentLength<\/code> header returns a <code class=\"fm-code-in-text\">long?<\/code> result (a nullable long value), and this means the result of my <code class=\"fm-code-in-text\">GetPageLength<\/code> method is <code class=\"fm-code-in-text\">Task&lt;long?&gt;<\/code>, like this:<\/p>\n<pre class=\"programlisting\">...\npublic static <b class=\"fm-bold\">Task&lt;long?&gt;<\/b> GetPageLength() {\n...<\/pre>\n<p class=\"body\">Do not worry if this does not make sense&mdash;you are not alone in your confusion. It is for this reason that Microsoft added keywords to C# to simplify asynchronous methods.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-103\">5.13.2 Applying the async and await keywords<\/h3>\n<p class=\"body\">Microsoft introduced two keywords to C# that simplify using asynchronous methods like <code class=\"fm-code-in-text\">HttpClient.GetAsync<\/code>. The keywords are <code class=\"fm-code-in-text\">async<\/code> and <code class=\"fm-code-in-text\">await<\/code>, and you can see how I have used them to simplify my example method in listing 5.54.<a id=\"calibre_link-1471\"><\/a><a id=\"calibre_link-1472\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.54 Using the async and await keywords in the MyAsyncMethods.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public class MyAsyncMethods {\n        \n        public async static Task&lt;long?&gt; GetPageLength() {\n            HttpClient client = new HttpClient();\n            var httpMessage = await client.GetAsync(\"http:\/\/manning.com\");\n            return httpMessage.Content.Headers.ContentLength;\n        }\n    }\n}<\/pre>\n<p class=\"body\">I used the <code class=\"fm-code-in-text\">await<\/code> keyword when calling the asynchronous method. This tells the C# compiler that I want to wait for the result of the <code class=\"fm-code-in-text\">Task<\/code> that the <code class=\"fm-code-in-text\">GetAsync<\/code> method returns and then carry on executing other statements in the same method.<\/p>\n<p class=\"body\">Applying the <code class=\"fm-code-in-text\">await<\/code> keyword means I can treat the result from the <code class=\"fm-code-in-text\">GetAsync<\/code> method as though it were a regular method and just assign the <code class=\"fm-code-in-text\">HttpResponseMessage<\/code> object that it returns to a variable. Even better, I can then use the <code class=\"fm-code-in-text\">return<\/code> keyword in the normal way to produce a result from another method&mdash;in this case, the value of the <code class=\"fm-code-in-text\">ContentLength<\/code> property. This is a much more natural technique, and it means I do not have to worry about the <code class=\"fm-code-in-text\">ContinueWith<\/code> method and multiple uses of the <code class=\"fm-code-in-text\">return<\/code> keyword.<\/p>\n<p class=\"body\"><a id=\"calibre_link-1473\"><\/a>When you use the <code class=\"fm-code-in-text\">await<\/code> keyword, you must also add the <code class=\"fm-code-in-text\">async<\/code> keyword to the method signature, as I have done in the example. The method result type does not change&mdash;my example <code class=\"fm-code-in-text\">GetPageLength<\/code> method still returns a <code class=\"fm-code-in-text\">Task&lt;long?&gt;<\/code>. This is because <code class=\"fm-code-in-text\">await<\/code> and <code class=\"fm-code-in-text\">async<\/code> are implemented using some clever compiler tricks, meaning that they allow a more natural syntax, but they do not change what is happening in the methods to which they are applied. Someone who is calling my <code class=\"fm-code-in-text\">GetPageLength<\/code> method still has to deal with a <code class=\"fm-code-in-text\">Task&lt;long?&gt;<\/code> result because there is still a background operation that produces a <code class=\"fm-code-in-text\">nullable<\/code> long&mdash;although, of course, that programmer can also choose to use the <code class=\"fm-code-in-text\">await<\/code> and <code class=\"fm-code-in-text\">async<\/code> keywords.<\/p>\n<p class=\"body\">This pattern follows through into the controller, which makes it easy to write asynchronous action methods, as shown in listing 5.55.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> You can also use the <code class=\"fm-code-in-text1\">async<\/code> and <code class=\"fm-code-in-text1\">await<\/code> keywords in lambda expressions, which I demonstrate in later chapters.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.55 An asynchronous action method in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        <b class=\"fm-bold\">public async Task&lt;ViewResult&gt; Index() {<\/b>\n            <b class=\"fm-bold\">long? length = await MyAsyncMethods.GetPageLength();<\/b>\n            <b class=\"fm-bold\">return View(new string[] { $\"Length: {length}\" });<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">I have changed the result of the <code class=\"fm-code-in-text\">Index<\/code> action method to <code class=\"fm-code-in-text\">Task&lt;ViewResult&gt;<\/code>, which declares that the action method will return a <code class=\"fm-code-in-text\">Task<\/code> that will produce a <code class=\"fm-code-in-text\">ViewResult<\/code> object when it completes, which will provide details of the view that should be rendered and the data that it requires. I have added the <code class=\"fm-code-in-text\">async<\/code> keyword to the method\u2019s definition, which allows me to use the <code class=\"fm-code-in-text\">await<\/code> keyword when calling the <code class=\"fm-code-in-text\">MyAsyncMethods.GetPathLength<\/code> method. .NET takes care of dealing with the continuations, and the result is asynchronous code that is easy to write, easy to read, and easy to maintain. Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see output similar to the following (although with a different length since the content of the Manning website changes often):<\/p>\n<pre class=\"programlisting\">Length: 472922<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-104\">5.13.3 Using an asynchronous enumerable<\/h3>\n<p class=\"body\"><a id=\"calibre_link-851\"><\/a>An asynchronous enumerable describes a sequence of values that will be generated over time. To demonstrate the issue that this feature addresses, listing 5.56 adds a method to the <code class=\"fm-code-in-text\">MyAsyncMethods<\/code> class.<a id=\"calibre_link-1474\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.56 Adding a method in the MyAsyncMethods.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public class MyAsyncMethods {\n        \n        public async static Task&lt;long?&gt; GetPageLength() {\n            HttpClient client = new HttpClient();\n            var httpMessage = await client.GetAsync(\"http:\/\/manning.com\");\n            return httpMessage.Content.Headers.ContentLength;\n        }\n                \n        <b class=\"fm-bold\">public static async Task&lt;IEnumerable&lt;long?&gt;&gt;<\/b>\n                <b class=\"fm-bold\">GetPageLengths(List&lt;string&gt; output,<\/b> \n                    <b class=\"fm-bold\">params string[] urls) {<\/b>\n            <b class=\"fm-bold\">List&lt;long?&gt; results = new List&lt;long?&gt;();<\/b>\n            <b class=\"fm-bold\">HttpClient client = new HttpClient();<\/b>\n            <b class=\"fm-bold\">foreach (string url in urls) {<\/b>\n                <b class=\"fm-bold\">output.Add($\"Started request for {url}\");<\/b>\n                <b class=\"fm-bold\">var httpMessage = await client.GetAsync($\"http:\/\/{url}\");<\/b>\n                <b class=\"fm-bold\">results.Add(httpMessage.Content.Headers.ContentLength);<\/b>\n                <b class=\"fm-bold\">output.Add($\"Completed request for {url}\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return results;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">GetPageLengths<\/code> method makes HTTP requests to a series of websites and gets their length. The requests are performed asynchronously, but there is no way to feed the results back to the method\u2019s caller as they arrive. Instead, the method waits until all the requests are complete and then returns all the results in one go. In addition to the URLs that will be requested, this method accepts a <code class=\"fm-code-in-text\">List&lt;string&gt;<\/code> to which I add messages in order to highlight how the code works. Listing 5.57 updates the <code class=\"fm-code-in-text\">Index<\/code> action method of the <code class=\"fm-code-in-text\">Home<\/code> controller to use the new method.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.57 Using the new method in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public async Task&lt;ViewResult&gt; Index() {\n            <b class=\"fm-bold\">List&lt;string&gt; output = new List&lt;string&gt;();<\/b>\n            <b class=\"fm-bold\">foreach (long? len in await MyAsyncMethods.GetPageLengths(<\/b>\n                    <b class=\"fm-bold\">output,<\/b>\n                    <b class=\"fm-bold\">\"manning.com\", \"microsoft.com\", \"amazon.com\")) {<\/b>\n                <b class=\"fm-bold\">output.Add($\"Page length: { len}\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return View(output);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1475\"><\/a>The action method enumerates the sequence produced by the <code class=\"fm-code-in-text\">GetPageLengths<\/code> method and adds each result to the <code class=\"fm-code-in-text\">List&lt;string&gt;<\/code> object, which produces an ordered sequence of messages showing the interaction between the <code class=\"fm-code-in-text\">foreach<\/code> loop in the <code class=\"fm-code-in-text\">Index<\/code> method that processes the results and the <code class=\"fm-code-in-text\">foreach<\/code> loop in the <code class=\"fm-code-in-text\">GetPageLengths<\/code> method that generates them. Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the following output in the browser (which may take several seconds to appear and may have different page lengths):<\/p>\n<pre class=\"programlisting\">Started request for manning.com\nCompleted request for manning.com\nStarted request for microsoft.com\nCompleted request for microsoft.com\nStarted request for amazon.com\nCompleted request for amazon.com\nPage length: 26973\nPage length: 199526\nPage length: 357777<\/pre>\n<p class=\"body\">You can see that the <code class=\"fm-code-in-text\">Index<\/code> action method doesn\u2019t receive the results until all the HTTP requests have been completed. This is the problem that the asynchronous enumerable feature solves, as shown in listing 5.58.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.58 Using an asynchronous enumerable in the MyAsyncMethods.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Models {\n\n    public class MyAsyncMethods {\n        \n        public async static Task&lt;long?&gt; GetPageLength() {\n            HttpClient client = new HttpClient();\n            var httpMessage = await client.GetAsync(\"http:\/\/manning.com\");\n            return httpMessage.Content.Headers.ContentLength;\n        }\n                \n        <b class=\"fm-bold\">public static async IAsyncEnumerable&lt;long?&gt;<\/b>\n                <b class=\"fm-bold\">GetPageLengths(List&lt;string&gt; output,<\/b> \n                    <b class=\"fm-bold\">params string[] urls) {<\/b>\n            <b class=\"fm-bold\">HttpClient client = new HttpClient();<\/b>\n            <b class=\"fm-bold\">foreach (string url in urls) {<\/b>\n                <b class=\"fm-bold\">output.Add($\"Started request for {url}\");<\/b>\n                <b class=\"fm-bold\">var httpMessage = await client.GetAsync($\"http:\/\/{url}\");<\/b>\n                <b class=\"fm-bold\">output.Add($\"Completed request for {url}\");<\/b>\n                <b class=\"fm-bold\">yield return httpMessage.Content.Headers.ContentLength;<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The methods result is <code class=\"fm-code-in-text\">IAsyncEnumerable&lt;long?&gt;<\/code>, which denotes an asynchronous sequence of nullable long values. This result type has special support in .NET Core and works with standard <code class=\"fm-code-in-text\">yield return<\/code> statements, which isn\u2019t otherwise possible because the result constraints for asynchronous methods conflict with the <code class=\"fm-code-in-text\">yield<\/code> keyword. Listing 5.59 updates the controller to use the revised method.<a id=\"calibre_link-1476\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.59 Using an asynchronous enumerable in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public async Task&lt;ViewResult&gt; Index() {\n            List&lt;string&gt; output = new List&lt;string&gt;();\n            <b class=\"fm-bold\">await foreach (long? len in MyAsyncMethods.GetPageLengths(<\/b>\n                    <b class=\"fm-bold\">output,<\/b>\n                    <b class=\"fm-bold\">\"manning.com\", \"microsoft.com\", \"amazon.com\")) {<\/b>\n                output.Add($\"Page length: { len}\");\n            }\n            return View(output);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The difference is that the <code class=\"fm-code-in-text\">await<\/code> keyword is applied before the <code class=\"fm-code-in-text\">foreach<\/code> keyword and not before the call to the <code class=\"fm-code-in-text\">async<\/code> method. Restart ASP.NET Core and request http:\/\/localhost:5000; once the HTTP requests are complete, you will see that the order of the response messages has changed, like this:<\/p>\n<pre class=\"programlisting\">Started request for manning.com\nCompleted request for manning.com\nPage length: 26973\nStarted request for microsoft.com\nCompleted request for microsoft.com\nPage length: 199528\nStarted request for amazon.com\nCompleted request for amazon.com\nPage length: 441398<\/pre>\n<p class=\"body\">The controller receives the next result in the sequence as it is produced. As I explain in chapter 19, ASP.NET Core has special support for using <code class=\"fm-code-in-text\">IAsyncEnumerable&lt;T&gt;<\/code> results in web services, allowing data values to be serialized as the values in the sequence are generated.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-105\">5.14 Getting names<\/h2>\n<p class=\"body\">There are many tasks in web application development in which you need to refer to the name of an argument, variable, method, or class. Common examples include when you throw an exception or create a validation error when processing input from the user. The traditional approach has been to use a string value hard-coded with the name, as shown in listing 5.60.<a id=\"calibre_link-1477\"><\/a><a id=\"calibre_link-862\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.60 Hard-coding a name in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            var products = new[] {\n                new { Name = \"Kayak\", Price = 275M },\n                new { Name = \"Lifejacket\", Price = 48.95M },\n                new { Name = \"Soccer ball\", Price = 19.50M },\n                new { Name = \"Corner flag\", Price = 34.95M }\n            };\n            return View(products.Select(p =&gt; \n                $\"Name: {p.Name}, Price: {p.Price}\"));\n        }\n    }\n}<\/pre>\n<p class=\"body\">The call to the LINQ <code class=\"fm-code-in-text\">Select<\/code> method generates a sequence of strings, each of which contains a hard-coded reference to the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Price<\/code> properties. Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the following output in the browser window:<\/p>\n<pre class=\"programlisting\">Name: Kayak, Price: 275\nName: Lifejacket, Price: 48.95\nName: Soccer ball, Price: 19.50\nName: Corner flag, Price: 34.95<\/pre>\n<p class=\"body\">This approach is prone to errors, either because the name was mistyped or because the code was refactored and the name in the string isn\u2019t correctly updated. C# supports the <code class=\"fm-code-in-text\">nameof<\/code> expression, in which the compiler takes responsibility for producing a name string, as shown in listing 5.61.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 5.61 Using nameof expressions in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">namespace LanguageFeatures.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            var products = new[] {\n                new { Name = \"Kayak\", Price = 275M },\n                new { Name = \"Lifejacket\", Price = 48.95M },\n                new { Name = \"Soccer ball\", Price = 19.50M },\n                new { Name = \"Corner flag\", Price = 34.95M }\n            };\n            <b class=\"fm-bold\">return View(products.Select(p =&gt;<\/b>\n            <b class=\"fm-bold\">$\"{nameof(p.Name)}: {p.Name}, {nameof(p.Price)}: {p.Price}\"));<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1478\"><\/a>The compiler processes a reference such as <code class=\"fm-code-in-text\">p.Name<\/code> so that only the last part is included in the string, producing the same output as in previous examples. There is IntelliSense support for <code class=\"fm-code-in-text\">nameof<\/code> expressions, so you will be prompted to select references, and expressions will be correctly updated when you refactor code. Since the compiler is responsible for dealing with <code class=\"fm-code-in-text\">nameof<\/code>, using an invalid reference causes a compiler error, which prevents incorrect or outdated references from escaping notice.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-106\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Top-level statements allow code to be defined outside of a class, which can make ASP.NET Core configuration more concise.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Global <code class=\"fm-code-in-text\">using<\/code> statements take effect throughout a project so that namespaces don\u2019t have to be imported in individual C# files.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Null state analysis ensures that null values are only assigned to nullable types and that values are read safely.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">String interpolation allows data values to be composed into strings.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Object initialization patterns simplify the code required to create objects.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Target-typed expressions omit the type name from the <code class=\"fm-code-in-text\">new<\/code> statement.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Pattern matching is used to execute code when a value has specific characteristics.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Extension methods allow new functionality to be added to a type without needing to modify the class file.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Lambda expressions are a concise way to express functions.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Interfaces can be defined with default implementations, which means it is possible to modify the interface without breaking implementation classes.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">async<\/code> and <code class=\"fm-code-in-text\">await<\/code> keywords are used to create asynchronous methods without needing to work directly with tasks and continuations.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-107\">\n<div class=\"calibre1\" id=\"calibre_link-1479\">\n<h1 class=\"tochead\" id=\"calibre_link-1480\"><a id=\"calibre_link-1481\"><\/a><a id=\"calibre_link-1482\"><\/a>6 Testing ASP.NET Core applications<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Creating unit tests projects for ASP.NET Core application<\/li>\n<li class=\"co-summary-bullet\">Writing and running unit tests<\/li>\n<li class=\"co-summary-bullet\">Isolating application components for testing<\/li>\n<li class=\"co-summary-bullet\">Simplifying component isolation with a mocking package<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-1483\"><\/a>In this chapter, I demonstrate how to unit test ASP.NET Core applications. Unit testing is a form of testing in which individual components are isolated from the rest of the application so their behavior can be thoroughly validated. ASP.NET Core has been designed to make it easy to create unit tests, and there is support for a wide range of unit testing frameworks. I show you how to set up a unit test project and describe the process for writing and running tests. <a id=\"calibre_link-1484\"><\/a>Table 6.1 provides a guide to the chapter.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Deciding whether to unit test<\/p>\n<p class=\"fm-sidebar-text\">Being able to easily perform unit testing is one of the benefits of using ASP.NET Core, but it isn\u2019t for everyone, and I have no intention of pretending otherwise.<\/p>\n<p class=\"fm-sidebar-text\">I like unit testing, and I use it in my own projects, but not all of them and not as consistently as you might expect. I tend to focus on writing unit tests for features and functions that I know will be hard to write and likely will be the source of bugs in deployment. In these situations, unit testing helps structure my thoughts about how to best implement what I need. I find that just thinking about what I need to test helps produce ideas about potential problems, and that\u2019s before I start dealing with actual bugs and defects.<\/p>\n<p class=\"fm-sidebar-text\">That said, unit testing is a tool and not a religion, and only you know how much testing you require. If you don\u2019t find unit testing useful or if you have a different methodology that suits you better, then don\u2019t feel you need to unit test just because it is fashionable. (However, if you <i class=\"fm-italics\">don\u2019t<\/i> have a better methodology and you are not testing at all, then you are probably letting users find your bugs, which is rarely ideal<i class=\"fm-italics\">.<\/i> You don\u2019t <i class=\"fm-italics\">have<\/i> to unit test, but you really should consider doing <i class=\"fm-italics\">some<\/i> testing of <i class=\"fm-italics\">some<\/i> kind.)<\/p>\n<p class=\"fm-sidebar-text\">If you have not encountered unit testing before, then I encourage you to give it a try to see how it works. If you are not a fan of unit testing, then you can skip this chapter and move on to chapter 7, where I start to build a more realistic ASP.NET Core application.<\/p>\n<\/div>\n<p class=\"fm-table-caption\">Table 6.1 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1485\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a unit test project<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">dotnet new<\/code> command with the project template for your preferred test framework.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">8<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating an XUnit test<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create a class with methods decorated with the <code class=\"fm-code-in-text1\">Fact<\/code> attribute and use the <code class=\"fm-code-in-text1\">Assert<\/code> class to inspect the test results.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">10<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Running unit tests<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the Visual Studio or Visual Studio Code test runners or use the <code class=\"fm-code-in-text1\">dotnet test<\/code> command.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">12<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Isolating a component for testing<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create mock implementations of the objects that the component under test requires.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">13&ndash;20<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-108\">6.1 Preparing for this chapter<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1486\"><\/a>To prepare for this chapter, I need to create a simple ASP.NET Core project. Open a new PowerShell command prompt using the Windows Start menu, navigate to a convenient location, and run the commands shown in listing 6.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.1 Creating the example project<\/p>\n<pre class=\"programlisting\">dotnet new globaljson --sdk-version 7.0.100 --output Testing\/SimpleApp\ndotnet new web --no-https --output <a id=\"calibre_link-1487\"><\/a>Testing\/SimpleApp --framework net7.0\ndotnet new sln -o Testing\n\ndotnet sln Testing add Testing\/SimpleApp<\/pre>\n<p class=\"body\">These commands create a new project named <code class=\"fm-code-in-text\">SimpleApp<\/code> using the <code class=\"fm-code-in-text\">web<\/code> template, which contains the minimal configuration for ASP.NET Core applications. The project folder is contained within a solution folder also called <code class=\"fm-code-in-text\">Testing<\/code>.<a id=\"calibre_link-1488\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-109\">6.1.1 Opening the project<\/h3>\n<p class=\"body\">If you are using Visual Studio, select File &gt; Open &gt; Project\/Solution, select the <code class=\"fm-code-in-text\">Testing.sln<\/code> file in the <code class=\"fm-code-in-text\">Testing<\/code> folder, and click the Open button to open the solution file and the project it references. If you are using Visual Studio Code, select File &gt; Open Folder, navigate to the <code class=\"fm-code-in-text\">Testing<\/code> folder, and click the Select Folder button.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-110\">6.1.2 Selecting the HTTP port<\/h3>\n<p class=\"body\">Set the port on which ASP.NET Core will receive HTTP requests by editing the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file in the <code class=\"fm-code-in-text\">Properties<\/code> folder, as shown in listing 6.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.2 Setting the HTTP port in the launchSettings.json file in the Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"SimpleApp\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-111\">6.1.3 Enabling the MVC Framework<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1489\"><\/a>As I explained in chapter 1, ASP.NET Core supports different application frameworks, but I am going to continue using the MVC Framework in this chapter. I introduce the other frameworks in the SportsStore application that I start to build in chapter 7, but for the moment, the MVC Framework gives me a foundation for demonstrating how to perform unit testing that is familiar from earlier examples. Add the statements shown in listing 6.3 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file in the <code class=\"fm-code-in-text\">SimpleApp<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.3 Enabling the MVC Framework in the Program.cs file in the SimpleApp folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddControllersWithViews();<\/b>\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">\/\/app.MapGet(\"\/\", () =&gt; \"Hello World!\");<\/b>\n<b class=\"fm-bold\">app.MapDefaultControllerRoute();<\/b>\n\napp.Run();<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-112\">6.1.4 Creating the application components<\/h3>\n<p class=\"body\">Now that the MVC Framework is set up, I can add the application components that I will use to run tests.<\/p>\n<p class=\"fm-head2\">Creating the data model<\/p>\n<p class=\"body\">I started by creating a simple model class so that I can have some data to work with. I added a folder called <code class=\"fm-code-in-text\">Models<\/code> and created a class file called <code class=\"fm-code-in-text\">Product.cs<\/code> within it, which I used to define the class shown in listing 6.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.4 The contents of the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace SimpleApp.Models {\n    public class Product {\n        \n        public string Name { get; set; } = string.Empty;\n        public decimal? Price { get; set; }\n                \n        public static Product[] GetProducts() {\n            Product kayak = new Product {\n                Name = \"Kayak\", Price = 275M\n            };\n                        \n            Product lifejacket = new Product {\n                Name = \"Lifejacket\", Price = 48.95M\n            };\n                        \n            return new Product[] { kayak, lifejacket };\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Product<\/code> class defines <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Price<\/code> properties, and there is a <code class=\"fm-code-in-text\">static<\/code> method called <code class=\"fm-code-in-text\">GetProducts<\/code> that returns a <code class=\"fm-code-in-text\">Products<\/code> array.<\/p>\n<p class=\"fm-head2\">Creating the controller and view<\/p>\n<p class=\"body\"><a id=\"calibre_link-1490\"><\/a>For the examples in this chapter, I use a simple controller class to demonstrate different language features. I created a <code class=\"fm-code-in-text\">Controllers<\/code> folder and added to it a class file called <code class=\"fm-code-in-text\">HomeController.cs<\/code>, the contents of which are shown in listing 6.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.5 The contents of the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing SimpleApp.Models;\n\nnamespace SimpleApp.Controllers {\n    public class HomeController : Controller {\n        \n        public ViewResult Index() {\n            return View(Product.GetProducts());\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Index<\/code> action method tells ASP.NET Core to render the default view and provides it with the <code class=\"fm-code-in-text\">Product<\/code> objects obtained from the static <code class=\"fm-code-in-text\">Product.GetProducts<\/code> method. To create the view for the action method, I added a <code class=\"fm-code-in-text\">Views\/Home<\/code> folder (by creating a <code class=\"fm-code-in-text\">Views<\/code> folder and then adding a <code class=\"fm-code-in-text\">Home<\/code> folder within it) and added a Razor View called <code class=\"fm-code-in-text\">Index.cshtml<\/code>, with the contents shown in listing 6.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.6 The contents of the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@using SimpleApp.Models\n@model IEnumerable&lt;Product&gt;\n@{ Layout = null; }\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;Simple App&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;ul&gt;\n        @foreach (Product p in Model ?? Enumerable.Empty&lt;Product&gt;()) {\n            &lt;li&gt;Name: @p.Name, Price: @p.Price&lt;\/li&gt;\n        }\n    &lt;\/ul&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-113\">6.1.5 Running the example application<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1197\"><\/a>Start ASP.NET Core by running the command shown in listing 6.7 in the <code class=\"fm-code-in-text\">SimpleApp<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.7 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Request http:\/\/localhost:5000, and you will see the output shown in figure 6.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre47\" src=\"\/images\/proaspnetcore7\/000042.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 6.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-114\">6.2 Creating a unit test project<\/h2>\n<p class=\"body\">For ASP.NET Core applications, you generally create a separate Visual Studio project to hold the unit tests, each of which is defined as a method in a C# class. Using a separate project means you can deploy your application without also deploying the tests. The .NET Core SDK includes templates for unit test projects using three popular test tools, as described in table 6.2.<a id=\"calibre_link-1491\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 6.2 The unit test project tools<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1492\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">mstest<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates a project configured for the MS Test framework, which is produced by Microsoft.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">nunit<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates a project configured for the NUnit framework.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">xunit<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This template creates a project configured for the XUnit framework.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">These testing frameworks have largely the same feature set and differ only in how they are implemented and how they integrate into third-party testing environments. I recommend starting with XUnit if you do not have an established preference, largely because it is the test framework that I find easiest to work with.<\/p>\n<p class=\"body\">The convention is to name the unit test project <code class=\"fm-code-in-text\">&lt;ApplicationName&gt;.Tests<\/code>. Run the commands shown in listing 6.8 in the <code class=\"fm-code-in-text\">Testing<\/code> folder to create the XUnit test project named <code class=\"fm-code-in-text\">SimpleApp.Tests<\/code>, add it to the solution file, and create a reference between projects so the unit tests can be applied to the classes defined in the <code class=\"fm-code-in-text\">SimpleApp<\/code> project.<a id=\"calibre_link-1493\"><\/a><a id=\"calibre_link-1494\"><\/a><a id=\"calibre_link-1495\"><\/a><a id=\"calibre_link-1496\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.8 Creating the unit test project<\/p>\n<pre class=\"programlisting\">dotnet new xunit -o SimpleApp.Tests --framework net7.0\ndotnet sln add SimpleApp.Tests\ndotnet add SimpleApp.Tests reference SimpleApp<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1201\"><\/a>If you are using Visual Studio, you will be prompted to reload the solution, which will cause the new unit test project to be displayed in the Solution Explorer, alongside the existing project. You may find that Visual Studio Code doesn\u2019t build the new project. If that happens, select Terminal &gt; Configure Default Build Task, select \u201cbuild\u201d from the list, and, if prompted, select .NET Core from the list of environments.<\/p>\n<p class=\"fm-head2\">Removing the default test class<\/p>\n<p class=\"body\">The project template adds a C# class file to the test project, which will confuse the results of later examples. Either delete the <code class=\"fm-code-in-text\">UnitTest1.cs<\/code> file from the <code class=\"fm-code-in-text\">SimpleApp.Tests<\/code> folder using the Solution Explorer or File Explorer pane or run the command shown in listing 6.9 in the <code class=\"fm-code-in-text\">Testing<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.9 Removing the default test class file<\/p>\n<pre class=\"programlisting\">Remove-Item SimpleApp.Tests\/UnitTest1.cs<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-115\">6.3 Writing and running unit tests<\/h2>\n<p class=\"body\">Now that all the preparation is complete, I can write some tests. To get started, I added a class file called <code class=\"fm-code-in-text\">ProductTests.cs<\/code> to the <code class=\"fm-code-in-text\">SimpleApp.Tests<\/code> project and used it to define the class shown in listing 6.10. This is a simple class, but it contains everything required to get started with unit testing.<a id=\"calibre_link-1497\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The <code class=\"fm-code-in-text1\">CanChangeProductPrice<\/code> method contains a deliberate error that I resolve later in this section.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.10 The contents of the ProductTests.cs file in the SimpleApp.Tests folder<\/p>\n<pre class=\"programlisting\">using SimpleApp.Models;\nusing Xunit;\n\nnamespace SimpleApp.Tests {\n\n    public class ProductTests {\n        \n        [Fact]\n        public void CanChangeProductName() {\n                \n            \/\/ Arrange\n            var p = new Product { Name = \"Test\", Price = 100M };\n                        \n            \/\/ Act\n            p.Name = \"New Name\";\n                        \n            \/\/Assert\n            Assert.Equal(\"New Name\", p.Name);\n        }\n                \n        [Fact]\n        public void CanChangeProductPrice() {\n                \n            \/\/ Arrange\n            var p = new Product { Name = \"Test\", Price = 100M };\n                        \n            \/\/ Act\n            p.Price = 200M;\n                        \n            \/\/Assert\n            Assert.Equal(100M, p.Price);\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1196\"><\/a>There are two unit tests in the <code class=\"fm-code-in-text\">ProductTests<\/code> class, each of which tests a behavior of the <code class=\"fm-code-in-text\">Product<\/code> model class from the <code class=\"fm-code-in-text\">SimpleApp<\/code> project. A test project can contain many classes, each of which can contain many unit tests.<\/p>\n<p class=\"body\">Conventionally, the name of the test methods describes what the test does, and the name of the class describes what is being tested. This makes it easier to structure the tests in a project and to understand what the results of all the tests are when they are run by Visual Studio. The name <code class=\"fm-code-in-text\">ProductTests<\/code> indicates that the class contains tests for the <code class=\"fm-code-in-text\">Product<\/code> class, and the method names indicate that they test the ability to change the name and price of a <code class=\"fm-code-in-text\">Product<\/code> object.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Fact<\/code> attribute is applied to each method to indicate that it is a test. Within the method body, a unit test follows a pattern called <i class=\"fm-italics\">arrange, act, assert<\/i> (A\/A\/A). <i class=\"fm-italics\">Arrange<\/i> refers to setting up the conditions for the test, <i class=\"fm-italics\">act<\/i> refers to performing the test, and <i class=\"fm-italics\">assert<\/i> refers to verifying that the result was the one that was expected.<\/p>\n<p class=\"body\">The arrange and act sections of these tests are regular C# code, but the assert section is handled by xUnit.net, which provides a class called <code class=\"fm-code-in-text\">Assert<\/code>, whose methods are used to check that the outcome of an action is the one that is expected.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">Fact<\/code> attribute and the <code class=\"fm-code-in-text1\">Assert<\/code> class are defined in the <code class=\"fm-code-in-text1\">Xunit<\/code> namespace, for which there must be a <code class=\"fm-code-in-text1\">using<\/code> statement in every test class.<\/p>\n<p class=\"body\">The methods of the <code class=\"fm-code-in-text\">Assert<\/code> class are static and are used to perform different kinds of comparison between the expected and actual results. Table 6.3 shows the commonly used <code class=\"fm-code-in-text\">Assert<\/code> methods.<a id=\"calibre_link-1498\"><\/a><a id=\"calibre_link-1499\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 6.3 Commonly used xUnit.net assert methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1500\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Equal(expected, result)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the result is equal to the expected outcome. There are overloaded versions of this method for comparing different types and for comparing collections. There is also a version of this method that accepts an additional argument of an object that implements the <code class=\"fm-code-in-text1\">IEqualityComparer&lt;T&gt;<\/code> interface for comparing objects.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">NotEqual(expected, result)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the result is not equal to the expected outcome.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">True(result)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the result is <code class=\"fm-code-in-text1\">true<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">False(result)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the result is <code class=\"fm-code-in-text1\">false<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsType(expected, result)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the result is of a specific type.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsNotType(expected, result)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the result is not a specific type.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsNull(result)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the result is <code class=\"fm-code-in-text1\">null<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsNotNull(result)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the result is not <code class=\"fm-code-in-text1\">null<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">InRange(result, low, high)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the result falls between <code class=\"fm-code-in-text1\">low<\/code> and <code class=\"fm-code-in-text1\">high<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">NotInRange(result, low, high)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the result falls outside <code class=\"fm-code-in-text1\">low<\/code> and <code class=\"fm-code-in-text1\">high<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Throws(exception, expression)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asserts that the specified expression throws a specific exception type.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\"><a id=\"calibre_link-1200\"><\/a>Each <code class=\"fm-code-in-text\">Assert<\/code> method allows different types of comparison to be made and throws an exception if the result is not what was expected. The exception is used to indicate that a test has failed. In the tests in listing 6.10, I used the <code class=\"fm-code-in-text\">Equal<\/code> method to determine whether the value of a property has been changed correctly.<\/p>\n<pre class=\"programlisting\">...\nAssert.<b class=\"fm-bold\">Equal<\/b>(\"New Name\", p.Name);\n...<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-116\">6.3.1 Running tests with the Visual Studio Test Explorer<\/h3>\n<p class=\"body\">Visual Studio includes support for finding and running unit tests through the Test Explorer window, which is available through the Test &gt; Test Explorer menu and is shown in figure 6.2.<a id=\"calibre_link-1501\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Build the solution if you don\u2019t see the unit tests in the Test Explorer window. Compilation triggers the process by which unit tests are discovered.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre48\" src=\"\/images\/proaspnetcore7\/000043.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 6.2 The Visual Studio Test Explorer<\/p>\n<\/div>\n<p class=\"body\">Run the tests by clicking the Run All Tests button in the Test Explorer window (it is the button that shows two arrows and is the first button in the row at the top of the window). As noted, the <code class=\"fm-code-in-text\">CanChangeProductPrice<\/code> test contains an error that causes the test to fail, which is clearly indicated in the test results shown in the figure.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-117\">6.3.2 Running tests with Visual Studio Code<\/h3>\n<p class=\"body\"><a id=\"calibre_link-881\"><\/a>Visual Studio Code detects tests and allows them to be run using the code lens feature, which displays details about code features in the editor. To run all the tests in the <code class=\"fm-code-in-text\">ProductTests<\/code> class, click Run All Tests in the code editor when the unit test class is open, as shown in figure 6.3.<a id=\"calibre_link-1502\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Close and reopen the <code class=\"fm-code-in-text1\">Testing<\/code> folder in Visual Studio Code if you don\u2019t see the code lens test features.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre49\" src=\"\/images\/proaspnetcore7\/000044.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 6.3 Running tests with the Visual Studio Code code lens feature<\/p>\n<\/div>\n<p class=\"body\">Visual Studio Code runs the tests using the command-line tools that I describe in the following section, and the results are displayed as text in a terminal window.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-118\">6.3.3 Running tests from the command line<\/h3>\n<p class=\"body\">To run the tests in the project, run the command shown in listing 6.11 in the <code class=\"fm-code-in-text\">Testing<\/code> folder.<a id=\"calibre_link-1503\"><\/a><a id=\"calibre_link-1504\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.11 Running unit tests<\/p>\n<pre class=\"programlisting\">dotnet test<\/pre>\n<p class=\"body\">The tests are discovered and executed, producing the following results, which show the deliberate error that I introduced earlier:<\/p>\n<pre class=\"programlisting\">Starting test execution, please wait...\nA total of 1 test files matched the specified pattern.\n[xUnit.net 00:00:00.81]\n    SimpleApp.Tests.ProductTests.CanChangeProductPrice [FAIL]\n  Failed SimpleApp.Tests.ProductTests.CanChangeProductPrice [4 ms]\n  Error Message:\n   Assert.Equal() Failure\nExpected: 100\nActual:   200\n  Stack Trace:\n     at SimpleApp.Tests.ProductTests.CanChangeProductPrice() in \n       C:\\Testing\\SimpleApp.Tests\\ProductTests.cs:line 31\n   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** \n      arguments, Signature sig, Boolean isConstructor)\n   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, \n     BindingFlags invokeAttr)\nFailed!  - Failed:     1, Passed:     1, Skipped:     0,\n    Total:     2, Duration: 26 ms - SimpleApp.Tests.dll (net7.0)<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-119\">6.3.4 Correcting the unit test<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1505\"><\/a>The problem with the unit test is with the arguments to the <code class=\"fm-code-in-text\">Assert.Equal<\/code> method, which compares the test result to the original <code class=\"fm-code-in-text\">Price<\/code> property value rather than the value it has been changed to. Listing 6.12 corrects the problem.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> When a test fails, it is always a good idea to check the accuracy of the test before looking at the component it targets, especially if the test is new or has been recently modified.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.12 Correcting a test in the ProductTests.cs file in the SimpleApp.Tests folder<\/p>\n<pre class=\"programlisting\">using SimpleApp.Models;\nusing Xunit;\n\nnamespace SimpleApp.Tests {\n\n    public class ProductTests {\n        \n        [Fact]\n        public void CanChangeProductName() {\n                \n            \/\/ Arrange\n            var p = new Product { Name = \"Test\", Price = 100M };\n                        \n            \/\/ Act\n            p.Name = \"New Name\";\n                        \n            \/\/Assert\n            Assert.Equal(\"New Name\", p.Name);\n        }\n                \n        [Fact]\n        public void CanChangeProductPrice() {\n                \n            \/\/ Arrange\n            var p = new Product { Name = \"Test\", Price = 100M };\n                        \n            \/\/ Act\n            p.Price = 200M;\n                        \n            \/\/Assert\n                        \n            <b class=\"fm-bold\">Assert.Equal(200M, p.Price);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">Run the tests again, and you will see they all pass. If you are using Visual Studio, you can click the Run Failed Tests button, which will execute only the tests that failed, as shown in figure 6.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre50\" src=\"\/images\/proaspnetcore7\/000045.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 6.4 Running only failed tests<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-120\">6.3.5 Isolating components for unit testing<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1198\"><\/a>Writing unit tests for model classes like <code class=\"fm-code-in-text\">Product<\/code> is easy. Not only is the <code class=\"fm-code-in-text\">Product<\/code> class simple, but it is self-contained, which means that when I perform an action on a <code class=\"fm-code-in-text\">Product<\/code> object, I can be confident that I am testing the functionality provided by the <code class=\"fm-code-in-text\">Product<\/code> class.<\/p>\n<p class=\"body\">The situation is more complicated with other components in an ASP.NET Core application because there are dependencies between them. The next set of tests that I define will operate on the controller, examining the sequence of <code class=\"fm-code-in-text\">Product<\/code> objects that are passed between the controller and the view.<a id=\"calibre_link-1506\"><\/a><\/p>\n<p class=\"body\">When comparing objects instantiated from custom classes, you will need to use the xUnit.net <code class=\"fm-code-in-text\">Assert.Equal<\/code> method that accepts an argument that implements the <code class=\"fm-code-in-text\">IEqualityComparer&lt;T&gt;<\/code> interface so that the objects can be compared. My first step is to add a class file called <code class=\"fm-code-in-text\">Comparer.cs<\/code> to the unit test project and use it to define the helper classes shown in listing 6.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.13 The contents of the Comparer.cs file in the SimpleApp.Tests folder<\/p>\n<pre class=\"programlisting\">using System;\nusing System.Collections.Generic;\n\nnamespace SimpleApp.Tests {\n\n    public class Comparer {\n        \n        public static Comparer&lt;U?&gt; Get&lt;U&gt;(Func&lt;U?, U?, bool&gt; func) {\n            return new Comparer&lt;U?&gt;(func);\n        }\n    }\n        \n    public class Comparer&lt;T&gt; : Comparer, IEqualityComparer&lt;T&gt; {\n        private Func&lt;T?, T?, bool&gt; comparisonFunction;\n        public Comparer(Func&lt;T?, T?, bool&gt; func) {\n            comparisonFunction = func;\n        }\n                \n        public bool Equals(T? x, T? y) {\n            return comparisonFunction(x, y);\n        }\n                \n        public int GetHashCode(T obj) {\n            return obj?.GetHashCode() ?? 0;\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1507\"><\/a>These classes will allow me to create <code class=\"fm-code-in-text\">IEqualityComparer&lt;T&gt;<\/code> objects using lambda expressions rather than having to define a new class for each type of comparison that I want to make. This isn\u2019t essential, but it will simplify the code in my unit test classes and make them easier to read and maintain.<\/p>\n<p class=\"body\">Now that I can easily make comparisons, I can illustrate the problem of dependencies between components in the application. I added a new class called <code class=\"fm-code-in-text\">HomeControllerTests.cs<\/code> to the <code class=\"fm-code-in-text\">SimpleApp.Tests<\/code> folder and used it to define the unit test shown in listing 6.14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.14 The HomeControllerTests.cs file in the SimpleApp.Tests folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing System.Collections.Generic;\nusing SimpleApp.Controllers;\nusing SimpleApp.Models;\nusing Xunit;\n\nnamespace SimpleApp.Tests {\n    public class HomeControllerTests {\n        \n        [Fact]\n        public void IndexActionModelIsComplete() {\n            \/\/ Arrange\n            var controller = new HomeController();\n            Product[] products = new Product[]  {\n                new Product { Name = \"Kayak\", Price = 275M },\n                new Product { Name = \"Lifejacket\", Price = 48.95M}\n            };\n                        \n            \/\/ Act\n            var model = (controller.Index() as ViewResult)?.ViewData.Model\n                as IEnumerable&lt;Product&gt;;\n                                \n            \/\/ Assert\n            Assert.Equal(products, model,\n                Comparer.Get&lt;Product&gt;((p1, p2) =&gt; p1?.Name == p2?.Name\n                    &amp;&amp; p1?.Price == p2?.Price));\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1508\"><\/a>The unit test creates an array of <code class=\"fm-code-in-text\">Product<\/code> objects and checks that they correspond to the ones the <code class=\"fm-code-in-text\">Index<\/code> action method provides as the view model. (Ignore the act section of the test for the moment; I explain the <code class=\"fm-code-in-text\">ViewResult<\/code> class in chapters 21 and 22. For the moment, it is enough to know that I am getting the model data returned by the <code class=\"fm-code-in-text\">Index<\/code> action method.)<\/p>\n<p class=\"body\">The test passes, but it isn\u2019t a useful result because the <code class=\"fm-code-in-text\">Product<\/code> data that I am testing is coming from the hardwired objects\u2019 <code class=\"fm-code-in-text\">Product<\/code> class. I can\u2019t write a test to make sure that the controller behaves correctly when there are more than two <code class=\"fm-code-in-text\">Product<\/code> objects, for example, or if the <code class=\"fm-code-in-text\">Price<\/code> property of the first object has a decimal fraction. The overall effect is that I am testing the combined behavior of the <code class=\"fm-code-in-text\">HomeController<\/code> and <code class=\"fm-code-in-text\">Product<\/code> classes and only for the specific hardwired objects.<\/p>\n<p class=\"body\">Unit tests are effective when they target small parts of an application, such as an individual method or class. What I need is the ability to isolate the <code class=\"fm-code-in-text\">Home<\/code> controller from the rest of the application so that I can limit the scope of the test and rule out any impact caused by the repository.<\/p>\n<p class=\"fm-head2\">Isolating a component<\/p>\n<p class=\"body\">The key to isolating components is to use C# interfaces. To separate the controller from the repository, I added a new class file called <code class=\"fm-code-in-text\">IDataSource.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and used it to define the interface shown in listing 6.15.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.15 The contents of the IDataSource.cs file in the SimpleApp\/Models folder<\/p>\n<pre class=\"programlisting\">namespace SimpleApp.Models {\n    public interface IDataSource {\n        \n        IEnumerable&lt;Product&gt; Products { get; }\n    }\n}<\/pre>\n<p class=\"body\">In listing 6.16, I have removed the static method from the <code class=\"fm-code-in-text\">Product<\/code> class and created a new class that implements the <code class=\"fm-code-in-text\">IDataSource<\/code> interface.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.16 A data source in the Product.cs file in the SimpleApp\/Models folder<\/p>\n<pre class=\"programlisting\">namespace SimpleApp.Models {\n    public class Product {\n        \n        public string Name { get; set; } = string.Empty;\n        public decimal? Price { get; set; }\n                \n        <b class=\"fm-bold\">\/\/public static Product[] GetProducts() {<\/b>\n                \n        <b class=\"fm-bold\">\/\/    Product kayak = new Product {<\/b>\n        <b class=\"fm-bold\">\/\/        Name =<\/b> <b class=\"fm-bold\">\"Kayak\", Price = 275M<\/b>\n        <b class=\"fm-bold\">\/\/    };<\/b>\n                \n        <b class=\"fm-bold\">\/\/    Product lifejacket = new Product {<\/b>\n        <b class=\"fm-bold\">\/\/        Name = \"Lifejacket\", Price = 48.95M<\/b>\n        <b class=\"fm-bold\">\/\/    };<\/b>\n        <b class=\"fm-bold\">\/\/    return new Product[] { kayak, lifejacket };<\/b>\n        <b class=\"fm-bold\">\/\/}<\/b>\n    }\n        \n    <b class=\"fm-bold\">public class ProductDataSource : IDataSource {<\/b>\n        <b class=\"fm-bold\">public IEnumerable&lt;Product&gt; Products =&gt;<\/b>\n            <b class=\"fm-bold\">new Product[] {<\/b>\n                <b class=\"fm-bold\">new Product { Name = \"Kayak\", Price = 275M },<\/b>\n                <b class=\"fm-bold\">new Product { Name = \"Lifejacket\", Price = 48.95M }<\/b>\n            <b class=\"fm-bold\">};<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">The next step is to modify the controller so that it uses the <code class=\"fm-code-in-text\">ProductDataSource<\/code> class as the source for its data, as shown in listing 6.17.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> ASP.NET Core supports a more elegant approach for solving this problem, known as <i class=\"fm-italics\">dependency injection<\/i>, which I describe in chapter 14. Dependency injection often causes confusion, so I isolate components in a simpler and more manual way in this chapter.<a id=\"calibre_link-1509\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.17 Adding a property in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing SimpleApp.Models;\n\nnamespace SimpleApp.Controllers {\n    public class HomeController : Controller {\n        <b class=\"fm-bold\">public IDataSource dataSource = new ProductDataSource();<\/b>\n                \n        public ViewResult Index() {\n            <b class=\"fm-bold\">return View(dataSource.Products);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">This may not seem like a significant change, but it allows me to change the data source the controller uses during testing, which is how I can isolate the controller. In listing 6.18, I have updated the controller unit tests so they use a special version of the repository.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.18 Isolating the controller in the HomeControllerTests.cs file in the SimpleApp.Tests folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing System.Collections.Generic;\nusing SimpleApp.Controllers;\nusing SimpleApp.Models;\nusing Xunit;\n\nnamespace SimpleApp.Tests {\n    public class HomeControllerTests {\n        \n        <b class=\"fm-bold\">class FakeDataSource : IDataSource {<\/b>\n            <b class=\"fm-bold\">public FakeDataSource(Product[] data) =&gt; Products = data;<\/b>\n            <b class=\"fm-bold\">public IEnumerable&lt;Product&gt; Products { get; set; }<\/b>\n        <b class=\"fm-bold\">}<\/b>\n        [Fact]\n        public void IndexActionModelIsComplete() {\n                \n            \/\/ Arrange\n            <b class=\"fm-bold\">Product[] testData = new Product[] {<\/b>\n                <b class=\"fm-bold\">new Product { Name = \"P1\", Price = 75.10M },<\/b>\n                <b class=\"fm-bold\">new Product { Name = \"P2\", Price = 120M },<\/b>\n                <b class=\"fm-bold\">new Product { Name = \"P3\", Price = 110M }<\/b>\n            <b class=\"fm-bold\">};<\/b>\n            <b class=\"fm-bold\">IDataSource data = new FakeDataSource(testData);<\/b>\n            var controller = new HomeController();\n            <b class=\"fm-bold\">controller.dataSource = data;<\/b>\n                        \n            \/\/ Act\n            var model = (controller.Index() as ViewResult)?.ViewData.Model\n                as IEnumerable&lt;Product&gt;;\n                                \n            \/\/ Assert\n            <b class=\"fm-bold\">Assert.Equal(data.Products, model,<\/b>\n                Comparer.Get&lt;Product&gt;((p1, p2) =&gt; p1?.Name == p2?.Name\n                    &amp;&amp; p1?.Price == p2?.Price));\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1510\"><\/a>I have defined a fake implementation of the <code class=\"fm-code-in-text\">IDataSource<\/code> interface that lets me use any test data with the controller.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding test-driven development<\/p>\n<p class=\"fm-sidebar-text\">I have followed the most commonly used unit testing style in this chapter, in which an application feature is written and then tested to make sure it works as required. This is popular because most developers think about application code first and testing comes second (this is certainly the category that I fall into).<\/p>\n<p class=\"fm-sidebar-text\">This approach is that it tends to produce unit tests that focus only on the parts of the application code that were difficult to write or that needed some serious debugging, leaving some aspects of a feature only partially tested or untested altogether.<\/p>\n<p class=\"fm-sidebar-text\">An alternative approach is <i class=\"fm-italics\">Test-Driven Development<\/i> (TDD). There are lots of variations on TDD, but the core idea is that you write the tests for a feature before implementing the feature itself. Writing the tests first makes you think more carefully about the specification you are implementing and how you will know that a feature has been implemented correctly. Rather than diving into the implementation detail, TDD makes you consider what the measures of success or failure will be in advance.<\/p>\n<p class=\"fm-sidebar-text\">The tests that you write will all fail initially because your new feature will not be implemented. But as you add code to the application, your tests will gradually move from red to green, and all your tests will pass by the time that the feature is complete. TDD requires discipline, but it does produce a more comprehensive set of tests and can lead to more robust and reliable code.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-121\">6.3.6 Using a mocking package<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1199\"><\/a>It was easy to create a fake implementation for the <code class=\"fm-code-in-text\">IDataSource<\/code> interface, but most classes for which fake implementations are required are more complex and cannot be handled as easily.<\/p>\n<p class=\"body\">A better approach is to use a mocking package, which makes it easy to create fake&mdash;or mock&mdash;objects for tests. There are many mocking packages available, but the one I use (and have for years) is called Moq. To add Moq to the unit test project, run the command shown in listing 6.19 in the <code class=\"fm-code-in-text\">Testing<\/code> folder.<a id=\"calibre_link-1511\"><\/a><a id=\"calibre_link-1512\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The Moq package is added to the unit testing project and not the project that contains the application to be tested.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.19 Installing the mocking package<\/p>\n<pre class=\"programlisting\">dotnet add SimpleApp.Tests package Moq --version 4.18.4<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-122\">6.3.7 Creating a mock object<\/h3>\n<p class=\"body\">I can use the Moq framework to create a fake <code class=\"fm-code-in-text\">IDataSource<\/code> object without having to define a custom test class, as shown in listing 6.20.<a id=\"calibre_link-1513\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 6.20 Creating a mock object in the HomeControllerTests.cs file in the SimpleApp.Tests folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing System.Collections.Generic;\nusing SimpleApp.Controllers;\nusing SimpleApp.Models;\nusing Xunit;\n<b class=\"fm-bold\">using Moq;<\/b>\n\nnamespace SimpleApp.Tests {\n    public class HomeControllerTests {\n        \n        <b class=\"fm-bold\">\/\/class FakeDataSource : IDataSource {<\/b>\n        <b class=\"fm-bold\">\/\/    public FakeDataSource(Product[] data) =&gt; Products = data;<\/b>\n        <b class=\"fm-bold\">\/\/    public IEnumerable&lt;Product&gt; Products { get; set; }<\/b>\n        <b class=\"fm-bold\">\/\/}<\/b>\n\n        [Fact]\n        public void IndexActionModelIsComplete() {\n                \n            \/\/ Arrange\n            Product[] testData = new Product[] {\n                new Product { Name = \"P1\", Price = 75.10M },\n                new Product { Name = \"P2\", Price = 120M },\n                new Product { Name = \"P3\", Price = 110M }\n            };\n            <b class=\"fm-bold\">var mock = new Mock&lt;IDataSource&gt;();<\/b>\n            <b class=\"fm-bold\">mock.SetupGet(m =&gt; m.Products).Returns(testData);<\/b>\n            var controller = new HomeController();\n            <b class=\"fm-bold\">controller.dataSource = mock.Object;<\/b>\n                        \n            \/\/ Act\n            var model = (controller.Index() as ViewResult)?.ViewData.Model\n                as IEnumerable&lt;Product&gt;;\n                                \n            \/\/ Assert\n            <b class=\"fm-bold\">Assert.Equal(testData, model,<\/b>\n                Comparer.Get&lt;Product&gt;((p1, p2) =&gt; p1?.Name == p2?.Name\n                    &amp;&amp; p1?.Price == p2?.Price));\n            <b class=\"fm-bold\">mock.VerifyGet(m =&gt; m.Products, Times.Once);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1514\"><\/a>The use of Moq has allowed me to remove the fake implementation of the <code class=\"fm-code-in-text\">IDataSource<\/code> interface and replace it with a few lines of code. I am not going to go into detail about the different features that Moq supports, but I will explain the way that I used Moq in the examples. (See <a class=\"url\" href=\"https:\/\/github.com\/Moq\/moq4\">https:\/\/github.com\/Moq\/moq4<\/a> for examples and documentation for Moq. There are also examples in later chapters as I explain how to unit test different types of components.)<\/p>\n<p class=\"body\">The first step is to create a new instance of the <code class=\"fm-code-in-text\">Mock<\/code> object, specifying the interface that should be implemented, like this:<\/p>\n<pre class=\"programlisting\">...\nvar mock = new Mock&lt;IDataSource&gt;();\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Mock<\/code> object I created will fake the <code class=\"fm-code-in-text\">IDataSource<\/code> interface. To create an implementation of the <code class=\"fm-code-in-text\">Product<\/code> property, I use the <code class=\"fm-code-in-text\">SetUpGet<\/code> method, like this:<\/p>\n<pre class=\"programlisting\">...\nmock.<b class=\"fm-bold\">SetupGet<\/b>(m =&gt; m.Products).Returns(testData);\n... <\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">SetupGet<\/code> method is used to implement the getter for a property. The argument to this method is a lambda expression that specifies the property to be implemented, which is <code class=\"fm-code-in-text\">Products<\/code> in this example. The <code class=\"fm-code-in-text\">Returns<\/code> method is called on the result of the <code class=\"fm-code-in-text\">SetupGet<\/code> method to specify the result that will be returned when the property value is read.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Mock<\/code> class defines an <code class=\"fm-code-in-text\">Object<\/code> property, which returns the object that implements the specified interface with the behaviors that have been defined. I used the <code class=\"fm-code-in-text\">Object<\/code> property to set the <code class=\"fm-code-in-text\">dataSource<\/code> field defined by the <code class=\"fm-code-in-text\">HomeController<\/code>, like this:<\/p>\n<pre class=\"programlisting\">...\ncontroller.dataSource = mock.Object;\n...<\/pre>\n<p class=\"body\">The final Moq feature I used was to check that the <code class=\"fm-code-in-text\">Products<\/code> property was called once, like this:<\/p>\n<pre class=\"programlisting\">...\nmock.VerifyGet(m =&gt; m.Products, Times.Once);\n...<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1515\"><\/a>The <code class=\"fm-code-in-text\">VerifyGet<\/code> method is one of the methods defined by the <code class=\"fm-code-in-text\">Mock<\/code> class to inspect the state of the mock object when the test has completed. In this case, the <code class=\"fm-code-in-text\">VerifyGet<\/code> method allows me to check the number of times that the <code class=\"fm-code-in-text\">Products<\/code> property method has been read. The <code class=\"fm-code-in-text\">Times.Once<\/code> value specifies that the <code class=\"fm-code-in-text\">VerifyGet<\/code> method should throw an exception if the property has not been read exactly once, which will cause the test to fail. (The <code class=\"fm-code-in-text\">Assert<\/code> methods usually used in tests work by throwing an exception when a test fails, which is why the <code class=\"fm-code-in-text\">VerifyGet<\/code> method can be used to replace an <code class=\"fm-code-in-text\">Assert<\/code> method when working with mock objects.)<\/p>\n<p class=\"body\">The overall effect is the same as my fake interface implementation, but mocking is more flexible and more concise and can provide more insight into the behavior of the components under test.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-123\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Unit tests are typically defined within a dedicated unit test project.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">A test framework simplifies writing unit tests by providing common features, such as assertions.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Unit tests typically follow the arrange\/act\/assert pattern.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Tests can be run within Visual Studio\/Visual Studio Code or using the <code class=\"fm-code-in-text\">dotnet test<\/code> command.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Effective unit tests isolate and test individual components.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Isolating components is simplified by mocking packages, such as Moq.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-124\">\n<div class=\"calibre1\" id=\"calibre_link-1516\">\n<h1 class=\"tochead\" id=\"calibre_link-1517\"><a id=\"calibre_link-1518\"><\/a>7 SportsStore: A real application<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Creating the SportsStore ASP.NET Core project<\/li>\n<li class=\"co-summary-bullet\">Adding a data model and support for a database<\/li>\n<li class=\"co-summary-bullet\">Displaying a basic product catalog<\/li>\n<li class=\"co-summary-bullet\">Paginating data<\/li>\n<li class=\"co-summary-bullet\">Styling content<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-1519\"><\/a>In the previous chapters, I built quick and simple ASP.NET Core applications. I described ASP.NET Core patterns, the essential C# features, and the tools that good ASP.NET Core developers require. Now it is time to put everything together and build a simple but realistic e-commerce application.<\/p>\n<p class=\"body\">My application, called SportsStore, will follow the classic approach taken by online stores everywhere. I will create an online product catalog that customers can browse by category and page, a shopping cart where users can add and remove products, and a checkout where customers can enter their shipping details. I will also create an administration area that includes create, read, update, and delete (CRUD) facilities for managing the catalog, and I will protect it so that only logged-in administrators can make changes.<\/p>\n<p class=\"body\">My goal in this chapter and those that follow is to give you a sense of what real ASP.NET Core development is by creating as realistic an example as possible. I want to focus on ASP.NET Core, of course, so I have simplified the integration with external systems, such as the database, and omitted others entirely, such as payment processing.<\/p>\n<p class=\"body\">You might find the going a little slow as I build up the levels of infrastructure I need, but the initial investment will result in maintainable, extensible, well-structured code with excellent support for unit testing.<a id=\"calibre_link-1520\"><\/a><a id=\"calibre_link-1151\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit testing<\/p>\n<p class=\"fm-sidebar-text\">I include sections on unit testing different components in the SportsStore application throughout the development process, demonstrating how to isolate and test different ASP.NET Core components.<a id=\"calibre_link-1521\"><\/a><a id=\"calibre_link-1522\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">I know that unit testing is not embraced by everyone. If you do not want to unit test, that is fine with me. To that end, when I have something to say that is purely about testing, I put it in a sidebar like this one. If you are not interested in unit testing, you can skip right over these sections, and the SportsStore application will work just fine. You do not need to do any kind of unit testing to get the technology benefits of ASP.NET Core, although, of course, support for testing is a key reason for adopting ASP.NET Core in many projects.<\/p>\n<\/div>\n<p class=\"body\">Most of the features I use for the SportsStore application have their own chapters later in the book. Rather than duplicate everything here, I tell you just enough to make sense of the example application and point you to another chapter for in-depth information.<\/p>\n<p class=\"body\">I will call out each step needed to build the application so that you can see how the ASP.NET Core features fit together. You should pay particular attention when I create views. You will get some odd results if you do not follow the examples closely.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-125\">7.1 Creating the projects<\/h2>\n<p class=\"body\">I am going to start with a minimal ASP.NET Core project and add the features I require as they are needed. Open a new PowerShell command prompt from the Windows Start menu and run the commands shown in listing 7.1 to get started.<a id=\"calibre_link-1523\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.1 Creating the SportsStore project<\/p>\n<pre class=\"programlisting\">dotnet new globaljson --sdk-version 7.0.100 --output SportsSln\/SportsStore\ndotnet new web --no-https --out<a id=\"calibre_link-1524\"><\/a>put SportsSln\/SportsStore --framework net7.0\ndotnet new sln -o SportsSln \n\ndotnet sln SportsSln add SportsSln\/SportsStore<\/pre>\n<p class=\"body\">These commands create a <code class=\"fm-code-in-text\">SportsSln<\/code> solution folder that contains a <code class=\"fm-code-in-text\">SportsStore<\/code> project folder created with the <code class=\"fm-code-in-text\">web<\/code> project template. The <code class=\"fm-code-in-text\">SportsSln<\/code> folder also contains a solution file, to which the <code class=\"fm-code-in-text\">SportsStore<\/code> project is added.<\/p>\n<p class=\"body\">I am using different names for the solution and project folders to make the examples easier to follow, but if you create a project with Visual Studio, the default is to use the same name for both folders. There is no \u201cright\u201d approach, and you can use whatever names suit your project.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-126\">7.1.1 Creating the unit test project<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1525\"><\/a>To create the unit test project, run the commands shown in listing 7.2 in the same location you used for the commands shown in listing 7.1.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.2 Creating the unit test project<\/p>\n<pre class=\"programlisting\">dotnet new xunit -o SportsSln\/SportsStore.Tests --framework net7.0\ndotnet sln SportsSln add SportsSln\/SportsStore.Tests\ndotnet add SportsSln\/SportsStore.Tests reference SportsSln\/SportsStore<\/pre>\n<p class=\"body\">I am going to use the Moq package to create mock objects. Run the command shown in listing 7.3 to install the Moq package into the unit testing project. Run this command from the same location as the commands in listing 7.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.3 Installing the Moq package<\/p>\n<pre class=\"programlisting\">dotnet add SportsSln\/SportsStore.Tests package Moq --version 4.18.4<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-127\">7.1.2 Opening the projects<\/h3>\n<p class=\"body\">If you are using Visual Studio Code, select File &gt; Open Folder, navigate to the <code class=\"fm-code-in-text\">SportsSln<\/code> folder, and click the Select Folder button. Visual Studio Code will open the folder and discover the solution and project files. When prompted, as shown in figure 7.1, click Yes to install the assets required to build the projects. Select SportsStore if Visual Studio Code prompts you to select the project to run.<\/p>\n<p class=\"body\">If you are using Visual Studio, click the \u201cOpen a project or solution\u201d button on the splash screen or select File &gt; Open &gt; Project\/Solution. Select the <code class=\"fm-code-in-text\">SportsSln.sln<\/code> file in the <code class=\"fm-code-in-text\">SportsSln<\/code> folder and click the Open button to open the project.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre51\" src=\"\/images\/proaspnetcore7\/000046.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 7.1 Adding assets in Visual Studio Code<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-128\">7.1.3 Configuring the HTTP port<\/h3>\n<p class=\"body\">To configure the HTTP port that ASP.NET Core will use to listen for HTTP requests, make the changes shown in listing 7.4 to the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file in the <code class=\"fm-code-in-text\">SportsStore\/Properties<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.4 Setting the HTTP Port in the launchSettings.json File in the SportsStore\/ Properties Folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"SportsStore\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-129\">7.1.4 Creating the application project folders<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1526\"><\/a>The next step is to create folders that will contain the application\u2019s components. Right-click the SportsStore item in the Visual Studio Solution Explorer or Visual Studio Code Explorer pane and select Add &gt; New Folder or New Folder to create the set of folders described in table 7.1.<\/p>\n<p class=\"fm-table-caption\">Table 7.1 The application project folders<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1527\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Models<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This folder will contain the data model and the classes that provide access to the data in the application\u2019s database.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Controllers<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This folder will contain the controller classes that handle HTTP requests.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Views<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This folder will contain all the Razor files, grouped into separate subfolders.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Views\/Home<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This folder will contain Razor files that are specific to the Home controller, which I create in the \u201cCreating the Controller and View\u201d section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Views\/Shared<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This folder will contain Razor files that are common to all controllers.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-130\">7.1.5 Preparing the services and the request pipeline<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1528\"><\/a>The <code class=\"fm-code-in-text\">Program.cs<\/code> file is used to configure the ASP.NET Core application. Apply the changes shown in listing 7.5 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file in the <code class=\"fm-code-in-text\">SportsStore<\/code> project to configure the basic application features.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The <code class=\"fm-code-in-text1\">Program.cs<\/code> file is an important ASP.NET Core feature. I describe it in detail in part 2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.5 Configuring the application in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddControllersWithViews();<\/b>\n\nvar app = builder.Build();\n<b class=\"fm-bold\">\/\/app.MapGet(\"\/\", () =&gt; \"Hello World!\");<\/b>\n\n<b class=\"fm-bold\">app.UseStaticFiles();<\/b>\n<b class=\"fm-bold\">app.MapDefaultControllerRoute();<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">builder.Services<\/code> property is used to set up objects, known as <i class=\"fm-italics\">services<\/i>, that can be used throughout the application and that are accessed through a feature called <i class=\"fm-italics\">dependency injection<\/i>, which I describe in chapter 14. The <code class=\"fm-code-in-text\">AddControllersWithViews<\/code> method sets up the shared objects required by applications using the MVC Framework and the Razor view engine.<\/p>\n<p class=\"body\">ASP.NET Core receives HTTP requests and passes them along a <i class=\"fm-italics\">request pipeline<\/i>, which is populated with middleware components registered using the <code class=\"fm-code-in-text\">app<\/code> property. Each middleware component is able to inspect requests, modify them, generate a response, or modify the responses that other components have produced. The request pipeline is the heart of ASP.NET Core, and I describe it in detail in chapter 12, where I also explain how to create custom middleware components.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UseStaticFiles<\/code> method enables support for serving static content from the <code class=\"fm-code-in-text\">wwwroot<\/code> folder and will be created later in the chapter.<\/p>\n<p class=\"body\">One especially important middleware component provides the endpoint routing feature, which matches HTTP requests to the application features&mdash;known as <i class=\"fm-italics\">endpoints<\/i>&mdash;able to produce responses for them, a process I describe in detail in chapter 13. The endpoint routing feature is added to the request pipeline automatically, and the <code class=\"fm-code-in-text\">MapDefaultControllerRoute<\/code> registers the MVC Framework as a source of endpoints using a default convention for mapping requests to classes and methods.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-131\">7.1.6 Configuring the Razor view engine<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1529\"><\/a>The Razor view engine is responsible for processing view files, which have the <code class=\"fm-code-in-text\">.cshtml<\/code> extension, to generate HTML responses. Some initial preparation is required to configure Razor to make it easier to create views for the application.<\/p>\n<p class=\"body\">Add a Razor View Imports file named <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> in the <code class=\"fm-code-in-text\">Views<\/code> folder with the content shown in listing 7.6.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Pay close attention to the contents of this file. It is easy to make a mistake that causes the application to generate incorrect HTML content.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.6 The contents of the _ViewImports.cshtml file in the SportsStore\/ Views folder<\/p>\n<pre class=\"programlisting\">@using SportsStore.Models\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@using<\/code> statement will allow me to use the types in the <code class=\"fm-code-in-text\">SportsStore.Models<\/code> namespace in views without needing to refer to the namespace. The <code class=\"fm-code-in-text\">@addTagHelper<\/code> statement enables the built-in tag helpers, which I use later to create HTML elements that reflect the configuration of the SportsStore application and which I describe in detail in chapter 15. (You may see a warning or error displayed by the code editor for the contents of this file, but this will be resolved shortly and can be ignored.)<\/p>\n<p class=\"body\">Add a Razor View Start file named <code class=\"fm-code-in-text\">_ViewStart.cshtml<\/code> to the <code class=\"fm-code-in-text\">SportsStore\/Views<\/code> folder with the content shown in listing 7.7. (The file will already contain this expression if you create the file using the Visual Studio item template.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.7 The contents of the _ViewStart.cshtml file in the SportsStore\/Views folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = \"_Layout\";\n}<\/pre>\n<p class=\"body\">The Razor View Start file tells Razor to use a layout file in the HTML that it generates, reducing the amount of duplication in views. To create the view, add a Razor layout named <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder, with the content shown in listing 7.8.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.8 The contents of the _Layout.cshtml file in the SportsStore\/Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;SportsStore&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">This file defines a simple HTML document into which the contents of other views will be inserted by the <code class=\"fm-code-in-text\">@RenderBody<\/code> expression. I explain how Razor expressions work in detail in chapter 21.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-132\">7.1.7 Creating the controller and view<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1159\"><\/a>Add a class file named <code class=\"fm-code-in-text\">HomeController.cs<\/code> in the <code class=\"fm-code-in-text\">SportsStore\/Controllers<\/code> folder and use it to define the class shown in listing 7.9. This is a minimal controller that contains just enough functionality to produce a response.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.9 The contents of the HomeController.cs file in the SportsStore\/Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace SportsStore.Controllers {\n    public class HomeController: Controller {\n        \n        public IActionResult Index() =&gt; View();\n                \n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">MapDefaultControllerRoute<\/code> method used in listing 7.5 tells ASP.NET Core how to match URLs to controller classes. The configuration applied by that method declares that the <code class=\"fm-code-in-text\">Index<\/code> action method defined by the <code class=\"fm-code-in-text\">Home<\/code> controller will be used to handle requests.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Index<\/code> action method doesn\u2019t do anything useful yet and just returns the result of calling the <code class=\"fm-code-in-text\">View<\/code> method, which is inherited from the <code class=\"fm-code-in-text\">Controller<\/code> base class. This result tells ASP.NET Core to render the default view associated with the action method. To create the view, add a Razor View file named <code class=\"fm-code-in-text\">Index.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder with the content shown in listing 7.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.10 The contents of the Index.cshtml file in the SportsStore\/Views\/Home folder<\/p>\n<pre class=\"programlisting\">&lt;h4&gt;Welcome to SportsStore&lt;\/h4&gt;<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-133\">7.1.8 Starting the data model<\/h3>\n<p class=\"body\">Almost all projects have a data model of some sort. Since this is an e-commerce application, the most obvious model I need is for a product. Add a class file named <code class=\"fm-code-in-text\">Product.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the class shown in listing 7.11.<a id=\"calibre_link-1530\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.11 The contents of the Product.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations.Schema;\n\nnamespace SportsStore.Models {\n\n    public class Product {\n        \n        public long? ProductID { get; set; }\n                \n        public string Name { get; set; } = String.Empty;\n                \n        public string Description { get; set; } = String.Empty;\n                \n        [Column(TypeName = \"decimal(8, 2)\")]\n        public decimal Price { get; set; }\n                \n        public string Category { get; set; } = String.Empty;\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Price<\/code> property has been decorated with the <code class=\"fm-code-in-text\">Column<\/code> attribute to specify the SQL data type that will be used to store values for this property. Not all C# types map neatly onto SQL types, and this attribute ensures the database uses an appropriate type for the application data.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-134\">7.1.9 Checking and running the application<\/h3>\n<p class=\"body\">Before going any further, it is a good idea to make sure the application builds and runs as expected. Run the command shown in listing 7.12 in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.12 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Request http:\/\/localhost:5000, and you will see the response shown in figure 7.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre47\" src=\"\/images\/proaspnetcore7\/000047.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 7.2 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-135\">7.2 Adding data to the application<\/h2>\n<p class=\"body\">Now that the SportsStore contains some basic setup and can produce a simple response, it is time to add some data so that the application has something more useful to display. The SportsStore application will store its data in a SQL Server LocalDB database, which is accessed using Entity Framework Core. Entity Framework Core is the Microsoft object-to-relational mapping (ORM) framework, and it is the most widely used method of accessing databases in ASP.NET Core projects.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> If you did not install LocalDB when you prepared your development environment in chapter 2, you must do so now. The SportsStore application will not work without its database.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-136\">7.2.1 Installing the Entity Framework Core packages<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1158\"><\/a>The first step is to add Entity Framework Core to the project. Use a PowerShell command prompt to run the command shown in listing 7.13 in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder. If you receive an error asking you to specify a project, then delete the <code class=\"fm-code-in-text\">SportsStore - Backup.csproj<\/code> file in the SportsStore folder and try again.<a id=\"calibre_link-1531\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.13 Adding the Entity Framework Core packages to the SportsStore project<\/p>\n<pre class=\"programlisting\">dotnet add package Microsoft.EntityFrameworkCore.Design --version 7.0.0\ndotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 7.0.0<\/pre>\n<p class=\"body\">These packages install Entity Framework Core and the support for using SQL Server. Entity Framework Core also requires a tools package, which includes the command-line tools required to prepare and create databases for ASP.NET Core applications. Run the commands shown in listing 7.14 to remove any existing version of the tools package, if there is one, and install the version used in this book. (Since this package is installed globally, you can run these commands in any folder.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.14 Installing the Entity Framework Core tool package<\/p>\n<pre class=\"programlisting\">dotnet tool uninstall --global dotnet-ef\ndotnet tool install --global dotnet-ef --version 7.0.0<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-137\">7.2.2 Defining the connection string<\/h3>\n<p class=\"body\">Configuration settings, such as database connection strings, are stored in JSON configuration files. To describe the connection to the database that will be used for the SportsStore data, add the entries shown in listing 7.15 to the <code class=\"fm-code-in-text\">appsettings.json<\/code> file in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder.<\/p>\n<p class=\"body\">The project also contains an <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> file that contains configuration settings that are used only in development. This file is displayed as nested within the <code class=\"fm-code-in-text\">appsettings.json<\/code> file by Solution Explorer but is always visible in Visual Studio Code. I use only the <code class=\"fm-code-in-text\">appsettings.json<\/code> file for the development of the SportsStore project, but I explain the relationship between the files and how they are both used in detail in chapter 15.<a id=\"calibre_link-1532\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Connection strings must be expressed as a single unbroken line, which is fine in the code editor but doesn\u2019t fit on the printed page and is the cause of the awkward formatting in listing 7.15. When you define the connection string in your own project, make sure that the value of the <code class=\"fm-code-in-text1\">SportsStoreConnection<\/code> item is on a single line.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.15 Adding a configuration setting in the appsettings.json file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\",\n  <b class=\"fm-bold\">\"ConnectionStrings\": {<\/b>\n    <b class=\"fm-bold\">\"SportsStoreConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>SportsStore;MultipleActiveResultSets=true\"<\/b>\n  <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">This configuration string specifies a LocalDB database called <code class=\"fm-code-in-text\">SportsStore<\/code> and enables the multiple active result set (MARS) feature, which is required for some of the database queries that will be made by the SportsStore application using Entity Framework Core.<\/p>\n<p class=\"body\">Pay close attention when you add the configuration setting. JSON data must be expressed exactly as shown in the listing, which means you must ensure you correctly quote the property names and values. You can download the configuration file from the GitHub repository if you have difficulty.<a id=\"calibre_link-1533\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Each database server requires its own connection string format. A helpful site for formulating connection strings is <a class=\"url\" href=\"https:\/\/www.connectionstrings.com\">https:\/\/www.connectionstrings.com<\/a>.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-138\">7.2.3 Creating the database context class<\/h3>\n<p class=\"body\">Entity Framework Core provides access to the database through a context class. Add a class file named <code class=\"fm-code-in-text\">StoreDbContext.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the class shown in listing 7.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.16 The contents of the StoreDbContext.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n\nnamespace SportsStore.Models {\n    public class StoreDbContext : DbContext {\n        \n        public StoreDbContext(DbContextOptions&lt;StoreDbContext&gt; options)\n            : base(options) { }\n                        \n        public DbSet&lt;Product&gt; Products =&gt; Set&lt;Product&gt;();\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">DbContext<\/code> base class provides access to the Entity Framework Core\u2019s underlying functionality, and the <code class=\"fm-code-in-text\">Products<\/code> property will provide access to the <code class=\"fm-code-in-text\">Product<\/code> objects in the database. The <code class=\"fm-code-in-text\">StoreDbContext<\/code> class is derived from <code class=\"fm-code-in-text\">DbContext<\/code> and adds the properties that will be used to read and write the application\u2019s data. There is only one property for now, which will provide access to <code class=\"fm-code-in-text\">Product<\/code> objects.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-139\">7.2.4 Configuring Entity Framework Core<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1115\"><\/a>Entity Framework Core must be configured so that it knows the type of database to which it will connect, which connection string describes that connection, and which context class will present the data in the database. Listing 7.17 shows the required changes to the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.17 Configuring Entity Framework Core in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Microsoft.EntityFrameworkCore;<\/b>\n<b class=\"fm-bold\">using SportsStore.Models;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\n<b class=\"fm-bold\">builder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.UseSqlServer(<\/b>\n        <b class=\"fm-bold\">builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapDefaultControllerRoute();\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">IConfiguration<\/code> interface provides access to the ASP.NET Core configuration system, which includes the contents of the <code class=\"fm-code-in-text\">appsettings.json<\/code> file and which I describe in detail in chapter 15. Access to the configuration data is through the <code class=\"fm-code-in-text\">builder.Configuration<\/code> property, which allows the database connection string to be obtained. Entity Framework Core is configured with the <code class=\"fm-code-in-text\">AddDbContext<\/code> method, which registers the database context class and configures the relationship with the database. The <code class=\"fm-code-in-text\">UseSQLServer<\/code> method declares that SQL Server is being used.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-140\">7.2.5 Creating a repository<\/h3>\n<p class=\"body\">The next step is to create a repository interface and implementation class. The repository pattern is one of the most widely used, and it provides a consistent way to access the features presented by the database context class. Not everyone finds a repository useful, but my experience is that it can reduce duplication and ensures that operations on the database are performed consistently. Add a class file named <code class=\"fm-code-in-text\">IStoreRepository.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the interface shown in listing 7.18.<a id=\"calibre_link-1534\"><\/a><a id=\"calibre_link-1535\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.18 The contents of the IStoreRepository.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Models {\n    public interface IStoreRepository {\n        IQueryable&lt;Product&gt; Products { get; }\n    }\n}<\/pre>\n<p class=\"body\">This interface uses <code class=\"fm-code-in-text\">IQueryable&lt;T&gt;<\/code> to allow a caller to obtain a sequence of <code class=\"fm-code-in-text\">Product<\/code> objects. The <code class=\"fm-code-in-text\">IQueryable&lt;T&gt;<\/code> interface is derived from the more familiar <code class=\"fm-code-in-text\">IEnumerable&lt;T&gt;<\/code> interface and represents a collection of objects that can be queried, such as those managed by a database.<\/p>\n<p class=\"body\">A class that depends on the <code class=\"fm-code-in-text\">IStoreRepository<\/code> interface can obtain <code class=\"fm-code-in-text\">Product<\/code> objects without needing to know the details of how they are stored or how the implementation class will deliver them.<a id=\"calibre_link-1536\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding IEnumerable&lt;T&gt; and IQueryable&lt;T&gt; interfaces<\/p>\n<p class=\"fm-sidebar-text\">The <code class=\"fm-code-in-text1\">IQueryable&lt;T&gt;<\/code> interface is useful because it allows a collection of objects to be queried efficiently. Later in this chapter, I add support for retrieving a subset of <code class=\"fm-code-in-text1\">Product<\/code> objects from a database, and using the <code class=\"fm-code-in-text1\">IQueryable&lt;T&gt;<\/code> interface allows me to ask the database for just the objects that I require using standard LINQ statements and without needing to know what database server stores the data or how it processes the query. Without the <code class=\"fm-code-in-text1\">IQueryable&lt;T&gt;<\/code> interface, I would have to retrieve all of the <code class=\"fm-code-in-text1\">Product<\/code> objects from the database and then discard the ones that I don\u2019t want, which becomes an expensive operation as the amount of data used by an application increases. It is for this reason that the <code class=\"fm-code-in-text1\">IQueryable&lt;T&gt;<\/code> interface is typically used instead of <code class=\"fm-code-in-text1\">IEnumerable&lt;T&gt;<\/code> in database repository interfaces and classes.<\/p>\n<p class=\"fm-sidebar-text\">However, care must be taken with the <code class=\"fm-code-in-text1\">IQueryable&lt;T&gt;<\/code> interface because each time the collection of objects is enumerated, the query will be evaluated again, which means that a new query will be sent to the database. This can undermine the efficiency gains of using <code class=\"fm-code-in-text1\">IQueryable&lt;T&gt;<\/code>. In such situations, you can convert the <code class=\"fm-code-in-text1\">IQueryable&lt;T&gt;<\/code> interface to a more predictable form using the <code class=\"fm-code-in-text1\">ToList<\/code> or <code class=\"fm-code-in-text1\">ToArray<\/code> extension method.<\/p>\n<\/div>\n<p class=\"body\">To create an implementation of the repository interface, add a class file named <code class=\"fm-code-in-text\">EFStoreRepository.cs<\/code> in the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the class shown in listing 7.19.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.19 The contents of the EFStoreRepository.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Models {\n    public class EFStoreRepository : IStoreRepository {\n        private StoreDbContext context;\n                \n        public EFStoreRepository(StoreDbContext ctx) {\n            context = ctx;\n        }\n                \n        public IQueryable&lt;Product&gt; Products =&gt; context.Products;\n    }\n}<\/pre>\n<p class=\"body\">I\u2019ll add additional functionality as I add features to the application, but for the moment, the repository implementation just maps the <code class=\"fm-code-in-text\">Products<\/code> property defined by the <code class=\"fm-code-in-text\">IStoreRepository<\/code> interface onto the <code class=\"fm-code-in-text\">Products<\/code> property defined by the <code class=\"fm-code-in-text\">StoreDbContext<\/code> class. The <code class=\"fm-code-in-text\">Products<\/code> property in the context class returns a <code class=\"fm-code-in-text\">DbSet&lt;Product&gt;<\/code> object, which implements the <code class=\"fm-code-in-text\">IQueryable&lt;T&gt;<\/code> interface and makes it easy to implement the repository interface when using Entity Framework Core.<\/p>\n<p class=\"body\">Earlier in the chapter, I explained that ASP.NET Core supports services that allow objects to be accessed throughout the application. One benefit of services is they allow classes to use interfaces without needing to know which implementation class is being used. I explain this in detail in chapter 14, but for the SportsStore chapters, it means that application components can access objects that implement the <code class=\"fm-code-in-text\">IStoreRepository<\/code> interface without knowing that it is the <code class=\"fm-code-in-text\">EFStoreRepository<\/code> implementation class they are using. This makes it easy to change the implementation class the application uses without needing to make changes to the individual components. Add the statement shown in listing 7.20 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file to create a service for the <code class=\"fm-code-in-text\">IStoreRepository<\/code> interface that uses <code class=\"fm-code-in-text\">EFStoreRepository<\/code> as the implementation class.<a id=\"calibre_link-1537\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Don\u2019t worry if this doesn\u2019t make sense right now. This topic is one of the most confusing aspects of working with ASP.NET Core, and it can take a while to understand.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.20 Creating the repository service in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapDefaultControllerRoute();\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddScoped<\/code> method creates a service where each HTTP request gets its own repository object, which is the way that Entity Framework Core is typically used.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-141\">7.2.6 Creating the database migration<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1161\"><\/a>Entity Framework Core can generate the schema for the database using the data model classes through a feature called <i class=\"fm-italics\">migrations<\/i>. When you prepare a migration, Entity Framework Core creates a C# class that contains the SQL commands required to prepare the database. If you need to modify your model classes, then you can create a new migration that contains the SQL commands required to reflect the changes. In this way, you don\u2019t have to worry about manually writing and testing SQL commands and can just focus on the C# model classes in the application.<a id=\"calibre_link-1538\"><\/a><\/p>\n<p class=\"body\">Entity Framework Core commands are performed from the command line. Open a PowerShell command prompt and run the command shown in listing 7.21 in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder to create the migration class that will prepare the database for its first use.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.21 Creating the database migration<\/p>\n<pre class=\"programlisting\">dotnet ef migrations add Initial<\/pre>\n<p class=\"body\">When this command has finished, the SportsStore project will contain a <code class=\"fm-code-in-text\">Migrations<\/code> folder. This is where Entity Framework Core stores its migration classes. One of the file names will be a timestamp followed by <code class=\"fm-code-in-text\">_Initial.cs<\/code>, and this is the class that will be used to create the initial schema for the database. If you examine the contents of this file, you can see how the <code class=\"fm-code-in-text\">Product<\/code> model class has been used to create the schema.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-142\">7.2.7 Creating seed data<\/h3>\n<p class=\"body\">To populate the database and provide some sample data, I added a class file called <code class=\"fm-code-in-text\">SeedData.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and defined the class shown in listing 7.22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.22 The contents of the SeedData.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n\nnamespace SportsStore.Models {\n\n    public static class SeedData {\n        \n        public static void EnsurePopulated(IApplicationBuilder app) {\n            StoreDbContext context = app.ApplicationServices\n                .CreateScope().ServiceProvider\n                .GetRequiredService&lt;StoreDbContext&gt;();\n                                \n            if (context.Database.GetPendingMigrations().Any()) {\n                context.Database.Migrate();\n            }\n                        \n            if (!context.Products.Any()) {\n                context.Products.AddRange(\n                    new Product {\n                        Name = \"Kayak\", Description = \n                            \"A boat for one person\",\n                        Category = \"Watersports\", Price = 275\n                    },\n                    new Product {\n                        Name = \"Lifejacket\",\n                        Description = \"Protective and fashionable\",\n                        Category = \"Watersports\", Price = 48.95m\n                    },\n                    new Product {\n                        Name = \"Soccer Ball\",\n                        Description = \"FIFA-approved size and weight\",\n                        Category = \"Soccer\", Price = 19.50m\n                    },\n                    new Product {\n                        Name = \"Corner Flags\",\n                        Description = \n                          \"Give your playing field a professional touch\",\n                        Category = \"Soccer\", Price = 34.95m\n                    },\n                    new Product {\n                        Name = \"Stadium\",\n                        Description = \"Flat-packed 35,000-seat stadium\",\n                        Category = \"Soccer\", Price = 79500\n                    },\n                    new Product {\n                        Name = \"Thinking Cap\",\n                        Description = \"Improve brain efficiency by 75%\",\n                        Category = \"Chess\", Price = 16\n                    },\n                    new Product {\n                        Name = \"Unsteady Chair\",\n                        Description = \n                          \"Secretly give your opponent a disadvantage\",\n                        Category = \"Chess\", Price = 29.95m\n                    },\n                    new Product {\n                        Name = \"Human Chess Board\",\n                        Description = \"A fun game for the family\",\n                        Category = \"Chess\", Price = 75\n                    },\n                    new Product {\n                        Name = \"Bling-Bling King\",\n                        Description = \"Gold-plated, diamond-studded King\",\n                        Category = \"Chess\", Price = 1200\n                    }\n                );\n                context.SaveChanges();\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1539\"><\/a>The static <code class=\"fm-code-in-text\">EnsurePopulated<\/code> method receives an <code class=\"fm-code-in-text\">IApplicationBuilder<\/code> argument, which is the interface used in the <code class=\"fm-code-in-text\">Program.cs<\/code> file to register middleware components to handle HTTP requests. <code class=\"fm-code-in-text\">IApplicationBuilder<\/code> also provides access to the application\u2019s services, including the Entity Framework Core database context service.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">EnsurePopulated<\/code> method obtains a <code class=\"fm-code-in-text\">StoreDbContext<\/code> object through the <code class=\"fm-code-in-text\">IApplicationBuilder<\/code> interface and calls the <code class=\"fm-code-in-text\">Database.Migrate<\/code> method if there are any pending migrations, which means that the database will be created and prepared so that it can store <code class=\"fm-code-in-text\">Product<\/code> objects. Next, the number of <code class=\"fm-code-in-text\">Product<\/code> objects in the database is checked. If there are no objects in the database, then the database is populated using a collection of <code class=\"fm-code-in-text\">Product<\/code> objects using the <code class=\"fm-code-in-text\">AddRange<\/code> method and then written to the database using the <code class=\"fm-code-in-text\">SaveChanges<\/code> method.<\/p>\n<p class=\"body\">The final change is to seed the database when the application starts, which I have done by adding a call to the <code class=\"fm-code-in-text\">EnsurePopulated<\/code> method from the <code class=\"fm-code-in-text\">Program.cs<\/code> file, as shown in listing 7.23.<a id=\"calibre_link-1540\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.23 Seeding the database in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapDefaultControllerRoute();\n\n<b class=\"fm-bold\">SeedData.EnsurePopulated(app);<\/b>\n\napp.Run();<\/pre>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Resetting the database<\/p>\n<p class=\"fm-sidebar-text\">If you need to reset the database, then run this command in the <code class=\"fm-code-in-text1\">SportsStore<\/code> folder:<\/p>\n<pre class=\"programlisting\">...\ndotnet ef database drop --force --context StoreDbContext\n...<\/pre>\n<p class=\"fm-sidebar-text\">Start ASP.NET Core, and the database will be re-created and seeded with data.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-143\">7.3 Displaying a list of products<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1541\"><\/a>As you have seen, the initial preparation work for an ASP.NET Core project can take some time. But the good news is that once the foundation is in place, the pace improves, and features are added more rapidly. In this section, I am going to create a controller and an action method that can display details of the products in the repository.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Using the Visual Studio scaffolding<\/p>\n<p class=\"fm-sidebar-text\">As I noted in chapter 4, Visual Studio supports scaffolding to add items to a project.<\/p>\n<p class=\"fm-sidebar-text\">I don\u2019t use scaffolding in this book. The code and markup that the scaffolding generates are so generic as to be all but useless, and the scenarios that are supported are narrow and don\u2019t address common development problems. My goal in this book is not only to make sure you know how to create ASP.NET Core applications but also to explain how everything works behind the scenes, and that is harder to do when responsibility for creating components is handed to the scaffolding.<\/p>\n<p class=\"fm-sidebar-text\">If you are using Visual Studio, add items to the project by right-clicking a folder in the Solution Explorer, selecting Add &gt; New Item from the pop-up menu, and then choosing an item template from the Add New Item window.<\/p>\n<p class=\"fm-sidebar-text\">You may find your development style to be different from mine, and you may find that you prefer working with the scaffolding in your own projects. That\u2019s perfectly reasonable, although I recommend you take the time to understand what the scaffolding does so you know where to look if you don\u2019t get the results you expect.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-144\">7.3.1 Preparing the controller<\/h3>\n<p class=\"body\">Add the statements shown in listing 7.24 to prepare the controller to display the list of products.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.24 Preparing the controller in the HomeController.cs file in the SportsStore\/Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n<b class=\"fm-bold\">using SportsStore.Models;<\/b>\n\nnamespace SportsStore.Controllers {\n    <b class=\"fm-bold\">public class HomeController : Controller {<\/b>\n        <b class=\"fm-bold\">private IStoreRepository repository;<\/b>\n                \n        <b class=\"fm-bold\">public HomeController(IStoreRepository repo) {<\/b>\n            <b class=\"fm-bold\">repository = repo;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">public IActionResult Index() =&gt; View(repository.Products);<\/b>\n    }\n}<\/pre>\n<p class=\"body\">When ASP.NET Core needs to create a new instance of the <code class=\"fm-code-in-text\">HomeController<\/code> class to handle an HTTP request, it will inspect the constructor and see that it requires an object that implements the <code class=\"fm-code-in-text\">IStoreRepository<\/code> interface. To determine what implementation class should be used, ASP.NET Core consults the configuration created in the <code class=\"fm-code-in-text\">Program.cs<\/code> file, which tells it that <code class=\"fm-code-in-text\">EFStoreRepository<\/code> should be used and that a new instance should be created for every request. ASP.NET Core creates a new <code class=\"fm-code-in-text\">EFStoreRepository<\/code> object and uses it to invoke the <code class=\"fm-code-in-text\">HomeController<\/code> constructor to create the controller object that will process the HTTP request.<\/p>\n<p class=\"body\">This is known as <i class=\"fm-italics\">dependency injection<\/i>, and its approach allows the <code class=\"fm-code-in-text\">HomeController<\/code> object to access the application\u2019s repository through the <code class=\"fm-code-in-text\">IStoreRepository<\/code> interface without knowing which implementation class has been configured. I could reconfigure the service to use a different implementation class&mdash;one that doesn\u2019t use Entity Framework Core, for example&mdash;and dependency injection means that the controller will continue to work without changes.<a id=\"calibre_link-1542\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Some developers don\u2019t like dependency injection and believe it makes applications more complicated. That\u2019s not my view, but if you are new to dependency injection, then I recommend you wait until you have read chapter 14 before you make up your mind.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit test: repository access<\/p>\n<p class=\"fm-sidebar-text\">I can unit test that the controller is accessing the repository correctly by creating a mock repository, injecting it into the constructor of the <code class=\"fm-code-in-text1\">HomeController<\/code> class, and then calling the <code class=\"fm-code-in-text1\">Index<\/code> method to get the response that contains the list of products. I then compare the <code class=\"fm-code-in-text1\">Product<\/code> objects I get to what I would expect from the test data in the mock implementation. See chapter 6 for details of how to set up unit tests. Here is the unit test I created for this purpose, in a class file called <code class=\"fm-code-in-text1\">HomeControllerTests.cs<\/code> that I added to the <code class=\"fm-code-in-text1\">SportsStore.Tests<\/code> project:<\/p>\n<pre class=\"programlisting\">using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Microsoft.AspNetCore.Mvc;\nusing Moq;\nusing SportsStore.Controllers;\nusing SportsStore.Models;\nusing Xunit;\n\nnamespace SportsStore.Tests {\n\n    public class HomeControllerTests {\n        \n        [Fact]\n        public void Can_Use_Repository() {\n            \/\/ Arrange\n            Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n            mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n                new Product {ProductID = 1, Name = \"P1\"},\n                new Product {ProductID = 2, Name = \"P2\"}\n            }).AsQueryable&lt;Product&gt;());\n                        \n            HomeController controller = new HomeController(mock.Object);\n                        \n            \/\/ Act\n            IEnumerable&lt;Product&gt;? result =\n                (controller.Index() as ViewResult)?.ViewData.Model\n                     as IEnumerable&lt;Product&gt;;\n                                         \n            \/\/ Assert\n            Product[] prodArray = result?.ToArray() \n                ?? Array.Empty&lt;Product&gt;();\n            Assert.True(prodArray.Length == 2);\n            Assert.Equal(\"P1\", prodArray[0].Name);\n            Assert.Equal(\"P2\", prodArray[1].Name);\n        }\n    }\n}<\/pre>\n<p class=\"fm-sidebar-text\">It is a little awkward to get the data returned from the action method. The result is a <code class=\"fm-code-in-text1\">ViewResult<\/code> object, and I have to cast the value of its <code class=\"fm-code-in-text1\">ViewData.Model<\/code> property to the expected data type. I explain the different result types that can be returned by action methods and how to work with them in part 2.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-145\">7.3.2 Updating the view<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1543\"><\/a>The <code class=\"fm-code-in-text\">Index<\/code> action method in listing 7.24 passes the collection of <code class=\"fm-code-in-text\">Product<\/code> objects from the repository to the <code class=\"fm-code-in-text\">View<\/code> method, which means these objects will be the view model that Razor uses when it generates HTML content from the view. Make the changes to the view shown in listing 7.25 to generate content using the <code class=\"fm-code-in-text\">Product<\/code> view model objects.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.25 Using the product data in the Index.cshtml file in the SportsStore\/Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IQueryable&lt;Product&gt;\n\n@foreach (var p in Model ?? Enumerable.Empty&lt;Product&gt;()) {\n    &lt;div&gt;\n        &lt;h3&gt;@p.Name&lt;\/h3&gt;\n        @p.Description\n        &lt;h4&gt;@p.Price.ToString(\"c\")&lt;\/h4&gt;\n    &lt;\/div&gt;\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@model<\/code> expression at the top of the file specifies that the view expects to receive a sequence of <code class=\"fm-code-in-text\">Product<\/code> objects from the action method as its model data. I use an <code class=\"fm-code-in-text\">@foreach<\/code> expression to work through the sequence and generate a simple set of HTML elements for each <code class=\"fm-code-in-text\">Product<\/code> object that is received.<\/p>\n<p class=\"body\">There is a quirk in the way that Razor Views work that means the model data is always nullable, even when the type specified by the <code class=\"fm-code-in-text\">@model<\/code> expression is not. For this reason, I use the null-coalescing operator in the <code class=\"fm-code-in-text\">@foreach<\/code> expression with an empty enumeration.<\/p>\n<p class=\"body\">The view doesn\u2019t know where the <code class=\"fm-code-in-text\">Product<\/code> objects came from, how they were obtained, or whether they represent all the products known to the application. Instead, the view deals only with how details of each <code class=\"fm-code-in-text\">Product<\/code> are displayed using HTML elements.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> I converted the <code class=\"fm-code-in-text1\">Price<\/code> property to a string using the <code class=\"fm-code-in-text1\">ToString(\"c\")<\/code> method, which renders numerical values as currency according to the culture settings that are in effect on your server. For example, if the server is set up as <code class=\"fm-code-in-text1\">en-US<\/code>, then <code class=\"fm-code-in-text1\">(1002.3).ToString(\"c\")<\/code> will return <code class=\"fm-code-in-text1\">$1,002.30<\/code>, but if the server is set to <code class=\"fm-code-in-text1\">en-GB<\/code>, then the same method will return <code class=\"fm-code-in-text1\">\u00a31,002.30<\/code>.<a id=\"calibre_link-1164\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-146\">7.3.3 Running the application<\/h3>\n<p class=\"body\">Start ASP.NET Core and request http:\/\/localhost:5000 to see the list of products, which is shown in figure 7.3. This is the typical pattern of development for ASP.NET Core. An initial investment of time setting everything up is necessary, and then the basic features of the application snap together quickly.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre52\" src=\"\/images\/proaspnetcore7\/000048.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 7.3 Displaying a list of products<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-147\">7.4 Adding pagination<\/h2>\n<p class=\"body\">You can see from figure 7.3 that the <code class=\"fm-code-in-text\">Index.cshtml<\/code> view displays the products in the database on a single page. In this section, I will add support for pagination so that the view displays a smaller number of products on a page and so the user can move from page to page to view the overall catalog. To do this, I am going to add a parameter to the <code class=\"fm-code-in-text\">Index<\/code> method in the <code class=\"fm-code-in-text\">Home<\/code> controller, as shown in listing 7.26.<a id=\"calibre_link-1544\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.26 Adding pagination in the HomeController.cs file in the SportsStore\/Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing SportsStore.Models;\n\nnamespace SportsStore.Controllers {\n    public class HomeController : Controller {\n        private IStoreRepository repository;\n                \n        <b class=\"fm-bold\">public int PageSize = 4;<\/b>\n                \n        public HomeController(IStoreRepository repo) {\n            repository = repo;\n        }\n                \n        <b class=\"fm-bold\">public ViewResult Index(int productPage = 1)<\/b>\n            <b class=\"fm-bold\">=&gt; View(repository.Products<\/b>\n                <b class=\"fm-bold\">.OrderBy(p =&gt; p.ProductID)<\/b>\n                <b class=\"fm-bold\">.Skip((productPage - 1) * PageSize)<\/b>\n                <b class=\"fm-bold\">.Take(PageSize));<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">PageSize<\/code> field specifies that I want four products per page. I have added an optional parameter to the <code class=\"fm-code-in-text\">Index<\/code> method, which means that if the method is called without a parameter, the call is treated as though I had supplied the value specified in the parameter definition, with the effect that the action method displays the first page of products when it is invoked without an argument. Within the body of the action method, I get the <code class=\"fm-code-in-text\">Product<\/code> objects, order them by the primary key, skip over the products that occur before the start of the current page, and take the number of products specified by the <code class=\"fm-code-in-text\">PageSize<\/code> field.<a id=\"calibre_link-1545\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit test: pagination<\/p>\n<p class=\"fm-sidebar-text\">I can unit test the pagination feature by mocking the repository, requesting a specific page from the controller, and making sure I get the expected subset of the data. Here is the unit test I created for this purpose and added to the <code class=\"fm-code-in-text1\">HomeControllerTests.cs<\/code> file in the <code class=\"fm-code-in-text1\">SportsStore.Tests<\/code> project:<\/p>\n<pre class=\"programlisting\">using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Microsoft.AspNetCore.Mvc;\nusing Moq;\nusing SportsStore.Controllers;\nusing SportsStore.Models;\nusing Xunit;\n\nnamespace SportsStore.Tests {\n\n    public class HomeControllerTests {\n        \n        [Fact]\n        public void Can_Use_Repository() {\n            \/\/ ...<i class=\"fm-italics\">statements omitted for brevity<\/i>...\n        }\n                \n        <b class=\"fm-bold\">[Fact]<\/b>\n        <b class=\"fm-bold\">public void Can_Paginate() {<\/b>\n            <b class=\"fm-bold\">\/\/ Arrange<\/b>\n            <b class=\"fm-bold\">Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();<\/b>\n            <b class=\"fm-bold\">mock.Setup(m =&gt; m.Products).Returns((new Product[] {<\/b>\n                <b class=\"fm-bold\">new Product {ProductID = 1, Name = \"P1\"},<\/b>\n                <b class=\"fm-bold\">new Product {ProductID = 2, Name = \"P2\"},<\/b>\n                <b class=\"fm-bold\">new Product {ProductID = 3, Name = \"P3\"},<\/b>\n                <b class=\"fm-bold\">new Product {ProductID = 4, Name = \"P4\"},<\/b>\n                <b class=\"fm-bold\">new Product {ProductID = 5, Name = \"P5\"}<\/b>\n            <b class=\"fm-bold\">}).AsQueryable&lt;Product&gt;());<\/b>\n                        \n            <b class=\"fm-bold\">HomeController controller = new HomeController(mock.Object);<\/b>\n            <b class=\"fm-bold\">controller.PageSize = 3;<\/b>\n                        \n            <b class=\"fm-bold\">\/\/ Act<\/b>\n            <b class=\"fm-bold\">IEnumerable&lt;Product&gt; result =<\/b>\n                <b class=\"fm-bold\">(controller.Index(2) as ViewResult)?.ViewData.Model<\/b>\n                    <b class=\"fm-bold\">as IEnumerable&lt;Product&gt;<\/b> \n                         <b class=\"fm-bold\">?? Enumerable.Empty&lt;Product&gt;();<\/b>\n                                                 \n            <b class=\"fm-bold\">\/\/ Assert<\/b>\n            <b class=\"fm-bold\">Product[] prodArray = result.ToArray();<\/b>\n            <b class=\"fm-bold\">Assert.True(prodArray.Length == 2);<\/b>\n            <b class=\"fm-bold\">Assert.Equal(\"P4\", prodArray[0].Name);<\/b>\n            <b class=\"fm-bold\">Assert.Equal(\"P5\", prodArray[1].Name);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"fm-sidebar-text\">You can see the new test follows the pattern of the existing one, relying on Moq to provide a known set of data with which to work.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-148\">7.4.1 Displaying page links<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1546\"><\/a>Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see that there are now four items shown on the page, as shown in figure 7.4. If you want to view another page, you can append query string parameters to the end of the URL, like this:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost:5000\/<b class=\"fm-bold\">?productPage=2<\/b><\/pre>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre53\" src=\"\/images\/proaspnetcore7\/000049.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 7.4 Paging through data<\/p>\n<\/div>\n<p class=\"body\">Using these query strings, you can navigate through the catalog of products. There is no way for customers to figure out that these query string parameters exist, and even if there were, customers are not going to want to navigate this way. Instead, I need to render some page links at the bottom of each list of products so that customers can navigate between pages. To do this, I am going to create a <i class=\"fm-italics\">tag helper<\/i>, which generates the HTML markup for the links I require.<\/p>\n<p class=\"fm-head2\">Adding the view model<\/p>\n<p class=\"body\">To support the tag helper, I am going to pass information to the view about the number of pages available, the current page, and the total number of products in the repository. The easiest way to do this is to create a view model class, which is used specifically to pass data between a controller and a view. Create a <code class=\"fm-code-in-text\">Models\/ViewModels<\/code> folder in the <code class=\"fm-code-in-text\">SportsStore<\/code> project, add to it a class file named <code class=\"fm-code-in-text\">PagingInfo.cs<\/code>, and define the class shown in listing 7.27.<a id=\"calibre_link-1167\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.27 The contents of the PagingInfo.cs file in the SportsStore\/Models\/ViewModels folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Models.ViewModels {\n\n    public class PagingInfo {\n        public int TotalItems { get; set; }\n        public int ItemsPerPage { get; set; }\n        public int CurrentPage { get; set; }\n                \n        public int TotalPages =&gt; \n            (int)Math.Ceiling((decimal)TotalItems \/ ItemsPerPage);\n    }\n}<\/pre>\n<p class=\"fm-head2\">Adding the tag helper class<\/p>\n<p class=\"body\">Now that I have a view model, it is time to create a tag helper class. Create a folder named <code class=\"fm-code-in-text\">Infrastructure<\/code> in the SportsStore project and add to it a class file called <code class=\"fm-code-in-text\">PageLinkTagHelper.cs<\/code>, with the code shown in listing 7.28. Tag helpers are a big part of ASP.NET Core development, and I explain how they work and how to use and create them in chapters 25&ndash;27.<a id=\"calibre_link-1547\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">Infrastructure<\/code> folder is where I put classes that deliver the plumbing for an application but that are not related to the application\u2019s main functionality. You don\u2019t have to follow this convention in your own projects.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.28 The contents of the PageLinkTagHelper.cs file in the SportsStore\/Infrastructure folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Mvc.Routing;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\nusing SportsStore.Models.ViewModels;\n\nnamespace SportsStore.Infrastructure {\n\n    [HtmlTargetElement(\"div\", Attributes = \"page-model\")]\n    public class PageLinkTagHelper : TagHelper {\n        private IUrlHelperFactory urlHelperFactory;\n                \n        public PageLinkTagHelper(IUrlHelperFactory helperFactory) {\n            urlHelperFactory = helperFactory;\n        }\n                \n        [ViewContext]\n        [HtmlAttributeNotBound]\n        public ViewContext? ViewContext { get; set; }\n                \n        public PagingInfo? PageModel { get; set; }\n                \n        public string? PageAction { get; set; }\n                \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n            if (ViewContext != null &amp;&amp; PageModel != null) {\n                IUrlHelper urlHelper \n                    = urlHelperFactory.GetUrlHelper(ViewContext);\n                TagBuilder result = new TagBuilder(\"div\");\n                for (int i = 1; i &lt;= PageModel.TotalPages; i++) {\n                    TagBuilder tag = new TagBuilder(\"a\");\n                    tag.Attributes[\"href\"] = urlHelper.Action(PageAction,\n                       new { productPage = i });\n                    tag.InnerHtml.Append(i.ToString());\n                    result.InnerHtml.AppendHtml(tag);\n                }\n                output.Content.AppendHtml(result.InnerHtml);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1548\"><\/a>This tag helper populates a <code class=\"fm-code-in-text\">div<\/code> element with <code class=\"fm-code-in-text\">a<\/code> elements that correspond to pages of products. I am not going to go into detail about tag helpers now; it is enough to know that they are one of the most useful ways that you can introduce C# logic into your views. The code for a tag helper can look tortured because C# and HTML don\u2019t mix easily. But using tag helpers is preferable to including blocks of C# code in a view because a tag helper can be easily unit tested.<\/p>\n<p class=\"body\">Most ASP.NET Core components, such as controllers and views, are discovered automatically, but tag helpers have to be registered. In listing 7.29, I have added a statement to the <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views<\/code> folder that tells ASP.NET Core to look for tag helper classes in the SportsStore project. I also added an <code class=\"fm-code-in-text\">@using<\/code> expression so that I can refer to the view model classes in views without having to qualify their names with the namespace.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.29 Registering a tag helper in the _ViewImports.cshtml file in the SportsStore\/Views folder<\/p>\n<pre class=\"programlisting\">@using SportsStore.Models\n<b class=\"fm-bold\">@using SportsStore.Models.ViewModels<\/b>\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n<b class=\"fm-bold\">@addTagHelper *, SportsStore<\/b><\/pre>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit test: creating page links<\/p>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1549\"><\/a>To test the <code class=\"fm-code-in-text1\">PageLinkTagHelper<\/code> tag helper class, I call the <code class=\"fm-code-in-text1\">Process<\/code> method with test data and provide a <code class=\"fm-code-in-text1\">TagHelperOutput<\/code> object that I inspect to see the HTML that is generated, as follows, which I defined in a new <code class=\"fm-code-in-text1\">PageLinkTagHelperTests.cs<\/code> file in the <code class=\"fm-code-in-text1\">SportsStore.Tests<\/code> project:<\/p>\n<pre class=\"programlisting\">using System.Collections.Generic;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Mvc.Routing;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\nusing Moq;\nusing SportsStore.Infrastructure;\nusing SportsStore.Models.ViewModels;\nusing Xunit;\n\nnamespace SportsStore.Tests {\n\n    public class PageLinkTagHelperTests {\n        \n        [Fact]\n        public void Can_Generate_Page_Links() {\n            \/\/ Arrange\n            var urlHelper = new Mock&lt;IUrlHelper&gt;();\n            urlHelper.SetupSequence(x =&gt; \n                     x.Action(It.IsAny&lt;UrlActionContext&gt;()))\n                .Returns(\"Test\/Page1\")\n                .Returns(\"Test\/Page2\")\n                .Returns(\"Test\/Page3\");\n                                \n            var urlHelperFactory = new Mock&lt;IUrlHelperFactory&gt;();\n            urlHelperFactory.Setup(f =&gt;\n                    f.GetUrlHelper(It.IsAny&lt;ActionContext&gt;()))\n                        .Returns(urlHelper.Object);\n                                                \n            var viewContext = new Mock&lt;ViewContext&gt;();\n                        \n            PageLinkTagHelper helper =\n                    new PageLinkTagHelper(urlHelperFactory.Object) {\n                        PageModel = new PagingInfo {\n                            CurrentPage = 2,\n                            TotalItems = 28,\n                            ItemsPerPage = 10\n                        },\n                        ViewContext = viewContext.Object,\n                        PageAction = \"Test\"\n                    };\n                                        \n            TagHelperContext ctx = new TagHelperContext(\n                new TagHelperAttributeList(),\n                new Dictionary&lt;object, object&gt;(), \"\");\n                                \n            var content = new Mock&lt;TagHelperContent&gt;();\n            TagHelperOutput output = new TagHelperOutput(\"div\",\n                new TagHelperAttributeList(),\n                (cache, encoder) =&gt; Task.FromResult(content.Object));\n                                \n            \/\/ Act\n            helper.Process(ctx, output);\n                        \n            \/\/ Assert\n            Assert.Equal(@\"&lt;a href=\"\"Test\/Page1\"\"&gt;1&lt;\/a&gt;\"\n                + @\"&lt;a href=\"\"Test\/Page2\"\"&gt;2&lt;\/a&gt;\"\n                + @\"&lt;a href=\"\"Test\/Page3\"\"&gt;3&lt;\/a&gt;\",\n                 output.Content.GetContent());\n        }\n    }\n}<\/pre>\n<p class=\"fm-sidebar-text\">The complexity in this test is in creating the objects that are required to create and use a tag helper. Tag helpers use <code class=\"fm-code-in-text1\">IUrlHelperFactory<\/code> objects to generate URLs that target different parts of the application, and I have used Moq to create an implementation of this interface and the related <code class=\"fm-code-in-text1\">IUrlHelper<\/code> interface that provides test data.<\/p>\n<p class=\"fm-sidebar-text\">The core part of the test verifies the tag helper output by using a literal string value that contains double quotes. C# is perfectly capable of working with such strings, as long as the string is prefixed with <code class=\"fm-code-in-text1\">@<\/code> and uses two sets of double quotes (<code class=\"fm-code-in-text1\">\"\"<\/code>) in place of one set of double quotes. You must remember not to break the literal string into separate lines unless the string you are comparing to is similarly broken. For example, the literal I use in the test method has wrapped onto several lines because the width of a printed page is narrow. I have not added a newline character; if I did, the test would fail.<\/p>\n<\/div>\n<p class=\"fm-head2\">Adding the view model data<\/p>\n<p class=\"body\"><a id=\"calibre_link-1550\"><\/a>I am not quite ready to use the tag helper because I have yet to provide an instance of the <code class=\"fm-code-in-text\">PagingInfo<\/code> view model class to the view. To do this, I added a class file called <code class=\"fm-code-in-text\">ProductsListViewModel.cs<\/code> to the <code class=\"fm-code-in-text\">Models\/ViewModels<\/code> folder of the SportsStore project with the content shown in listing 7.30.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.30 The contents of the ProductsListViewModel.cs file in the SportsStore\/Models\/ViewModels folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Models.ViewModels {\n\n    public class ProductsListViewModel {\n        public IEnumerable&lt;Product&gt; Products { get; set; } \n            = Enumerable.Empty&lt;Product&gt;();\n        public PagingInfo PagingInfo { get; set; } = new();\n    }\n}<\/pre>\n<p class=\"body\">I can update the <code class=\"fm-code-in-text\">Index<\/code> action method in the <code class=\"fm-code-in-text\">HomeController<\/code> class to use the <code class=\"fm-code-in-text\">ProductsListViewModel<\/code> class to provide the view with details of the products to display on the page and with details of the pagination, as shown in listing 7.31.<a id=\"calibre_link-1551\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.31 Updating the action method in the HomeController.cs file in the SportsStore\/Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing SportsStore.Models;\n<b class=\"fm-bold\">using SportsStore.Models.ViewModels;<\/b>\n\nnamespace SportsStore.Controllers {\n    public class HomeController : Controller {\n        private IStoreRepository repository;\n        public int PageSize = 4;\n                \n        public HomeController(IStoreRepository repo) {\n            repository = repo;\n        }\n                \n        <b class=\"fm-bold\">public ViewResult Index(int productPage = 1)<\/b>\n           <b class=\"fm-bold\">=&gt; View(new ProductsListViewModel {<\/b>\n               <b class=\"fm-bold\">Products = repository.Products<\/b>\n                   <b class=\"fm-bold\">.OrderBy(p =&gt; p.ProductID)<\/b>\n                   <b class=\"fm-bold\">.Skip((productPage - 1) * PageSize)<\/b>\n                   <b class=\"fm-bold\">.Take(PageSize),<\/b>\n               <b class=\"fm-bold\">PagingInfo = new PagingInfo {<\/b>\n                   <b class=\"fm-bold\">CurrentPage = productPage,<\/b>\n                   <b class=\"fm-bold\">ItemsPerPage = PageSize,<\/b>\n                   <b class=\"fm-bold\">TotalItems = repository.Products.Count()<\/b>\n               <b class=\"fm-bold\">}<\/b>\n           <b class=\"fm-bold\">});<\/b>\n    }\n}<\/pre>\n<p class=\"body\">These changes pass a <code class=\"fm-code-in-text\">ProductsListViewModel<\/code> object as the model data to the view.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit test: page model view data<\/p>\n<p class=\"fm-sidebar-text\">I need to ensure that the controller sends the correct pagination data to the view. Here is the unit test I added to the <code class=\"fm-code-in-text1\">HomeControllerTests<\/code> class in the test project to make sure:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Send_Pagination_View_Model() {\n\n    \/\/ Arrange\n    Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n    mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product {ProductID = 1, Name = \"P1\"},\n        new Product {ProductID = 2, Name = \"P2\"},\n        new Product {ProductID = 3, Name = \"P3\"},\n        new Product {ProductID = 4, Name = \"P4\"},\n        new Product {ProductID = 5, Name = \"P5\"}\n    }).AsQueryable&lt;Product&gt;());\n        \n    \/\/ Arrange\n    HomeController controller =\n        new HomeController(mock.Object) { PageSize = 3 };\n                \n    \/\/ Act\n    ProductsListViewModel result =\n        controller.Index(2)?.ViewData.Model as ProductsListViewModel \n            ?? new();\n                        \n    \/\/ Assert\n    PagingInfo pageInfo = result.PagingInfo;\n    Assert.Equal(2, pageInfo.CurrentPage);\n    Assert.Equal(3, pageInfo.ItemsPerPage);\n    Assert.Equal(5, pageInfo.TotalItems);\n    Assert.Equal(2, pageInfo.TotalPages);\n} \n...<\/pre>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1552\"><\/a>I also need to modify the earlier unit tests to reflect the new result from the <code class=\"fm-code-in-text1\">Index<\/code> action method. Here are the revised tests:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Use_Repository() {\n    \/\/ Arrange\n    Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n    mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product {ProductID = 1, Name = \"P1\"},\n        new Product {ProductID = 2, Name = \"P2\"}\n    }).AsQueryable&lt;Product&gt;());\n        \n    HomeController controller = new HomeController(mock.Object);\n        \n    \/\/ Act\n    <b class=\"fm-bold\">ProductsListViewModel result =<\/b>\n        <b class=\"fm-bold\">controller.Index()?.ViewData.Model as ProductsListViewModel<\/b> \n            <b class=\"fm-bold\">?? new();<\/b>\n                        \n    \/\/ Assert\n    <b class=\"fm-bold\">Product[] prodArray = result.Products.ToArray();<\/b>\n    Assert.True(prodArray.Length == 2);\n    Assert.Equal(\"P1\", prodArray[0].Name);\n    Assert.Equal(\"P2\", prodArray[1].Name);\n}\n\n[Fact]\npublic void Can_Paginate() {\n    \/\/ Arrange\n    Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n    mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product {ProductID = 1, Name = \"P1\"},\n        new Product {ProductID = 2, Name = \"P2\"},\n        new Product {ProductID = 3, Name = \"P3\"},\n        new Product {ProductID = 4, Name = \"P4\"},\n        new Product {ProductID = 5, Name = \"P5\"}\n    }).AsQueryable&lt;Product&gt;());\n        \n    HomeController controller = new HomeController(mock.Object);\n    controller.PageSize = 3;\n        \n    \/\/ Act\n    <b class=\"fm-bold\">ProductsListViewModel result =<\/b>\n        <b class=\"fm-bold\">controller.Index(2)?.ViewData.Model as ProductsListViewModel<\/b> \n            <b class=\"fm-bold\">?? new();<\/b>\n                        \n    \/\/ Assert\n    <b class=\"fm-bold\">Product[] prodArray = result.Products.ToArray();<\/b>\n    Assert.True(prodArray.Length == 2);\n    Assert.Equal(\"P4\", prodArray[0].Name);\n    Assert.Equal(\"P5\", prodArray[1].Name);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">I would usually create a common setup method, given the degree of duplication between these two test methods. However, since I am delivering the unit tests in individual sidebars like this one, I am going to keep everything separate so you can see each test on its own.<\/p>\n<\/div>\n<p class=\"body\">The view is currently expecting a sequence of <code class=\"fm-code-in-text\">Product<\/code> objects, so I need to update the <code class=\"fm-code-in-text\">Index.cshtml<\/code> file, as shown in listing 7.32, to deal with the new view model type.<a id=\"calibre_link-1553\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.32 Updating the Index.cshtml file in the SportsStore\/Views\/Home folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@model ProductsListViewModel<\/b>\n\n<b class=\"fm-bold\">@foreach (var p in Model.Products ?? Enumerable.Empty&lt;Product&gt;()) {<\/b>\n    &lt;div&gt;\n        &lt;h3&gt;@p.Name&lt;\/h3&gt;\n        @p.Description\n        &lt;h4&gt;@p.Price.ToString(\"c\")&lt;\/h4&gt;\n    &lt;\/div&gt;\n}<\/pre>\n<p class=\"body\">I have changed the <code class=\"fm-code-in-text\">@model<\/code> directive to tell Razor that I am now working with a different data type. I updated the <code class=\"fm-code-in-text\">foreach<\/code> loop so that the data source is the <code class=\"fm-code-in-text\">Products<\/code> property of the model data.<\/p>\n<p class=\"fm-head2\">Displaying the page links<\/p>\n<p class=\"body\">I have everything in place to add the page links to the <code class=\"fm-code-in-text\">Index<\/code> view. I created the view model that contains the paging information, updated the controller so that it passes this information to the view, and changed the <code class=\"fm-code-in-text\">@model<\/code> directive to match the new model view type. All that remains is to add an HTML element that the tag helper will process to create the page links, as shown in listing 7.33.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.33 Adding the pagination links in the Index.cshtml file in the SportsStore\/Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model ProductsListViewModel\n\n@foreach (var p in Model.Products ?? Enumerable.Empty&lt;Product&gt;()) {\n    &lt;div&gt;\n        &lt;h3&gt;@p.Name&lt;\/h3&gt;\n        @p.Description\n        &lt;h4&gt;@p.Price.ToString(\"c\")&lt;\/h4&gt;\n    &lt;\/div&gt;\n}\n\n<b class=\"fm-bold\">&lt;div page-model=\"@Model.PagingInfo\" page-action=\"Index\"&gt;&lt;\/div&gt;<\/b><\/pre>\n<p class=\"body\"><a id=\"calibre_link-1554\"><\/a>Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the new page links, as shown in figure 7.5. The style is still basic, which I will fix later in the chapter. What is important for the moment is that the links take the user from page to page in the catalog and allow for exploration of the products for sale. When Razor finds the <code class=\"fm-code-in-text\">page-model<\/code> attribute on the <code class=\"fm-code-in-text\">div<\/code> element, it asks the <code class=\"fm-code-in-text\">PageLinkTagHelper<\/code> class to transform the element, which produces the set of links shown in the figure.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre54\" src=\"\/images\/proaspnetcore7\/000050.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 7.5 Displaying page navigation links<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-149\">7.4.2 Improving the URLs<\/h3>\n<p class=\"body\">I have the page links working, but they still use the query string to pass page information to the server, like this:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost\/?productPage=2<\/pre>\n<p class=\"body\">I can create URLs that are more appealing by creating a scheme that follows the pattern of <i class=\"fm-italics\">composable URLs<\/i>. A composable URL is one that makes sense to the user, like this one:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost\/Page2<\/pre>\n<p class=\"body\">The ASP.NET Core routing feature makes it easy to change the URL scheme in an application. All I need to do is add a new route in the <code class=\"fm-code-in-text\">Program.cs<\/code> file, as shown in listing 7.34.<a id=\"calibre_link-1555\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.34 Adding a new route in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n<b class=\"fm-bold\">app.MapControllerRoute(\"pagination\",<\/b>\n    <b class=\"fm-bold\">\"Products\/Page{productPage}\",<\/b>\n    <b class=\"fm-bold\">new { Controller = \"Home\", action = \"Index\" });<\/b>\napp.MapDefaultControllerRoute();\n\nSeedData.EnsurePopulated(app);\n\napp.Run();<\/pre>\n<p class=\"body\">This is the only alteration required to change the URL scheme for product pagination. ASP.NET Core and the routing function are tightly integrated, so the application automatically reflects a change like this in the URLs used by the application, including those generated by tag helpers like the one I use to generate the page navigation links.<\/p>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000, and click one of the pagination links. The browser will navigate to a URL that uses the new URL scheme, as shown in figure 7.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre55\" src=\"\/images\/proaspnetcore7\/000051.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 7.6 The new URL scheme displayed in the browser<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-150\">7.5 Styling the content<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1157\"><\/a>I have built a great deal of infrastructure, and the basic features of the application are starting to come together, but I have not paid any attention to appearance. Even though this book is not about design or CSS, the SportsStore application design is so miserably plain that it undermines its technical strengths. In this section, I will put some of that right. I am going to implement a classic two-column layout with a header, as shown in figure 7.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre56\" src=\"\/images\/proaspnetcore7\/000052.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 7.7 The design goal for the SportsStore application<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-151\">7.5.1 Installing the Bootstrap package<\/h3>\n<p class=\"body\">I am going to use the Bootstrap package to provide the CSS styles I will apply to the application. As explained in chapter 4, client-side packages are installed using LibMan. If you did not install the LibMan package when following the examples in chapter 4, use a PowerShell command prompt to run the commands shown in listing 7.35, which remove any existing LibMan package and install the version required for this book.<a id=\"calibre_link-1556\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.35 Installing the LibMan tool package<\/p>\n<pre class=\"programlisting\">dotnet tool uninstall --global Microsoft.Web.LibraryManager.Cli\ndotnet tool install --global Microsoft.Web.LibraryManager.Cli\n     --version 2.1.175<\/pre>\n<p class=\"body\">Once you have installed LibMan, run the commands shown in listing 7.36 in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder to initialize the example project and install the Bootstrap package.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.36 Initializing the example project<\/p>\n<pre class=\"programlisting\">libman init -p cdnjs\nlibman install bootstrap@5.2.3 -d wwwroot\/lib\/bootstrap<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-152\">7.5.2 Applying Bootstrap styles<\/h3>\n<p class=\"body\">Razor layouts provide common content so that it doesn\u2019t have to be repeated in multiple views. Add the elements shown in listing 7.37 to the <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder to include the Bootstrap CSS stylesheet in the content sent to the browser and define a common header that will be used throughout the SportsStore application.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.37 Applying Bootstrap CSS to the _Layout.cshtml file in the SportsStore\/Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;SportsStore&lt;\/title&gt;\n    <b class=\"fm-bold\">&lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;<\/b>    \n&lt;\/head&gt;\n&lt;body&gt;\n    <b class=\"fm-bold\">&lt;div class=\"bg-dark text-white p-2\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;span class=\"navbar-brand ml-2\"&gt;SPORTS STORE&lt;\/span&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">&lt;div class=\"row m-1 p-1\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div id=\"categories\" class=\"col-3\"&gt;<\/b>\n            <b class=\"fm-bold\">Put something useful here later<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"col-9\"&gt;<\/b>\n            @RenderBody()\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Adding the Bootstrap CSS stylesheet to the layout means that I can use the styles it defines in any of the views that rely on the layout. Listing 7.38 shows the styling I applied to the <code class=\"fm-code-in-text\">Index.cshtml<\/code> file.<a id=\"calibre_link-1557\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.38 Styling content in the Index.cshtml file in the SportsStore\/Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model ProductsListViewModel\n\n@foreach (var p in Model.Products ?? Enumerable.Empty&lt;Product&gt;()) {\n    <b class=\"fm-bold\">&lt;div class=\"card card-outline-primary m-1 p-1\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"bg-faded p-1\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;h4&gt;<\/b>\n                <b class=\"fm-bold\">@p.Name<\/b>\n                <b class=\"fm-bold\">&lt;span class=\"badge rounded-pill bg-primary text-white\"<\/b> \n                        <b class=\"fm-bold\">style=\"float:right\"&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;small&gt;@p.Price.ToString(\"c\")&lt;\/small&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/span&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/h4&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"card-text p-1\"&gt;@p.Description&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n}\n\n<b class=\"fm-bold\">&lt;div page-model=\"@Model.PagingInfo\" page-action=\"Index\"<\/b> \n     <b class=\"fm-bold\">page-classes-enabled=\"true\" page-class=\"btn\"<\/b> \n     <b class=\"fm-bold\">page-class-normal=\"btn-outline-dark\"<\/b>\n     <b class=\"fm-bold\">page-class-selected=\"btn-primary\" class=\"btn-group pull-right m-1\"&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b><\/pre>\n<p class=\"body\"><a id=\"calibre_link-1558\"><\/a>I need to style the buttons generated by the <code class=\"fm-code-in-text\">PageLinkTagHelper<\/code> class, but I don\u2019t want to hardwire the Bootstrap classes into the C# code because it makes it harder to reuse the tag helper elsewhere in the application or change the appearance of the buttons. Instead, I have defined custom attributes on the <code class=\"fm-code-in-text\">div<\/code> element that specify the classes that I require, and these correspond to properties I added to the tag helper class, which are then used to style the <code class=\"fm-code-in-text\">a<\/code> elements that are produced, as shown in listing 7.39.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.39 Adding classes to elements in the PageLinkTagHelper.cs file in the SportsStore\/Infrastructure folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Mvc.Routing;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\nusing SportsStore.Models.ViewModels;\n\nnamespace SportsStore.Infrastructure {\n\n    [HtmlTargetElement(\"div\", Attributes = \"page-model\")]\n    public class PageLinkTagHelper : TagHelper {\n        private IUrlHelperFactory urlHelperFactory;\n                \n        public PageLinkTagHelper(IUrlHelperFactory helperFactory) {\n            urlHelperFactory = helperFactory;\n        }\n                \n        [ViewContext]\n        [HtmlAttributeNotBound]\n        public ViewContext? ViewContext { get; set; }\n                \n        public PagingInfo? PageModel { get; set; }\n                \n        public string? PageAction { get; set; }\n                \n        <b class=\"fm-bold\">public bool PageClassesEnabled { get; set; } = false;<\/b>\n        <b class=\"fm-bold\">public string PageClass { get; set; } = String.Empty;<\/b>\n        <b class=\"fm-bold\">public string PageClassNormal { get; set; } = String.Empty;<\/b>\n        <b class=\"fm-bold\">public string PageClassSelected { get; set; } = String.Empty;<\/b>\n                \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n            if (ViewContext != null &amp;&amp; PageModel != null) {\n                IUrlHelper urlHelper \n                    = urlHelperFactory.GetUrlHelper(ViewContext);\n                TagBuilder result = new TagBuilder(\"div\");\n                for (int i = 1; i &lt;= PageModel.TotalPages; i++) {\n                    TagBuilder tag = new TagBuilder(\"a\");\n                    tag.Attributes[\"href\"] = urlHelper.Action(PageAction,\n                       new { productPage = i });\n                    <b class=\"fm-bold\">if (PageClassesEnabled) {<\/b>\n                        <b class=\"fm-bold\">tag.AddCssClass(PageClass);<\/b>\n                        <b class=\"fm-bold\">tag.AddCssClass(i == PageModel.CurrentPage<\/b>\n                            <b class=\"fm-bold\">? PageClassSelected : PageClassNormal);<\/b>\n                    <b class=\"fm-bold\">}<\/b>\n                    tag.InnerHtml.Append(i.ToString());\n                    result.InnerHtml.AppendHtml(tag);\n                }\n                output.Content.AppendHtml(result.InnerHtml);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The values of the attributes are automatically used to set the tag helper property values, with the mapping between the HTML attribute name format (<code class=\"fm-code-in-text\">page-class-normal<\/code>) and the C# property name format (<code class=\"fm-code-in-text\">PageClassNormal<\/code>) taken into account. This allows tag helpers to respond differently based on the attributes of an HTML element, creating a more flexible way to generate content in an ASP.NET Core application.<\/p>\n<p class=\"body\"><a id=\"calibre_link-1559\"><\/a>Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the appearance of the application has been improved&mdash;at least a little, anyway&mdash;as illustrated by figure 7.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre57\" src=\"\/images\/proaspnetcore7\/000053.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 7.8 Applying styles to the SportsStore application<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-153\">7.5.3 Creating a partial view<\/h3>\n<p class=\"body\">As a finishing flourish for this chapter, I am going to refactor the application to simplify the <code class=\"fm-code-in-text\">Index.cshtml<\/code> view. I am going to create a <i class=\"fm-italics\">partial view<\/i>, which is a fragment of content that you can embed into another view, rather like a template. I describe partial views in detail in chapter 22, and they help reduce duplication when you need the same content to appear in different places in an application. Rather than copy and paste the same Razor markup into multiple views, you can define it once in a partial view. To create the partial view, I added a Razor View called <code class=\"fm-code-in-text\">ProductSummary.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder and added the markup shown in listing 7.40.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.40 The contents of the ProductSummary.cshtml file in the SportsStore\/Views\/Shared folder<\/p>\n<pre class=\"programlisting\">@model Product\n\n&lt;div class=\"card card-outline-primary m-1 p-1\"&gt;\n    &lt;div class=\"bg-faded p-1\"&gt;\n        &lt;h4&gt;\n            @Model.Name\n            &lt;span class=\"badge rounded-pill bg-primary text-white\"\n                  style=\"float:right\"&gt;\n                &lt;small&gt;@Model.Price.ToString(\"c\")&lt;\/small&gt;\n            &lt;\/span&gt;\n        &lt;\/h4&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"card-text p-1\"&gt;@Model.Description&lt;\/div&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1560\"><\/a>Now I need to update the <code class=\"fm-code-in-text\">Index.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder so that it uses the partial view, as shown in listing 7.41.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 7.41 Using a partial view in the Index.cshtml file in the SportsStore\/Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model ProductsListViewModel\n\n@foreach (var p in Model.Products ?? Enumerable.Empty&lt;Product&gt;()) {\n    <b class=\"fm-bold\">&lt;partial name=\"ProductSummary\" model=\"p\" \/&gt;<\/b>\n}\n\n&lt;div page-model=\"@Model.PagingInfo\" page-action=\"Index\"\n     page-classes-enabled=\"true\" page-class=\"btn\"\n     page-class-normal=\"btn-outline-dark\"\n     page-class-selected=\"btn-primary\" class=\"btn-group pull-right m-1\"&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">I have taken the markup that was previously in the <code class=\"fm-code-in-text\">@foreach<\/code> expression in the <code class=\"fm-code-in-text\">Index.cshtml<\/code> view and moved it to the new partial view. I call the partial view using a <code class=\"fm-code-in-text\">partial<\/code> element, using the <code class=\"fm-code-in-text\">name<\/code> and <code class=\"fm-code-in-text\">model<\/code> attributes to specify the name of the partial view and its view model. Using a partial view allows the same markup to be inserted into any view that needs to display a summary of a product.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see that introducing the partial view doesn\u2019t change the appearance of the application; it just changes where Razor finds the content that is used to generate the response sent to the browser.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-154\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The SportsStore ASP.NET Core project is created using the basic ASP.NET Core template.<a class=\"calibre58\" id=\"calibre_link-1561\"><\/a><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core has close integration with Entity Framework Core, which is the .NET framework for working with relational data.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Data can be paginated by including the page number in the request, either using the query string or the URL path and using the page when querying the database.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The HTML content generated by ASP.NET Core can be styled using popular CSS frameworks, such as Bootstrap.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-155\">\n<div class=\"calibre1\" id=\"calibre_link-1562\">\n<h1 class=\"tochead\" id=\"calibre_link-1563\"><a id=\"calibre_link-1564\"><\/a>8 SportsStore: Navigation and cart<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Navigating between product categories<\/li>\n<li class=\"co-summary-bullet\">Correcting the pagination controls to support category navigation<\/li>\n<li class=\"co-summary-bullet\">Using sessions to store data between requests<\/li>\n<li class=\"co-summary-bullet\">Implementing a shopping cart using session data<\/li>\n<li class=\"co-summary-bullet\">Displaying the shopping cart contents using Razor Pages<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-1565\"><\/a>In this chapter, I continue to build out the SportsStore example app. I add support for navigating around the application and start building a shopping cart.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-156\">8.1 Adding navigation controls<\/h2>\n<p class=\"body\">The SportsStore application will be more useful if customers can navigate products by category. I will do this in three phases:<\/p>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Enhance the <code class=\"fm-code-in-text\">Index<\/code> action method in the <code class=\"fm-code-in-text\">HomeController<\/code> class so that it can filter the <code class=\"fm-code-in-text\">Product<\/code> objects in the repository<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Revisit and enhance the URL scheme<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Create a category list that will go into the sidebar of the site, highlighting the current category and linking to others<\/p>\n<\/li>\n<\/ul>\n<h3 class=\"fm-head1\" id=\"calibre_link-157\">8.1.1 Filtering the product list<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1160\"><\/a>I am going to start by enhancing the view model class, <code class=\"fm-code-in-text\">ProductsListViewModel<\/code>, which I added to the <code class=\"fm-code-in-text\">SportsStore<\/code> project in the previous chapter. I need to communicate the current category to the view to render the sidebar, and this is as good a place to start as any. Listing 8.1 shows the changes I made to the <code class=\"fm-code-in-text\">ProductsListViewModel.cs<\/code> file in the <code class=\"fm-code-in-text\">Models\/ViewModels<\/code> folder.<a id=\"calibre_link-1566\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.1 Modifying the ProductsListViewModel.cs file in the SportsStore\/Models\/ ViewModels folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Models.ViewModels {\n\n    public class ProductsListViewModel {\n        public IEnumerable&lt;Product&gt; Products { get; set; }\n            = Enumerable.Empty&lt;Product&gt;();\n        public PagingInfo PagingInfo { get; set; } = new();\n        <b class=\"fm-bold\">public string? CurrentCategory { get; set; }<\/b>\n    }\n}<\/pre>\n<p class=\"body\">I added a property called <code class=\"fm-code-in-text\">CurrentCategory<\/code>. The next step is to update the <code class=\"fm-code-in-text\">Home<\/code> controller so that the <code class=\"fm-code-in-text\">Index<\/code> action method will filter <code class=\"fm-code-in-text\">Product<\/code> objects by category and use the property I added to the view model to indicate which category has been selected, as shown in listing 8.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.2 Supporting categories in the HomeController.cs file in the SportsStore\/ Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing SportsStore.Models;\nusing SportsStore.Models.ViewModels;\n\nnamespace SportsStore.Controllers {\n    public class HomeController : Controller {\n        private IStoreRepository repository;\n        public int PageSize = 4;\n                \n        public HomeController(IStoreRepository repo) {\n            repository = repo;\n        }\n                \n        <b class=\"fm-bold\">public ViewResult Index(string? category, int productPage = 1)<\/b>\n           =&gt; View(new ProductsListViewModel {\n               Products = repository.Products\n                    <b class=\"fm-bold\">.Where(p =&gt; category == null ||<\/b> \n                        <b class=\"fm-bold\">p.Category == category)<\/b>\n                   .OrderBy(p =&gt; p.ProductID)\n                   .Skip((productPage - 1) * PageSize)\n                   .Take(PageSize),\n               PagingInfo = new PagingInfo {\n                   CurrentPage = productPage,\n                   ItemsPerPage = PageSize,\n                   TotalItems = repository.Products.Count()\n               },\n               <b class=\"fm-bold\">CurrentCategory = category<\/b>\n           });\n    }\n}<\/pre>\n<p class=\"body\">I made three changes to the action method. First, I added a parameter called <code class=\"fm-code-in-text\">category<\/code>. This parameter is used by the second change in the listing, which is an enhancement to the LINQ query: if <code class=\"fm-code-in-text\">cat<\/code> is not <code class=\"fm-code-in-text\">null<\/code>, only those <code class=\"fm-code-in-text\">Product<\/code> objects with a matching <code class=\"fm-code-in-text\">Category<\/code> property are selected. The last change is to set the value of the <code class=\"fm-code-in-text\">CurrentCategory<\/code> property I added to the <code class=\"fm-code-in-text\">ProductsListViewModel<\/code> class. However, these changes mean that the value of <code class=\"fm-code-in-text\">PagingInfo.TotalItems<\/code> is incorrectly calculated because it doesn\u2019t take the category filter into account. I will fix this later.<a id=\"calibre_link-1567\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit test: updating existing tests<\/p>\n<p class=\"fm-sidebar-text\">I changed the signature of the <code class=\"fm-code-in-text1\">Index<\/code> action method, which will prevent some of the existing unit test methods from compiling. To address this, I need to pass <code class=\"fm-code-in-text1\">null<\/code> as the first parameter to the <code class=\"fm-code-in-text1\">Index<\/code> method in those unit tests that work with the controller. For example, in the <code class=\"fm-code-in-text1\">Can_Use_Repository<\/code> test in the <code class=\"fm-code-in-text1\">HomeControllerTests.cs<\/code> file, the action section of the unit test becomes as follows:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Use_Repository() {\n    \/\/ Arrange\n    Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n    mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product {ProductID = 1, Name = \"P1\"},\n        new Product {ProductID = 2, Name = \"P2\"}\n    }).AsQueryable&lt;Product&gt;());\n        \n    HomeController controller = new HomeController(mock.Object);\n        \n    \/\/ Act\n    <b class=\"fm-bold\">ProductsListViewModel result =<\/b>\n        <b class=\"fm-bold\">controller.Index(null)?.ViewData.Model<\/b> \n            <b class=\"fm-bold\">as ProductsListViewModel ?? new();<\/b>\n                        \n    \/\/ Assert\n    Product[] prodArray = result.Products.ToArray();\n    Assert.True(prodArray.Length == 2);\n    Assert.Equal(\"P1\", prodArray[0].Name);\n    Assert.Equal(\"P2\", prodArray[1].Name);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1568\"><\/a>By using <code class=\"fm-code-in-text1\">null<\/code> for the <code class=\"fm-code-in-text1\">category<\/code> argument, I receive all the <code class=\"fm-code-in-text1\">Product<\/code> objects that the controller gets from the repository, which is the same situation I had before adding the new parameter. I need to make the same change to the <code class=\"fm-code-in-text1\">Can_Paginate<\/code> and <code class=\"fm-code-in-text1\">Can_Send_Pagination_View_Model<\/code> tests as well.<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Paginate() {\n    \/\/ Arrange\n    Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n    mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product {ProductID = 1, Name = \"P1\"},\n        new Product {ProductID = 2, Name = \"P2\"},\n        new Product {ProductID = 3, Name = \"P3\"},\n        new Product {ProductID = 4, Name = \"P4\"},\n        new Product {ProductID = 5, Name = \"P5\"}\n    }).AsQueryable&lt;Product&gt;());\n        \n    HomeController controller = new HomeController(mock.Object);\n    controller.PageSize = 3;\n        \n    \/\/ Act\n    <b class=\"fm-bold\">ProductsListViewModel result =<\/b>\n        <b class=\"fm-bold\">controller.Index(null, 2)?.ViewData.Model<\/b> \n            <b class=\"fm-bold\">as ProductsListViewModel ?? new();<\/b>\n                        \n    \/\/ Assert\n    Product[] prodArray = result.Products.ToArray();\n    Assert.True(prodArray.Length == 2);\n    Assert.Equal(\"P4\", prodArray[0].Name);\n    Assert.Equal(\"P5\", prodArray[1].Name);\n}\n\n[Fact]\npublic void Can_Send_Pagination_View_Model() {\n\n    \/\/ Arrange\n    Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n    mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product {ProductID = 1, Name = \"P1\"},\n        new Product {ProductID = 2, Name = \"P2\"},\n        new Product {ProductID = 3, Name = \"P3\"},\n        new Product {ProductID = 4, Name = \"P4\"},\n        new Product {ProductID = 5, Name = \"P5\"}\n    }).AsQueryable&lt;Product&gt;());\n        \n    \/\/ Arrange\n    HomeController controller =\n        new HomeController(mock.Object) { PageSize = 3 };\n                \n    \/\/ Act\n    <b class=\"fm-bold\">ProductsListViewModel result =<\/b>\n        <b class=\"fm-bold\">controller.Index(null, 2)?.ViewData.Model as<\/b>\n           <b class=\"fm-bold\">ProductsListViewModel ?? new();<\/b>\n                   \n    \/\/ Assert\n    PagingInfo pageInfo = result.PagingInfo;\n    Assert.Equal(2, pageInfo.CurrentPage);\n    Assert.Equal(3, pageInfo.ItemsPerPage);\n    Assert.Equal(5, pageInfo.TotalItems);\n    Assert.Equal(2, pageInfo.TotalPages);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">Keeping your unit tests synchronized with your code changes quickly becomes second nature when you get into the testing mindset.<\/p>\n<\/div>\n<p class=\"body\">To see the effect of the category filtering, start ASP.NET Core and select a category using the following URL:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost:5000\/?category=soccer<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1569\"><\/a>You will see only the products in the <code class=\"fm-code-in-text\">Soccer<\/code> category, as shown in figure 8.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre59\" src=\"\/images\/proaspnetcore7\/000054.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.1 Using the query string to filter by category<\/p>\n<\/div>\n<p class=\"body\">Users won\u2019t want to navigate to categories using URLs, but you can see how small changes can have a big impact once the basic structure of an ASP.NET Core application is in place.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit Test: category filtering<\/p>\n<p class=\"fm-sidebar-text\">I need a unit test to properly test the category filtering function to ensure that the filter can correctly generate products in a specified category. Here is the test method I added to the <code class=\"fm-code-in-text1\">HomeControllerTests<\/code> class:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Filter_Products() {\n    \/\/ Arrange\n    \/\/ - create the mock repository\n    Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n    mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product {ProductID = 1, Name = \"P1\", Category = \"Cat1\"},\n        new Product {ProductID = 2, Name = \"P2\", Category = \"Cat2\"},\n        new Product {ProductID = 3, Name = \"P3\", Category = \"Cat1\"},\n        new Product {ProductID = 4, Name = \"P4\", Category = \"Cat2\"},\n        new Product {ProductID = 5, Name = \"P5\", Category = \"Cat3\"}\n    }).AsQueryable&lt;Product&gt;());\n        \n    \/\/ Arrange - create a controller and make the page size 3 items\n    HomeController controller = new HomeController(mock.Object);\n    controller.PageSize = 3;\n        \n    \/\/ Action\n    Product[] result = (controller.Index(\"Cat2\", 1)?.ViewData.Model \n        as ProductsListViewModel ?? new()).Products.ToArray();\n                \n    \/\/ Assert\n    Assert.Equal(2, result.Length);\n    Assert.True(result[0].Name == \"P2\" &amp;&amp; result[0].Category == \"Cat2\");\n    Assert.True(result[1].Name == \"P4\" &amp;&amp; result[1].Category == \"Cat2\");\n} \n...<\/pre>\n<p class=\"fm-sidebar-text\">This test creates a mock repository containing <code class=\"fm-code-in-text1\">Product<\/code> objects that belong to a range of categories. One specific category is requested using the action method, and the results are checked to ensure that the results are the right objects in the right order.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-158\">8.1.2 Refining the URL scheme<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1570\"><\/a>No one wants to see or use ugly URLs such as <code class=\"fm-code-in-text\">\/?category=Soccer<\/code>. To address this, I am going to change the routing configuration in the <code class=\"fm-code-in-text\">Program.cs<\/code> file to create a more useful set of URLs, as shown in listing 8.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.3 Changing the schema in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\n<b class=\"fm-bold\">app.MapControllerRoute(\"catpage\",<\/b>\n    <b class=\"fm-bold\">\"{category}\/Page{productPage:int}\",<\/b>\n    <b class=\"fm-bold\">new { Controller = \"Home\", action = \"Index\" });<\/b>\n        \n<b class=\"fm-bold\">app.MapControllerRoute(\"page\",<\/b> <b class=\"fm-bold\">\"Page{productPage:int}\",<\/b>\n    <b class=\"fm-bold\">new { Controller =<\/b> <b class=\"fm-bold\">\"Home\", action =<\/b> <b class=\"fm-bold\">\"Index\", productPage = 1 });<\/b>\n        \n<b class=\"fm-bold\">app.MapControllerRoute(\"category\", \"{category}\",<\/b>\n    <b class=\"fm-bold\">new { Controller = \"Home\", action = \"Index\", productPage = 1 });<\/b>\n        \n<b class=\"fm-bold\">app.MapControllerRoute(\"pagination\",<\/b>\n    <b class=\"fm-bold\">\"Products\/Page{productPage}\",<\/b>\n    <b class=\"fm-bold\">new { Controller = \"Home\", action = \"Index\", productPage = 1 });<\/b>\n        \napp.MapDefaultControllerRoute();\n\nSeedData.EnsurePopulated(app);\n\napp.Run();<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1571\"><\/a>Table 8.1 describes the URL scheme that these routes represent. I explain the routing system in detail in chapter 13.<\/p>\n<p class=\"fm-table-caption\">Table 8.1 Route summary<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1572\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">URL<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Leads To<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Lists the first page of products from all categories<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/Page2<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Lists the specified page (in this case, page 2), showing items from all categories<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/Soccer<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Shows the first page of items from a specific category (in this case, the <code class=\"fm-code-in-text1\">Soccer<\/code> category)<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/Soccer\/Page2<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Shows the specified page (in this case, page 2) of items from the specified category (in this case, <code class=\"fm-code-in-text1\">Soccer<\/code>)<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The ASP.NET Core routing system handles <i class=\"fm-italics\">incoming<\/i> requests from clients, but it also generates <i class=\"fm-italics\">outgoing<\/i> URLs that conform to the URL scheme and that can be embedded in web pages. By using the routing system both to handle incoming requests and to generate outgoing URLs, I can ensure that all the URLs in the application are consistent.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">IUrlHelper<\/code> interface provides access to URL-generating functionality. I used this interface and the <code class=\"fm-code-in-text\">Action<\/code> method it defines in the tag helper I created in the previous chapter. Now that I want to start generating more complex URLs, I need a way to receive additional information from the view without having to add extra properties to the tag helper class. Fortunately, tag helpers have a nice feature that allows properties with a common prefix to be received all together in a single collection, as shown in listing 8.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.4 Prefixed values in the PageLinkTagHelper.cs file in the SportsStore\/ Infrastructure folder<a id=\"calibre_link-1573\"><\/a><\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Mvc.Routing;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\nusing SportsStore.Models.ViewModels;\n\nnamespace SportsStore.Infrastructure {\n\n    [HtmlTargetElement(\"div\", Attributes = \"page-model\")]\n    public class PageLinkTagHelper : TagHelper {\n        private IUrlHelperFactory urlHelperFactory;\n                \n        public PageLinkTagHelper(IUrlHelperFactory helperFactory) {\n            urlHelperFactory = helperFactory;\n        }\n                \n        [ViewContext]\n        [HtmlAttributeNotBound]\n        public ViewContext? ViewContext { get; set; }\n                \n        public PagingInfo? PageModel { get; set; }\n                \n        public string? PageAction { get; set; }\n                \n        <b class=\"fm-bold\">[HtmlAttributeName(DictionaryAttributePrefix = \"page-url-\")]<\/b>\n        <b class=\"fm-bold\">public Dictionary&lt;string, object&gt; PageUrlValues { get; set; }<\/b>\n            <b class=\"fm-bold\">= new Dictionary&lt;string, object&gt;();<\/b>\n                        \n        public bool PageClassesEnabled { get; set; } = false;\n        public string PageClass { get; set; } = String.Empty;\n        public string PageClassNormal { get; set; } = String.Empty;\n        public string PageClassSelected { get; set; } = String.Empty;\n                \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n            if (ViewContext != null &amp;&amp; PageModel != null) {\n                IUrlHelper urlHelper \n                    = urlHelperFactory.GetUrlHelper(ViewContext);\n                TagBuilder result = new TagBuilder(\"div\");\n                for (int i = 1; i &lt;= PageModel.TotalPages; i++) {\n                    TagBuilder tag = new TagBuilder(\"a\");\n                    <b class=\"fm-bold\">PageUrlValues[\"productPage\"] = i;<\/b>\n                    <b class=\"fm-bold\">tag.Attributes[\"href\"] = urlHelper.Action(PageAction,<\/b> \n                        <b class=\"fm-bold\">PageUrlValues);<\/b>\n                    if (PageClassesEnabled) {\n                        tag.AddCssClass(PageClass);\n                        tag.AddCssClass(i == PageModel.CurrentPage\n                            ? PageClassSelected : PageClassNormal);\n                    }\n                    tag.InnerHtml.Append(i.ToString());\n                    result.InnerHtml.AppendHtml(tag);\n                }\n                output.Content.AppendHtml(result.InnerHtml);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">Decorating a tag helper property with the <code class=\"fm-code-in-text\">HtmlAttributeName<\/code> attribute allows me to specify a prefix for attribute names on the element, which in this case will be <code class=\"fm-code-in-text\">page-url-<\/code>. The value of any attribute whose name begins with this prefix will be added to the dictionary that is assigned to the <code class=\"fm-code-in-text\">PageUrlValues<\/code> property, which is then passed to the <code class=\"fm-code-in-text\">IUrlHelper.Action<\/code> method to generate the URL for the <code class=\"fm-code-in-text\">href<\/code> attribute of the <code class=\"fm-code-in-text\">a<\/code> elements that the tag helper produces.<a id=\"calibre_link-1574\"><\/a><\/p>\n<p class=\"body\">In listing 8.5, I have added a new attribute to the <code class=\"fm-code-in-text\">div<\/code> element that is processed by the tag helper, specifying the category that will be used to generate the URL. I have added only one new attribute to the view, but any attribute with the same prefix would be added to the dictionary.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.5 Adding a attribute in the Index.cshtml file in the SportsStore\/Views\/ Home folder<\/p>\n<pre class=\"programlisting\">@model ProductsListViewModel\n\n@foreach (var p in Model.Products ?? Enumerable.Empty&lt;Product&gt;()) {\n    &lt;partial name=\"ProductSummary\" model=\"p\" \/&gt;\n}\n\n&lt;div page-model=\"@Model.PagingInfo\" page-action=\"Index\"\n     page-classes-enabled=\"true\" page-class=\"btn\"\n     page-class-normal=\"btn-outline-dark\"\n     <b class=\"fm-bold\">page-class-selected=\"btn-primary\"<\/b> \n     <b class=\"fm-bold\">page-url-category=\"@Model.CurrentCategory!\"<\/b>\n     class=\"btn-group pull-right m-1\"&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">I used the null-forgiving operator in the <code class=\"fm-code-in-text\">page-url-category<\/code> expression so that I can pass a <code class=\"fm-code-in-text\">null<\/code> value without receiving a compiler warning.<\/p>\n<p class=\"body\">Prior to this change, the links generated for the pagination links looked like this:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost:5000\/Page1<\/pre>\n<p class=\"body\">If the user clicked a page link like this, the category filter would be lost, and the application would present a page containing products from all categories. By adding the current category, taken from the view model, I generate URLs like this instead:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost:5000\/Chess\/Page1<\/pre>\n<p class=\"body\">When the user clicks this kind of link, the current category will be passed to the <code class=\"fm-code-in-text\">Index<\/code> action method, and the filtering will be preserved. To see the effect of this change, start ASP.NET Core and request http:\/\/localhost:5000\/chess, which will display just the products in the <code class=\"fm-code-in-text\">Chess<\/code> category, as shown in figure 8.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre59\" src=\"\/images\/proaspnetcore7\/000054.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.2 Filtering data by category<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-159\">8.1.3 Building a category navigation menu<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1162\"><\/a>I need to provide users with a way to select a category that does not involve typing in URLs. This means presenting a list of the available categories and indicating which, if any, is currently selected.<\/p>\n<p class=\"body\">ASP.NET Core has the concept of <i class=\"fm-italics\">view components<\/i>, which are perfect for creating items such as reusable navigation controls. A view component is a C# class that provides a small amount of reusable application logic with the ability to select and display Razor partial views. I describe view components in detail in chapter 24.<\/p>\n<p class=\"body\">In this case, I will create a view component that renders the navigation menu and integrate it into the application by invoking the component from the shared layout. This approach gives me a regular C# class that can contain whatever application logic I need and that can be unit tested like any other class.<a id=\"calibre_link-1575\"><\/a><\/p>\n<p class=\"fm-head2\">Creating the navigation view component<\/p>\n<p class=\"body\">I created a folder called <code class=\"fm-code-in-text\">Components<\/code>, which is the conventional home of view components, in the SportsStore project and added to it a class file named <code class=\"fm-code-in-text\">NavigationMenuViewComponent.cs<\/code>, which I used to define the class shown in listing 8.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.6 The contents of the NavigationMenuViewComponent.cs file in the SportsStore\/Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace SportsStore.Components {\n\n    public class NavigationMenuViewComponent : ViewComponent {\n        \n        public string Invoke() {\n            return \"Hello from the Nav View Component\";\n        }\n    }\n}<\/pre>\n<p class=\"body\">The view component\u2019s <code class=\"fm-code-in-text\">Invoke<\/code> method is called when the component is used in a Razor view, and the result of the <code class=\"fm-code-in-text\">Invoke<\/code> method is inserted into the HTML sent to the browser. I have started with a simple view component that returns a string, but I\u2019ll replace this with HTML shortly.<\/p>\n<p class=\"body\">I want the category list to appear on all pages, so I am going to use the view component in the shared layout, rather than in a specific view. Within a view, view components are applied using a tag helper, as shown in listing 8.7.<a id=\"calibre_link-1576\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.7 Using a view component in the _Layout.cshtml file in the SportsStore\/ Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;SportsStore&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;    \n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-dark text-white p-2\"&gt;\n        &lt;span class=\"navbar-brand ml-2\"&gt;SPORTS STORE&lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"row m-1 p-1\"&gt;\n        &lt;div id=\"categories\" class=\"col-3\"&gt;\n            <b class=\"fm-bold\">&lt;vc:navigation-menu \/&gt;<\/b>\n        &lt;\/div&gt;\n        &lt;div class=\"col-9\"&gt;\n            @RenderBody()\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">I removed the placeholder text and replaced it with the <code class=\"fm-code-in-text\">vc:navigation-menu<\/code> element, which inserts the view component. The element omits the <code class=\"fm-code-in-text\">ViewComponent<\/code> part of the class name and hyphenates it, such that <code class=\"fm-code-in-text\">vc:navigation-menu<\/code> specifies the <code class=\"fm-code-in-text\">NavigationMenuViewComponent<\/code> class.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see that the output from the <code class=\"fm-code-in-text\">Invoke<\/code> method is included in the HTML sent to the browser, as shown in figure 8.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre60\" src=\"\/images\/proaspnetcore7\/000055.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.3 Using a view component<\/p>\n<\/div>\n<p class=\"fm-head2\">Generating category lists<\/p>\n<p class=\"body\"><a id=\"calibre_link-1577\"><\/a>I can now return to the navigation view component and generate a real set of categories. I could build the HTML for the categories programmatically, as I did for the page tag helper, but one of the benefits of working with view components is they can render Razor partial views. That means I can use the view component to generate the list of categories and then use the more expressive Razor syntax to render the HTML that will display them. The first step is to update the view component, as shown in listing 8.8.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.8 Adding categories in the NavigationMenuViewComponent.cs file in the SportsStore\/Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n<b class=\"fm-bold\">using SportsStore.Models;<\/b>\n\nnamespace SportsStore.Components {\n\n    public class NavigationMenuViewComponent : ViewComponent {\n        <b class=\"fm-bold\">private IStoreRepository  repository;<\/b>\n                \n        <b class=\"fm-bold\">public NavigationMenuViewComponent(IStoreRepository repo) {<\/b>\n            <b class=\"fm-bold\">repository = repo;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">public IViewComponentResult Invoke() {<\/b>\n            <b class=\"fm-bold\">return View(repository.Products<\/b>\n                <b class=\"fm-bold\">.Select(x =&gt; x.Category)<\/b>\n                <b class=\"fm-bold\">.Distinct()<\/b>\n                <b class=\"fm-bold\">.OrderBy(x =&gt; x));<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The constructor defined in listing 8.8 defines an <code class=\"fm-code-in-text\">IStoreRepository<\/code> parameter. When ASP.NET Core needs to create an instance of the view component class, it will note the need to provide a value for this parameter and inspect the configuration in the <code class=\"fm-code-in-text\">Program.cs<\/code> file to determine which implementation object should be used. This is the same dependency injection feature that I used in the controller in chapter 7, and it has the same effect, which is to allow the view component to access data without knowing which repository implementation will be used, a feature I describe in detail in chapter 14.<\/p>\n<p class=\"body\">In the <code class=\"fm-code-in-text\">Invoke<\/code> method, I use LINQ to select and order the set of categories in the repository and pass them as the argument to the <code class=\"fm-code-in-text\">View<\/code> method, which renders the default Razor partial view, details of which are returned from the method using an <code class=\"fm-code-in-text\">IViewComponentResult<\/code> object, a process I describe in more detail in chapter 24.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit test: generating the category list<\/p>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1578\"><\/a>The unit test for my ability to produce a category list is relatively simple. The goal is to create a list that is sorted in alphabetical order and contains no duplicates, and the simplest way to do this is to supply some test data that <i class=\"fm-italics\">does<\/i> have duplicate categories and that is <i class=\"fm-italics\">not<\/i> in order, pass this to the view component class, and assert that the data has been properly cleaned up. Here is the unit test, which I defined in a new class file called <code class=\"fm-code-in-text1\">NavigationMenuViewComponentTests.cs<\/code> in the <code class=\"fm-code-in-text1\">SportsStore.Tests<\/code> project:<\/p>\n<pre class=\"programlisting\">using System.Collections.Generic;\nusing System.Linq;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Mvc.ViewComponents;\nusing Moq;\nusing SportsStore.Components;\nusing SportsStore.Models;\nusing Xunit;\n\nnamespace SportsStore.Tests {\n\n    public class NavigationMenuViewComponentTests {\n        \n        [Fact]\n        public void Can_Select_Categories() {\n            \/\/ Arrange\n            Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n            mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n                new Product {ProductID = 1, Name = \"P1\", \n                    Category = \"Apples\"},\n                new Product {ProductID = 2, Name = \"P2\", \n                    Category = \"Apples\"},\n                new Product {ProductID = 3, Name = \"P3\", \n                    Category = \"Plums\"},\n                new Product {ProductID = 4, Name = \"P4\", \n                    Category = \"Oranges\"},\n            }).AsQueryable&lt;Product&gt;());\n                        \n            NavigationMenuViewComponent target =\n                new NavigationMenuViewComponent(mock.Object);\n                                \n            \/\/ Act = get the set of categories\n            string[] results = ((IEnumerable&lt;string&gt;?)(target.Invoke() \n               as ViewViewComponentResult)?.ViewData?.Model \n                 ?? Enumerable.Empty&lt;string&gt;()).ToArray();\n                                 \n            \/\/ Assert\n            Assert.True(Enumerable.SequenceEqual(new string[] { \"Apples\",\n                \"Oranges\", \"Plums\" }, results));\n        }\n    }\n}<\/pre>\n<p class=\"fm-sidebar-text\">I created a mock repository implementation that contains repeating categories and categories that are not in order. I assert that the duplicates are removed and that alphabetical ordering is imposed.<\/p>\n<\/div>\n<p class=\"fm-head2\">Creating the view<\/p>\n<p class=\"body\"><a id=\"calibre_link-1579\"><\/a>Razor uses different conventions for locating views that are selected by view components. Both the default name of the view and the locations that are searched for the view are different from those used for controllers. To that end, I created the <code class=\"fm-code-in-text\">Views\/Shared\/Components\/NavigationMenu<\/code> folder in the SportsStore project and added to it a Razor View named <code class=\"fm-code-in-text\">Default.cshtml<\/code>, to which I added the content shown in listing 8.9.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.9 The contents of the Default.cshtml file in the SportsStore\/Views\/ Shared\/Components\/NavigationMenu folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;string&gt;\n\n&lt;div class=\"d-grid gap-2\"&gt;\n    &lt;a class=\"btn btn-outline-secondary\"asp-action=\"Index\"\n       asp-controller=\"Home\" asp-route-category=\"\"&gt;\n        Home\n    &lt;\/a&gt;\n    @foreach (string category in Model ?? Enumerable.Empty&lt;string&gt;()) {\n        &lt;a class=\"btn btn-outline-secondary\"\n           asp-action=\"Index\" asp-controller=\"Home\"\n           asp-route-category=\"@category\"\n           asp-route-productPage=\"1\"&gt;\n            @category\n        &lt;\/a&gt;\n    }\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">This view uses one of the built-in tag helpers, which I describe in chapters 25&ndash;27, to create anchor elements whose <code class=\"fm-code-in-text\">href<\/code> attribute contains a URL that selects a different product category.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000 to see the category navigation buttons. If you click a button, the list of items is updated to show only items from the selected category, as shown in figure 8.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre61\" src=\"\/images\/proaspnetcore7\/000056.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.4 Generating category links with a view component<\/p>\n<\/div>\n<p class=\"fm-head2\">Highlighting the current category<\/p>\n<p class=\"body\"><a id=\"calibre_link-1580\"><\/a>There is no feedback to the user to indicate which category has been selected. It might be possible to infer the category from the items in the list, but some clear visual feedback seems like a good idea. ASP.NET Core components such as controllers and view components can receive information about the current request by asking for a context object. Most of the time, you can rely on the base classes that you use to create components to take care of getting the context object for you, such as when you use the <code class=\"fm-code-in-text\">Controller<\/code> base class to create controllers.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ViewComponent<\/code> base class is no exception and provides access to context objects through a set of properties. One of the properties is called <code class=\"fm-code-in-text\">RouteData<\/code>, which provides information about how the request URL was handled by the routing system.<\/p>\n<p class=\"body\">In listing 8.10, I use the <code class=\"fm-code-in-text\">RouteData<\/code> property to access the request data to get the value for the currently selected category. I could pass the category to the view by creating another view model class (and that\u2019s what I would do in a real project), but for variety, I am going to use the view bag feature, which allows unstructured data to be passed to a view alongside the view model object. I describe how this feature works in detail in chapter 22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.10 Passing the selected category in the NavigationMenuViewComponent.cs file in the SportsStore\/Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing SportsStore.Models;\n\nnamespace SportsStore.Components {\n\n    public class NavigationMenuViewComponent : ViewComponent {\n        private IStoreRepository repository;\n                \n        public NavigationMenuViewComponent(IStoreRepository repo) {\n            repository = repo;\n        }\n                \n        public IViewComponentResult Invoke() {\n            <b class=\"fm-bold\">ViewBag.SelectedCategory = RouteData?.Values[\"category\"];<\/b>\n            return View(repository.Products\n                .Select(x =&gt; x.Category)\n                .Distinct()\n                .OrderBy(x =&gt; x));\n        }\n    }\n}<\/pre>\n<p class=\"body\">Inside the <code class=\"fm-code-in-text\">Invoke<\/code> method, I have dynamically assigned a <code class=\"fm-code-in-text\">SelectedCategory<\/code> property to the <code class=\"fm-code-in-text\">ViewBag<\/code> object and set its value to be the current category, which is obtained through the context object returned by the <code class=\"fm-code-in-text\">RouteData<\/code> property. The <code class=\"fm-code-in-text\">ViewBag<\/code> is a dynamic object that allows me to define new properties simply by assigning values to them.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit test: reporting the selected category<\/p>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1581\"><\/a>I can test that the view component correctly adds details of the selected category by reading the value of the <code class=\"fm-code-in-text1\">ViewBag<\/code> property in a unit test, which is available through the <code class=\"fm-code-in-text1\">ViewViewComponentResult<\/code> class. Here is the test, which I added to the <code class=\"fm-code-in-text1\">NavigationMenuViewComponentTests<\/code> class:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Indicates_Selected_Category() {\n\n    \/\/ Arrange\n    string categoryToSelect = \"Apples\";\n    Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n    mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product {ProductID = 1, Name = \"P1\", Category = \"Apples\"},\n        new Product {ProductID = 4, Name = \"P2\", Category = \"Oranges\"},\n    }).AsQueryable&lt;Product&gt;());\n        \n    NavigationMenuViewComponent target =\n        new NavigationMenuViewComponent(mock.Object);\n    target.ViewComponentContext = new ViewComponentContext {\n        ViewContext = new ViewContext {\n            RouteData = new Microsoft.AspNetCore.Routing.RouteData()\n        }\n    };\n    target.RouteData.Values[\"category\"] = categoryToSelect;\n        \n    \/\/ Action\n    string? result = (string?)(target.Invoke() \n        as ViewViewComponentResult)?.ViewData?[\"SelectedCategory\"];\n                \n    \/\/ Assert\n    Assert.Equal(categoryToSelect, result);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">This unit test provides the view component with routing data through the <code class=\"fm-code-in-text1\">ViewComponentContext<\/code> property, which is how view components receive all their context data. The <code class=\"fm-code-in-text1\">ViewComponentContext<\/code> property provides access to view-specific context data through its <code class=\"fm-code-in-text1\">ViewContext<\/code> property, which in turn provides access to the routing information through its <code class=\"fm-code-in-text1\">RouteData<\/code> property. Most of the code in the unit test goes into creating the context objects that will provide the selected category in the same way that it would be presented when the application is running and the context data is provided by ASP.NET Core MVC.<\/p>\n<\/div>\n<p class=\"body\">Now that I am providing information about which category is selected, I can update the view selected by the view component and vary the CSS classes used to style the links so that the one representing the current category is distinct. Listing 8.11 shows the change I made to the <code class=\"fm-code-in-text\">Default.cshtml<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.11 Highlighting in the Default.cshtml file in the SportsStore\/Views\/Shared\/Components\/NavigationMenu folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;string&gt;\n\n&lt;div class=\"d-grid gap-2\"&gt;\n    &lt;a class=\"btn btn-outline-secondary\"asp-action=\"Index\"\n       asp-controller=\"Home\" asp-route-category=\"\"&gt;\n        Home\n    &lt;\/a&gt;\n    @foreach (string category in Model ?? Enumerable.Empty&lt;string&gt;()) {\n        <b class=\"fm-bold\">&lt;a class=\"btn @(category == ViewBag.SelectedCategory<\/b> \n                <b class=\"fm-bold\">? \"btn-primary\": \"btn-outline-secondary\")\"<\/b>\n           asp-action=\"Index\" asp-controller=\"Home\"\n           asp-route-category=\"@category\"\n           asp-route-productPage=\"1\"&gt;\n            @category\n        &lt;\/a&gt;\n    }\n&lt;\/div&gt;<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1163\"><\/a>I have used a Razor expression within the <code class=\"fm-code-in-text\">class<\/code> attribute to apply the <code class=\"fm-code-in-text\">btn-primary<\/code> class to the element that represents the selected category and the <code class=\"fm-code-in-text\">btn-secondary<\/code> class otherwise. These classes apply different Bootstrap styles and make the active button obvious, which you can see by restarting ASP.NET Core, requesting http:\/\/localhost:5000, and clicking one of the category buttons, as shown in figure 8.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre62\" src=\"\/images\/proaspnetcore7\/000057.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.5 Highlighting the selected category<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-160\">8.1.4 Correcting the page count<\/h3>\n<p class=\"body\">I need to correct the page links so that they work correctly when a category is selected. Currently, the number of page links is determined by the total number of products in the repository and not the number of products in the selected category. This means that the customer can click the link for page 2 of the <code class=\"fm-code-in-text\">Chess<\/code> category and end up with an empty page because there are not enough chess products to fill two pages. You can see the problem in figure 8.6.<a id=\"calibre_link-1582\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre63\" src=\"\/images\/proaspnetcore7\/000058.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.6 Displaying the wrong page links when a category is selected<\/p>\n<\/div>\n<p class=\"body\"><a id=\"calibre_link-1583\"><\/a>I can fix this by updating the <code class=\"fm-code-in-text\">Index<\/code> action method in the <code class=\"fm-code-in-text\">Home<\/code> controller so that the pagination information takes the categories into account, as shown in listing 8.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.12 Creating Category Pagination Data in the HomeController.cs File in the SportsStore\/Controllers Folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing SportsStore.Models;\nusing SportsStore.Models.ViewModels;\n\nnamespace SportsStore.Controllers {\n    public class HomeController : Controller {\n        private IStoreRepository repository;\n        public int PageSize = 4;\n                \n        public HomeController(IStoreRepository repo) {\n            repository = repo;\n        }\n                \n        public ViewResult Index(string? category, int productPage = 1)\n           =&gt; View(new ProductsListViewModel {\n               Products = repository.Products\n                    .Where(p =&gt; category == null \n                       || p.Category == category)\n                   .OrderBy(p =&gt; p.ProductID)\n                   .Skip((productPage - 1) * PageSize)\n                   .Take(PageSize),\n               PagingInfo = new PagingInfo {\n                   CurrentPage = productPage,\n                   ItemsPerPage = PageSize,\n                   <b class=\"fm-bold\">TotalItems = category == null<\/b> \n                        <b class=\"fm-bold\">? repository.Products.Count()<\/b> \n                        <b class=\"fm-bold\">: repository.Products.Where(e =&gt;<\/b> \n                            <b class=\"fm-bold\">e.Category == category).Count()<\/b>\n               },\n               CurrentCategory = category\n           });\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1584\"><\/a>If a category has been selected, I return the number of items in that category; if not, I return the total number of products. Restart ASP.NET Core and request http:\/\/localhost:5000 to see the changes when a category is selected, as shown in figure 8.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre64\" src=\"\/images\/proaspnetcore7\/000059.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.7 Displaying category-specific page counts<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit test: category-specific product counts<\/p>\n<p class=\"fm-sidebar-text\">Testing that I am able to generate the current product count for different categories is simple. I create a mock repository that contains known data in a range of categories and then call the <code class=\"fm-code-in-text1\">Index<\/code> action method requesting each category in turn. Here is the unit test method that I added to the <code class=\"fm-code-in-text1\">HomeControllerTests<\/code> class (you will need to import the <code class=\"fm-code-in-text1\">System<\/code> namespace for this test):<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Generate_Category_Specific_Product_Count() {\n    \/\/ Arrange\n    Mock&lt;IStoreRepository&gt; mock = new Mock&lt;IStoreRepository&gt;();\n    mock.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product {ProductID = 1, Name = \"P1\", Category = \"Cat1\"},\n        new Product {ProductID = 2, Name = \"P2\", Category = \"Cat2\"},\n        new Product {ProductID = 3, Name = \"P3\", Category = \"Cat1\"},\n        new Product {ProductID = 4, Name = \"P4\", Category = \"Cat2\"},\n        new Product {ProductID = 5, Name = \"P5\", Category = \"Cat3\"}\n    }).AsQueryable&lt;Product&gt;());\n        \n    HomeController target = new HomeController(mock.Object);\n    target.PageSize = 3;\n        \n    Func&lt;ViewResult, ProductsListViewModel?&gt; GetModel = result \n        =&gt; result?.ViewData?.Model as ProductsListViewModel;\n                \n    \/\/ Action\n    int? res1 = GetModel(target.Index(\"Cat1\"))?.PagingInfo.TotalItems;\n    int? res2 = GetModel(target.Index(\"Cat2\"))?.PagingInfo.TotalItems;\n    int? res3 = GetModel(target.Index(\"Cat3\"))?.PagingInfo.TotalItems;\n    int? resAll = GetModel(target.Index(null))?.PagingInfo.TotalItems;\n        \n    \/\/ Assert\n    Assert.Equal(2, res1);\n    Assert.Equal(2, res2);\n    Assert.Equal(1, res3);\n    Assert.Equal(5, resAll);\n} \n...<\/pre>\n<p class=\"fm-sidebar-text\">Notice that I also call the <code class=\"fm-code-in-text1\">Index<\/code> method, specifying no category, to make sure I get the correct total count as well.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-161\">8.2 Building the shopping cart<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1154\"><\/a>The application is progressing nicely, but I cannot sell any products until I implement a shopping cart. In this section, I will create the shopping cart experience shown in figure 8.8. This will be familiar to anyone who has ever made a purchase online.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre65\" src=\"\/images\/proaspnetcore7\/000060.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.8 The basic shopping cart flow<\/p>\n<\/div>\n<p class=\"body\">An Add To Cart button will be displayed alongside each of the products in the catalog. Clicking this button will show a summary of the products the customer has selected so far, including the total cost. At this point, the user can click the Continue Shopping button to return to the product catalog or click the Checkout Now button to complete the order and finish the shopping session.<a id=\"calibre_link-1585\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-162\">8.2.1 Configuring Razor Pages<\/h3>\n<p class=\"body\">So far, I have used the MVC Framework to define the SportsStore project features. For variety, I am going to use Razor Pages&mdash;another application framework supported by ASP.NET Core&mdash;to implement the shopping cart. Listing 8.13 configures the <code class=\"fm-code-in-text\">Program.cs<\/code> file to enable Razor Pages in the SportsStore application.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.13 Enabling Razor Pages in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\n\n<b class=\"fm-bold\">builder.Services.AddRazorPages();<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\napp.MapControllerRoute(\"catpage\",\n    \"{category}\/Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\" });\n        \napp.MapControllerRoute(\"page\", \"Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"category\", \"{category}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"pagination\",\n    \"Products\/Page{productPage}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapDefaultControllerRoute();\n<b class=\"fm-bold\">app.MapRazorPages();<\/b>\n\nSeedData.EnsurePopulated(app);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddRazorPages<\/code> method sets up the services used by Razor Pages, and the <code class=\"fm-code-in-text\">MapRazorPages<\/code> method registers Razor Pages as endpoints that the URL routing system can use to handle requests.<a id=\"calibre_link-1586\"><\/a><\/p>\n<p class=\"body\">Add a folder named <code class=\"fm-code-in-text\">Pages<\/code>, which is the conventional location for Razor Pages, to the <code class=\"fm-code-in-text\">SportsStore<\/code> project. Add a Razor View Imports file named <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 8.14. These expressions set the namespace that the Razor Pages will belong to and allow the SportsStore classes to be used in Razor Pages without needing to specify their namespace.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.14 The _ViewImports.cshtml file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">@namespace SportsStore.Pages\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using SportsStore.Models\n@using SportsStore.Infrastructure\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers<\/pre>\n<p class=\"body\">Next, add a Razor View Start file named <code class=\"fm-code-in-text\">_ViewStart.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder, with the content shown in listing 8.15. Razor Pages have their own configuration files, and this one specifies that the Razor Pages in the SportsStore project will use a layout file named <code class=\"fm-code-in-text\">_CartLayout<\/code> by default.<a id=\"calibre_link-1587\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.15 The contents of the _ViewStart.cshtml file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = \"_CartLayout\";\n}<\/pre>\n<p class=\"body\">Finally, to provide the layout the Razor Pages will use, add a Razor View named <code class=\"fm-code-in-text\">_CartLayout.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 8.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.16 The contents of the _CartLayout.cshtml file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;SportsStore&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;    \n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-dark text-white p-2\"&gt;\n        &lt;span class=\"navbar-brand ml-2\"&gt;SPORTS STORE&lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"m-1 p-1\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-163\">8.2.2 Creating a Razor Page<\/h3>\n<p class=\"body\">If you are using Visual Studio, use the Razor Page template item and set the item name to <code class=\"fm-code-in-text\">Cart.cshtml<\/code>. This will create a <code class=\"fm-code-in-text\">Cart.cshtml<\/code> file and a <code class=\"fm-code-in-text\">Cart.cshtml.cs<\/code> class file. Replace the contents of the file with those shown in listing 8.17. If you are using Visual Studio Code, just create a <code class=\"fm-code-in-text\">Cart.cshtml<\/code> file with the content shown in listing 8.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.17 The contents of the Cart.cshtml file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">@page\n\n&lt;h4&gt;This is the Cart Page&lt;\/h4&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/cart to see the placeholder content from listing 8.17, which is shown in figure 8.9. Notice that I have not had to register the page and that the mapping between the <code class=\"fm-code-in-text\">\/cart<\/code> URL path and the Razor Page has been handled automatically.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre66\" src=\"\/images\/proaspnetcore7\/000061.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.9 Placeholder content from a Razor Page<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-164\">8.2.3 Creating the Add to Cart buttons<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1588\"><\/a>I have some preparation to do before I can implement the cart feature. First, I need to create the buttons that will add products to the cart. To prepare for this, I added a class file called <code class=\"fm-code-in-text\">UrlExtensions.cs<\/code> to the <code class=\"fm-code-in-text\">Infrastructure<\/code> folder and defined the extension method shown in listing 8.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.18 The UrlExtensions.cs file in the SportsStore\/Infrastructure folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Infrastructure {\n\n    public static class UrlExtensions {\n        \n        public static string PathAndQuery(this HttpRequest request) =&gt;\n            request.QueryString.HasValue \n                ? $\"{request.Path}{request.QueryString}\" \n                : request.Path.ToString();\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">PathAndQuery<\/code> extension method operates on the <code class=\"fm-code-in-text\">HttpRequest<\/code> class, which ASP.NET Core uses to describe an HTTP request. The extension method generates a URL that the browser will be returned to after the cart has been updated, taking into account the query string, if there is one. In listing 8.19, I have added the namespace that contains the extension method to the view imports file so that I can use it in the partial view.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> This is the view imports file in the <code class=\"fm-code-in-text1\">Views<\/code> folder and not the one added to the <code class=\"fm-code-in-text1\">Pages<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.19 Adding a namespace in the _ViewImports.cshtml file in the SportsStore\/Views folder<\/p>\n<pre class=\"programlisting\">@using SportsStore.Models\n@using SportsStore.Models.ViewModels\n<b class=\"fm-bold\">@using SportsStore.Infrastructure<\/b>\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n@addTagHelper *, SportsStore<\/pre>\n<p class=\"body\">In listing 8.20, I have updated the partial view that describes each product so that it contains an Add To Cart button.<a id=\"calibre_link-1589\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.20 Adding the Buttons to the ProductSummary.cshtml File in the SportsStore\/Views\/Shared Folder<\/p>\n<pre class=\"programlisting\">@model Product\n\n&lt;div class=\"card card-outline-primary m-1 p-1\"&gt;\n    &lt;div class=\"bg-faded p-1\"&gt;\n        &lt;h4&gt;\n            @Model.Name\n            &lt;span class=\"badge rounded-pill bg-primary text-white\"\n                  style=\"float:right\"&gt;\n                &lt;small&gt;@Model.Price.ToString(\"c\")&lt;\/small&gt;\n            &lt;\/span&gt;\n        &lt;\/h4&gt;\n    &lt;\/div&gt;\n        \n    <b class=\"fm-bold\">&lt;form id=\"@Model.ProductID\" asp-page=\"\/Cart\" method=\"post\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;input type=\"hidden\" asp-for=\"ProductID\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;input type=\"hidden\" name=\"returnUrl\"<\/b>\n               <b class=\"fm-bold\">value=\"@ViewContext.HttpContext.Request.PathAndQuery()\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;span class=\"card-text p-1\"&gt;<\/b>\n            <b class=\"fm-bold\">@Model.Description<\/b>\n            <b class=\"fm-bold\">&lt;button type=\"submit\" style=\"float:right\"<\/b>\n                    <b class=\"fm-bold\">class=\"btn btn-success btn-sm pull-right\" &gt;<\/b>\n                <b class=\"fm-bold\">Add To Cart<\/b>\n            <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/span&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/form&gt;<\/b>\n        \n&lt;\/div&gt;<\/pre>\n<p class=\"body\">I have added a <code class=\"fm-code-in-text\">form<\/code> element that contains hidden <code class=\"fm-code-in-text\">input<\/code> elements specifying the <code class=\"fm-code-in-text\">ProductID<\/code> value from the view model and the URL that the browser should be returned to after the cart has been updated. The <code class=\"fm-code-in-text\">form<\/code> element and one of the <code class=\"fm-code-in-text\">input<\/code> elements are configured using built-in tag helpers, which are a useful way of generating forms that contain model values and that target controllers or Razor Pages, as described in chapter 27. The other <code class=\"fm-code-in-text\">input<\/code> element uses the extension method I created to set the return URL. I also added a <code class=\"fm-code-in-text\">button<\/code> element that will submit the form to the application.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Notice that I have set the <code class=\"fm-code-in-text1\">method<\/code> attribute on the form element to <code class=\"fm-code-in-text1\">post<\/code>, which instructs the browser to submit the form data using an HTTP <code class=\"fm-code-in-text1\">POST<\/code> request. You can change this so that forms use the <code class=\"fm-code-in-text1\">GET<\/code> method, but you should think carefully about doing so. The HTTP specification requires that <code class=\"fm-code-in-text1\">GET<\/code> requests be <i class=\"fm-italics\">idempotent<\/i>, meaning that they must not cause changes, and adding a product to a cart is definitely a change.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-165\">8.2.4 Enabling sessions<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1166\"><\/a>I am going to store details of a user\u2019s cart using <i class=\"fm-italics\">session state<\/i>, which is data associated with a series of requests made by a user. ASP.NET provides a range of different ways to store session state, including storing it in memory, which is the approach that I am going to use. This has the advantage of simplicity, but it means that the session data is lost when the application is stopped or restarted. Enabling sessions requires adding services and middleware in the <code class=\"fm-code-in-text\">Program.cs<\/code> file, as shown in listing 8.21.<a id=\"calibre_link-1590\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.21 Enabling sessions in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\n\nbuilder.Services.AddRazorPages();\n<b class=\"fm-bold\">builder.Services.AddDistributedMemoryCache();<\/b>\n<b class=\"fm-bold\">builder.Services.AddSession();<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n<b class=\"fm-bold\">app.UseSession();<\/b>\n\napp.MapControllerRoute(\"catpage\",\n    \"{category}\/Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\" });\n        \napp.MapControllerRoute(\"page\", \"Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"category\", \"{category}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"pagination\",\n    \"Products\/Page{productPage}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nSeedData.EnsurePopulated(app);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddDistributedMemoryCache<\/code> method call sets up the in-memory data store. The <code class=\"fm-code-in-text\">AddSession<\/code> method registers the services used to access session data, and the <code class=\"fm-code-in-text\">UseSession<\/code> method allows the session system to automatically associate requests with sessions when they arrive from the client.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-166\">8.2.5 Implementing the cart feature<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1591\"><\/a>Now that the preparations are complete, I can implement the cart features. I started by adding a class file called <code class=\"fm-code-in-text\">Cart.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder in the SportsStore project and used it to define the classes shown in listing 8.22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.22 The contents of the Cart.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Models {\n\n    public class Cart {\n        \n        public List&lt;CartLine&gt; Lines { get; set; } = new List&lt;CartLine&gt;();\n                \n        public void AddItem(Product product, int quantity) {\n            CartLine? line = Lines\n                .Where(p =&gt; p.Product.ProductID == product.ProductID)\n                .FirstOrDefault();\n                                \n            if (line == null) {\n                Lines.Add(new CartLine {\n                    Product = product,\n                    Quantity = quantity\n                });\n            } else {\n                line.Quantity += quantity;\n            }\n        }\n                \n        public void RemoveLine(Product product) =&gt;\n            Lines.RemoveAll(l =&gt; l.Product.ProductID \n                == product.ProductID);\n                                \n        public decimal ComputeTotalValue() =&gt;\n            Lines.Sum(e =&gt; e.Product.Price * e.Quantity);\n        public void Clear() =&gt; Lines.Clear();\n    }\n        \n    public class CartLine {\n        public int CartLineID { get; set; }\n        public Product Product { get; set; } = new();\n        public int Quantity { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Cart<\/code> class uses the <code class=\"fm-code-in-text\">CartLine<\/code> class, defined in the same file, to represent a product selected by the customer and the quantity the user wants to buy. I defined methods to add an item to the cart, remove a previously added item from the cart, calculate the total cost of the items in the cart, and reset the cart by removing all the items.<a id=\"calibre_link-1592\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit test: testing the cart<\/p>\n<p class=\"fm-sidebar-text\">The <code class=\"fm-code-in-text1\">Cart<\/code> class is relatively simple, but it has a range of important behaviors that must work properly. A poorly functioning cart would undermine the entire SportsStore application. I have broken down the features and tested them individually. I created a new unit test file called <code class=\"fm-code-in-text1\">CartTests.cs<\/code> in the <code class=\"fm-code-in-text1\">SportsStore.Tests<\/code> project to contain these tests.<\/p>\n<p class=\"fm-sidebar-text\">The first behavior relates to when I add an item to the cart. If this is the first time that a given <code class=\"fm-code-in-text1\">Product<\/code> has been added to the cart, I want a new <code class=\"fm-code-in-text1\">CartLine<\/code> to be added. Here is the test, including the unit test class definition:<\/p>\n<pre class=\"programlisting\">using System.Linq;\nusing SportsStore.Models;\nusing Xunit;\n\nnamespace SportsStore.Tests {\n\n    public class CartTests {\n        \n        [Fact]\n        public void Can_Add_New_Lines() {\n                \n            \/\/ Arrange - create some test products\n            Product p1 = new Product { ProductID = 1, Name = \"P1\" };\n            Product p2 = new Product { ProductID = 2, Name = \"P2\" };\n                        \n            \/\/ Arrange - create a new cart\n            Cart target = new Cart();\n                        \n            \/\/ Act\n            target.AddItem(p1, 1);\n            target.AddItem(p2, 1);\n            CartLine[] results = target.Lines.ToArray();\n                        \n            \/\/ Assert\n            Assert.Equal(2, results.Length);\n            Assert.Equal(p1, results[0].Product);\n            Assert.Equal(p2, results[1].Product);\n        }\n    }\n}<\/pre>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1593\"><\/a>However, if the customer has already added a <code class=\"fm-code-in-text1\">Product<\/code> to the cart, I want to increment the quantity of the corresponding <code class=\"fm-code-in-text1\">CartLine<\/code> and not create a new one. Here is the test:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Add_Quantity_For_Existing_Lines() {\n    \/\/ Arrange - create some test products\n    Product p1 = new Product { ProductID = 1, Name = \"P1\" };\n    Product p2 = new Product { ProductID = 2, Name = \"P2\" };\n        \n    \/\/ Arrange - create a new cart\n    Cart target = new Cart();\n        \n    \/\/ Act\n    target.AddItem(p1, 1);\n    target.AddItem(p2, 1);\n    target.AddItem(p1, 10);\n    CartLine[] results = (target.Lines ?? new())\n        .OrderBy(c =&gt; c.Product.ProductID).ToArray();\n                \n    \/\/ Assert\n    Assert.Equal(2, results.Length);\n    Assert.Equal(11, results[0].Quantity);\n    Assert.Equal(1, results[1].Quantity);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">I also need to check that users can change their mind and remove products from the cart. This feature is implemented by the <code class=\"fm-code-in-text1\">RemoveLine<\/code> method. Here is the test:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Remove_Line() {\n    \/\/ Arrange - create some test products\n    Product p1 = new Product { ProductID = 1, Name = \"P1\" };\n    Product p2 = new Product { ProductID = 2, Name = \"P2\" };\n    Product p3 = new Product { ProductID = 3, Name = \"P3\" };\n        \n    \/\/ Arrange - create a new cart\n    Cart target = new Cart();\n    \/\/ Arrange - add some products to the cart\n    target.AddItem(p1, 1);\n    target.AddItem(p2, 3);\n    target.AddItem(p3, 5);\n    target.AddItem(p2, 1);\n        \n    \/\/ Act\n    target.RemoveLine(p2);\n        \n    \/\/ Assert\n    Assert.Empty(target.Lines.Where(c =&gt; c.Product == p2));\n    Assert.Equal(2, target.Lines.Count());\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1594\"><\/a>The next behavior I want to test is the ability to calculate the total cost of the items in the cart. Here\u2019s the test for this behavior:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Calculate_Cart_Total() {\n    \/\/ Arrange - create some test products\n    Product p1 = new Product { ProductID = 1, Name = \"P1\", Price = 100M };\n    Product p2 = new Product { ProductID = 2, Name = \"P2\", Price = 50M };\n        \n    \/\/ Arrange - create a new cart\n    Cart target = new Cart();\n        \n    \/\/ Act\n    target.AddItem(p1, 1);\n    target.AddItem(p2, 1);\n    target.AddItem(p1, 3);\n    decimal result = target.ComputeTotalValue();\n        \n    \/\/ Assert\n    Assert.Equal(450M, result);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">The final test is simple. I want to ensure that the contents of the cart are properly removed when reset. Here is the test:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Clear_Contents() {\n    \/\/ Arrange - create some test products\n    Product p1 = new Product { ProductID = 1, Name = \"P1\", Price = 100M };\n    Product p2 = new Product { ProductID = 2, Name = \"P2\", Price = 50M };\n        \n    \/\/ Arrange - create a new cart\n    Cart target = new Cart();\n        \n    \/\/ Arrange - add some items\n    target.AddItem(p1, 1);\n    target.AddItem(p2, 1);\n        \n    \/\/ Act - reset the cart\n    target.Clear();\n        \n    \/\/ Assert\n    Assert.Empty(target.Lines);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">Sometimes, as in this case, the code required to test the functionality of a class is longer and more complex than the class itself. Do not let that put you off writing the unit tests. Defects in simple classes can have huge impacts, especially ones that play such an important role as <code class=\"fm-code-in-text1\">Cart<\/code> does in the example application.<\/p>\n<\/div>\n<p class=\"fm-head2\">Defining session state extension methods<\/p>\n<p class=\"body\"><a id=\"calibre_link-1595\"><\/a>The session state feature in ASP.NET Core stores only <code class=\"fm-code-in-text\">int<\/code>, <code class=\"fm-code-in-text\">string<\/code>, and <code class=\"fm-code-in-text\">byte[]<\/code> values. Since I want to store a <code class=\"fm-code-in-text\">Cart<\/code> object, I need to define extension methods to the <code class=\"fm-code-in-text\">ISession<\/code> interface, which provides access to the session state data to serialize <code class=\"fm-code-in-text\">Cart<\/code> objects into JSON and convert them back. I added a class file called <code class=\"fm-code-in-text\">SessionExtensions.cs<\/code> to the <code class=\"fm-code-in-text\">Infrastructure<\/code> folder and defined the extension methods shown in listing 8.23.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.23 The SessionExtensions.cs file in the SportsStore\/Infrastructure folder<\/p>\n<pre class=\"programlisting\">using System.Text.Json;\n\nnamespace SportsStore.Infrastructure {\n\n    public static class SessionExtensions {\n        \n        public static void SetJson(this ISession session, \n                string key, object value) {\n            session.SetString(key, JsonSerializer.Serialize(value));\n        }\n                \n        public static T? GetJson&lt;T&gt;(this ISession session, string key) {\n            var sessionData = session.GetString(key);\n            return sessionData == null\n                ? default(T) : JsonSerializer.Deserialize&lt;T&gt;(sessionData);\n        }\n    }\n}<\/pre>\n<p class=\"body\">These methods serialize objects into the JavaScript Object Notation format, making it easy to store and retrieve <code class=\"fm-code-in-text\">Cart<\/code> objects.<\/p>\n<p class=\"fm-head2\">Completing the Razor Page<\/p>\n<p class=\"body\">The Cart Razor Page will receive the HTTP POST request that the browser sends when the user clicks an Add To Cart button. It will use the request form data to get the <code class=\"fm-code-in-text\">Product<\/code> object from the database and use it to update the user\u2019s cart, which will be stored as session data for use by future requests. Listing 8.24 implements these features.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.24 Handling requests in the Cart.cshtml file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">@page\n<b class=\"fm-bold\">@model CartModel<\/b>\n\n<b class=\"fm-bold\">&lt;h2&gt;Your cart&lt;\/h2&gt;<\/b>\n<b class=\"fm-bold\">&lt;table class=\"table table-bordered table-striped\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;thead&gt;<\/b>\n        <b class=\"fm-bold\">&lt;tr&gt;<\/b>\n            <b class=\"fm-bold\">&lt;th&gt;Quantity&lt;\/th&gt;<\/b>\n            <b class=\"fm-bold\">&lt;th&gt;Item&lt;\/th&gt;<\/b>\n            <b class=\"fm-bold\">&lt;th class=\"text-right\"&gt;Price&lt;\/th&gt;<\/b>\n            <b class=\"fm-bold\">&lt;th class=\"text-right\"&gt;Subtotal&lt;\/th&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/thead&gt;<\/b>\n    <b class=\"fm-bold\">&lt;tbody&gt;<\/b>\n        <b class=\"fm-bold\">@foreach (var line in Model.Cart?.Lines<\/b> \n                    <b class=\"fm-bold\">?? Enumerable.Empty&lt;CartLine&gt;()) {<\/b>\n            <b class=\"fm-bold\">&lt;tr&gt;<\/b>\n                <b class=\"fm-bold\">&lt;td class=\"text-center\"&gt;@line.Quantity&lt;\/td&gt;<\/b>\n                <b class=\"fm-bold\">&lt;td class=\"text-left\"&gt;@line.Product.Name&lt;\/td&gt;<\/b>\n                <b class=\"fm-bold\">&lt;td class=\"text-right\"&gt;<\/b>\n                    <b class=\"fm-bold\">@line.Product.Price.ToString(\"c\")<\/b>\n                <b class=\"fm-bold\">&lt;\/td&gt;<\/b>\n                <b class=\"fm-bold\">&lt;td class=\"text-right\"&gt;<\/b>\n                    <b class=\"fm-bold\">@((line.Quantity * line.Product.Price).ToString(\"c\"))<\/b>\n                <b class=\"fm-bold\">&lt;\/td&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">&lt;\/tbody&gt;<\/b>\n    <b class=\"fm-bold\">&lt;tfoot&gt;<\/b>\n        <b class=\"fm-bold\">&lt;tr&gt;<\/b>\n            <b class=\"fm-bold\">&lt;td colspan=\"3\" class=\"text-right\"&gt;Total:&lt;\/td&gt;<\/b>\n            <b class=\"fm-bold\">&lt;td class=\"text-right\"&gt;<\/b>\n                <b class=\"fm-bold\">@Model.Cart?.ComputeTotalValue().ToString(\"c\")<\/b>\n            <b class=\"fm-bold\">&lt;\/td&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/tfoot&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/table&gt;<\/b>\n\n<b class=\"fm-bold\">&lt;div class=\"text-center\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;a class=\"btn btn-primary\" href=\"@Model.ReturnUrl\"&gt;<\/b>\n        <b class=\"fm-bold\">Continue shopping<\/b>\n    <b class=\"fm-bold\">&lt;\/a&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b><\/pre>\n<p class=\"body\"><a id=\"calibre_link-1596\"><\/a>Razor Pages allow HTML content, Razor expressions, and code to be combined in a single file, as I explain in chapter 23, but if you want to unit test a Razor Page, then you need to use a separate class file. If you are using Visual Studio, there will already be a class file named <code class=\"fm-code-in-text\">Cart.cshtml.cs<\/code> in the <code class=\"fm-code-in-text\">Pages<\/code> folder, which was created by the Razor Page template item. If you are using Visual Studio Code, you will need to create the class file separately. Use the class file, however it has been created, to define the class shown in listing 8.25.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 8.25 The Cart.cshtml.cs file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\nusing SportsStore.Infrastructure;\nusing SportsStore.Models;\n\nnamespace SportsStore.Pages {\n\n    public class CartModel : PageModel {\n        private IStoreRepository repository;\n                \n        public CartModel(IStoreRepository repo) {\n            repository = repo;\n        }\n                \n        public Cart? Cart { get; set; }\n        public string ReturnUrl { get; set; } = \"\/\";\n                \n        public void OnGet(string returnUrl) {\n            ReturnUrl = returnUrl ?? \"\/\";\n            Cart = HttpContext.Session.GetJson&lt;Cart&gt;(\"cart\") \n                ?? new Cart();\n        }\n                \n        public IActionResult OnPost(long productId, string returnUrl) {\n            Product? product = repository.Products\n                .FirstOrDefault(p =&gt; p.ProductID == productId);\n            if (product != null) {\n                Cart = HttpContext.Session.GetJson&lt;Cart&gt;(\"cart\") \n                    ?? new Cart();\n                Cart.AddItem(product, 1);\n                HttpContext.Session.SetJson(\"cart\", Cart);\n            }\n            return RedirectToPage(new { returnUrl = returnUrl });\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1597\"><\/a>The class associated with a Razor Page is known as its <i class=\"fm-italics\">page model class<\/i>, and it defines handler methods that are invoked for different types of HTTP requests, which update state before rendering the view. The page model class in listing 8.25, which is named <code class=\"fm-code-in-text\">CartModel<\/code>, defines an <code class=\"fm-code-in-text\">OnPost<\/code> handler method, which is invoked to handle HTTP POST requests. It does this by retrieving a <code class=\"fm-code-in-text\">Product<\/code> from the database, retrieving the user\u2019s <code class=\"fm-code-in-text\">Cart<\/code> from the session data, and updating its content using the <code class=\"fm-code-in-text\">Product<\/code>. The modified <code class=\"fm-code-in-text\">Cart<\/code> is stored, and the browser is redirected to the same Razor Page, which it will do using a GET request (which prevents reloading the browser from triggering a duplicate POST request).<\/p>\n<p class=\"body\">The GET request is handled by the <code class=\"fm-code-in-text\">OnGet<\/code> handler method, which sets the values of the <code class=\"fm-code-in-text\">ReturnUrl<\/code> and <code class=\"fm-code-in-text\">Cart<\/code> properties, after which the Razor content section of the page is rendered. The expressions in the HTML content are evaluated using the <code class=\"fm-code-in-text\">CartModel<\/code> as the view model object, which means that the values assigned to the <code class=\"fm-code-in-text\">ReturnUrl<\/code> and <code class=\"fm-code-in-text\">Cart<\/code> properties can be accessed within the expressions. The content generated by the Razor Page details the products added to the user\u2019s cart and provides a button to navigate back to the point where the product was added to the cart.<\/p>\n<p class=\"body\">The handler methods use parameter names that match the <code class=\"fm-code-in-text\">input<\/code> elements in the HTML forms produced by the <code class=\"fm-code-in-text\">ProductSummary.cshtml<\/code> view. This allows ASP.NET Core to associate incoming form POST variables with those parameters, meaning I do not need to process the form directly. This is known as <i class=\"fm-italics\">model binding<\/i> and is a powerful tool for simplifying development, as I explain in detail in chapter 28.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding Razor Pages<\/p>\n<p class=\"fm-sidebar-text\">Razor Pages can feel a little odd when you first start using them, especially if you have previous experience with the MVC Framework features provided by ASP.NET Core. But Razor Pages are complementary to the MVC Framework, and I find myself using them alongside controllers and views because they are well-suited to self-contained features that don\u2019t require the complexity of the MVC Framework. I describe Razor Pages in chapter 23 and show their use alongside controllers throughout part 3 and part 4 of this book.<\/p>\n<\/div>\n<p class=\"body\"><a id=\"calibre_link-1598\"><\/a>The result is that the basic functions of the shopping cart are in place. First, products are listed along with a button to add them to the cart, which you can see by restarting ASP.NET Core and requesting http:\/\/localhost:5000, as shown in figure 8.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre67\" src=\"\/images\/proaspnetcore7\/000062.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.10 The Add To Cart buttons<\/p>\n<\/div>\n<p class=\"body\">Second, when the user clicks an Add To Cart button, the appropriate product is added to their cart, and a summary of the cart is displayed, as shown in figure 8.11. Clicking the Continue Shopping button returns the user to the product page they came from.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre68\" src=\"\/images\/proaspnetcore7\/000063.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 8.11 Displaying the contents of the shopping cart<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit testing: razor pages<\/p>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1599\"><\/a>Testing Razor Pages can require a lot of mocking to create the context objects that the page model class requires. To test the behavior of the <code class=\"fm-code-in-text1\">OnGet<\/code> method defined by the <code class=\"fm-code-in-text1\">CartModel<\/code> class, I added a class file named <code class=\"fm-code-in-text1\">CartPageTests.cs<\/code> to the <code class=\"fm-code-in-text1\">SportsStore.Tests<\/code> project and defined this test:<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Http;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\nusing Microsoft.AspNetCore.Routing;\nusing Moq;\nusing SportsStore.Models;\nusing SportsStore.Pages;\nusing System.Linq;\nusing System.Text;\nusing System.Text.Json;\nusing Xunit;\n\nnamespace SportsStore.Tests {\n\n    public class CartPageTests {\n        \n        [Fact]\n        public void Can_Load_Cart() {\n                \n            \/\/ Arrange\n            \/\/ - create a mock repository\n            Product p1 = new Product { ProductID = 1, Name = \"P1\" };\n            Product p2 = new Product { ProductID = 2, Name = \"P2\" };\n            Mock&lt;IStoreRepository&gt; mockRepo \n                = new Mock&lt;IStoreRepository&gt;();\n            mockRepo.Setup(m =&gt; m.Products).Returns((new Product[] {\n                p1, p2\n            }).AsQueryable&lt;Product&gt;());\n                        \n            \/\/ - create a cart \n            Cart testCart = new Cart();\n            testCart.AddItem(p1, 2);\n            testCart.AddItem(p2, 1);\n            \/\/ - create a mock page context and session\n            Mock&lt;ISession&gt; mockSession = new Mock&lt;ISession&gt;();\n            byte[] data = Encoding.UTF8.GetBytes(\n                    JsonSerializer.Serialize(testCart));\n            mockSession.Setup(c =&gt; \n                c.TryGetValue(It.IsAny&lt;string&gt;(), out data!));\n            Mock&lt;HttpContext&gt; mockContext = new Mock&lt;HttpContext&gt;();\n            mockContext.SetupGet(c =&gt; \n                c.Session).Returns(mockSession.Object);\n                                \n            \/\/ Action\n            CartModel cartModel = new CartModel(mockRepo.Object) {\n                PageContext = new PageContext(new ActionContext {\n                    HttpContext = mockContext.Object,\n                    RouteData = new RouteData(),\n                    ActionDescriptor = new PageActionDescriptor()\n                })\n            };\n            cartModel.OnGet(\"myUrl\");\n                        \n            \/\/Assert\n            Assert.Equal(2, cartModel.Cart?.Lines.Count());\n            Assert.Equal(\"myUrl\", cartModel.ReturnUrl);\n        }\n    }\n}<\/pre>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1600\"><\/a>I am not going to describe these unit tests in detail because there is a simpler way to perform these tests, which I explain in the next chapter. The complexity in this test is mocking the <code class=\"fm-code-in-text1\">ISession<\/code> interface so that the page model class can use extension methods to retrieve a JSON representation of a <code class=\"fm-code-in-text1\">Cart<\/code> object. The <code class=\"fm-code-in-text1\">ISession<\/code> interface only stores byte arrays, and getting and deserializing a string is performed by extension methods. Once the mock objects are defined, they can be wrapped in context objects and used to configure an instance of the page model class, which can be subjected to tests.<\/p>\n<p class=\"fm-sidebar-text\">The process of testing the <code class=\"fm-code-in-text1\">OnPost<\/code> method of the page model class means capturing the byte array that is passed to the <code class=\"fm-code-in-text1\">ISession<\/code> interface mock and then deserializing it to ensure that it contains the expected content. Here is the unit test I added to the <code class=\"fm-code-in-text1\">CartPageTests<\/code> class:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Update_Cart() {\n    \/\/ Arrange\n    \/\/ - create a mock repository\n    Mock&lt;IStoreRepository&gt; mockRepo = \n        new Mock&lt;IStoreRepository&gt;();\n    mockRepo.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product { ProductID = 1, Name = \"P1\" }\n    }).AsQueryable&lt;Product&gt;());\n        \n    Cart? testCart = new Cart();\n        \n    Mock&lt;ISession&gt; mockSession = new Mock&lt;ISession&gt;();\n    mockSession.Setup(s =&gt; \n            s.Set(It.IsAny&lt;string&gt;(), It.IsAny&lt;byte[]&gt;()))\n        .Callback&lt;string, byte[]&gt;((key, val) =&gt; {\n            testCart = JsonSerializer.Deserialize&lt;Cart&gt;(\n                Encoding.UTF8.GetString(val));\n        });\n                \n    Mock&lt;HttpContext&gt; mockContext = new Mock&lt;HttpContext&gt;();\n    mockContext.SetupGet(c =&gt; \n        c.Session).Returns(mockSession.Object);\n                \n    \/\/ Action\n    CartModel cartModel = new CartModel(mockRepo.Object) {\n        PageContext = new PageContext(new ActionContext {\n            HttpContext = mockContext.Object,\n            RouteData = new RouteData(),\n            ActionDescriptor = new PageActionDescriptor()\n        })\n    };\n    cartModel.OnPost(1, \"myUrl\");\n        \n    \/\/Assert\n    Assert.Single(testCart.Lines);\n    Assert.Equal(\"P1\", testCart.Lines.First().Product.Name);\n    Assert.Equal(1, testCart.Lines.First().Quantity);\n} \n...<\/pre>\n<p class=\"fm-sidebar-text\">Patience and a little experimentation are required to write effective unit tests, especially when the feature you are testing operates on the context objects that ASP.NET Core provides.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-167\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><a class=\"calibre58\" id=\"calibre_link-1601\"><\/a>The navigation controls include the selected category in the request URL, which is combined with the page number when querying the database.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The View Bag allows data to be passed to views alongside the view model.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Razor Pages are well-suited for simple self-contained features, like displaying the contents of a shopping cart.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Sessions allow data to be associated with a series of related requests.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-168\">\n<div class=\"calibre1\" id=\"calibre_link-1602\">\n<h1 class=\"tochead\" id=\"calibre_link-1603\"><a id=\"calibre_link-1604\"><\/a>9 SportsStore: Completing the cart<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Updating the shopping cart so that it persists itself as session data<\/li>\n<li class=\"co-summary-bullet\">Creating a shopping cart summary widget using a view component<\/li>\n<li class=\"co-summary-bullet\">Receiving and validating user data<\/li>\n<li class=\"co-summary-bullet\">Displaying data validation errors to the user<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-1605\"><\/a>In this chapter, I continue to build the SportsStore example app. In the previous chapter, I added the basic support for a shopping cart, and now I am going to improve on and complete that functionality.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/ManningBooks\/pro-asp.net-core-7\">https:\/\/github.com\/ManningBooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-169\">9.1 Refining the cart model with a service<\/h2>\n<p class=\"body\">I defined a <code class=\"fm-code-in-text\">Cart<\/code> model class in the previous chapter and demonstrated how it can be stored using the session feature, allowing the user to build up a set of products for purchase. The responsibility for managing the persistence of the <code class=\"fm-code-in-text\">Cart<\/code> class fell to the <code class=\"fm-code-in-text\">Cart<\/code> Razor Page, which has to deal with getting and storing <code class=\"fm-code-in-text\">Cart<\/code> objects as session data.<\/p>\n<p class=\"body\">The problem with this approach is that I will have to duplicate the code that obtains and stores <code class=\"fm-code-in-text\">Cart<\/code> objects in any other Razor Page or controller that uses them. In this section, I am going to use the services feature that sits at the heart of ASP.NET Core to simplify the way that <code class=\"fm-code-in-text\">Cart<\/code> objects are managed, freeing individual components from needing to deal with the details directly.<\/p>\n<p class=\"body\"><a id=\"calibre_link-1155\"><\/a>Services are commonly used to hide details of how interfaces are implemented from the components that depend on them. But services can be used to solve lots of other problems as well and can be used to shape and reshape an application, even when you are working with concrete classes such as <code class=\"fm-code-in-text\">Cart<\/code>.<a id=\"calibre_link-1606\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-170\">9.1.1 Creating a storage-aware cart class<\/h3>\n<p class=\"body\">The first step in tidying up the way that the <code class=\"fm-code-in-text\">Cart<\/code> class is used will be to create a subclass that is aware of how to store itself using session state. To prepare, I apply the <code class=\"fm-code-in-text\">virtual<\/code> keyword to the <code class=\"fm-code-in-text\">Cart<\/code> class, as shown in listing 9.1, so that I can override the members.<a id=\"calibre_link-1607\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.1 Applying the keyword in the Cart.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Models {\n\n    public class Cart {\n        \n        public List&lt;CartLine&gt; Lines { get; set; } = new List&lt;CartLine&gt;();\n                \n        <b class=\"fm-bold\">public virtual void AddItem(Product product, int quantity) {<\/b>\n            CartLine? line = Lines\n                .Where(p =&gt; p.Product.ProductID == product.ProductID)\n                .FirstOrDefault();\n                                \n            if (line == null) {\n                Lines.Add(new CartLine {\n                    Product = product,\n                    Quantity = quantity\n                });\n            } else {\n                line.Quantity += quantity;\n            }\n        }\n                \n        <b class=\"fm-bold\">public virtual void RemoveLine(Product product) =&gt;<\/b>\n            Lines.RemoveAll(l =&gt; \n                l.Product.ProductID == product.ProductID);\n                                \n        public decimal ComputeTotalValue() =&gt;\n            Lines.Sum(e =&gt; e.Product.Price * e.Quantity);\n                        \n        <b class=\"fm-bold\">public virtual void Clear() =&gt; Lines.Clear();<\/b>\n    }\n        \n    public class CartLine {\n        public int CartLineID { get; set; }\n        public Product Product { get; set; } = new();\n        public int Quantity { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Next, I added a class file called <code class=\"fm-code-in-text\">SessionCart.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and used it to define the class shown in listing 9.2.<a id=\"calibre_link-1608\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.2 The contents of the SessionCart.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using System.Text.Json.Serialization;\nusing SportsStore.Infrastructure;\n\nnamespace SportsStore.Models {\n\n    public class SessionCart : Cart {\n        \n        public static Cart GetCart(IServiceProvider services) {\n            ISession? session = \n                services.GetRequiredService&lt;IHttpContextAccessor&gt;()\n                    .HttpContext?.Session;            \n            SessionCart cart = session?.GetJson&lt;SessionCart&gt;(\"Cart\") \n                ?? new SessionCart();\n            cart.Session = session;\n            return cart;\n        }\n                \n        [JsonIgnore]\n        public ISession? Session { get; set; }\n                \n        public override void AddItem(Product product, int quantity) {\n            base.AddItem(product, quantity);\n            Session?.SetJson(\"Cart\", this);\n        }\n                \n        public override void RemoveLine(Product product) {\n            base.RemoveLine(product);\n            Session?.SetJson(\"Cart\", this);\n        }\n                \n        public override void Clear() {\n            base.Clear();\n            Session?.Remove(\"Cart\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">SessionCart<\/code> class subclasses the <code class=\"fm-code-in-text\">Cart<\/code> class and overrides the <code class=\"fm-code-in-text\">AddItem<\/code>, <code class=\"fm-code-in-text\">RemoveLine<\/code>, and <code class=\"fm-code-in-text\">Clear<\/code> methods so they call the base implementations and then store the updated state in the session using the extension methods on the <code class=\"fm-code-in-text\">ISession<\/code> interface. The static <code class=\"fm-code-in-text\">GetCart<\/code> method is a factory for creating <code class=\"fm-code-in-text\">SessionCart<\/code> objects and providing them with an <code class=\"fm-code-in-text\">ISession<\/code> object so they can store themselves.<\/p>\n<p class=\"body\">Getting hold of the <code class=\"fm-code-in-text\">ISession<\/code> object is a little complicated. I obtain an instance of the <code class=\"fm-code-in-text\">IHttpContextAccessor<\/code> service, which provides me with access to an <code class=\"fm-code-in-text\">HttpContext<\/code> object that, in turn, provides me with the <code class=\"fm-code-in-text\">ISession<\/code>. This indirect approach is required because the session isn\u2019t provided as a regular service.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-171\">9.1.2 Registering the service<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1609\"><\/a>The next step is to create a service for the <code class=\"fm-code-in-text\">Cart<\/code> class. My goal is to satisfy requests for <code class=\"fm-code-in-text\">Cart<\/code> objects with <code class=\"fm-code-in-text\">SessionCart<\/code> objects that will seamlessly store themselves. You can see how I created the service in listing 9.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.3 Creating the cart service in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\n\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddDistributedMemoryCache();\nbuilder.Services.AddSession();\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;Cart&gt;(sp =&gt; SessionCart.GetCart(sp));<\/b>\n<b class=\"fm-bold\">builder.Services.AddSingleton&lt;IHttpContextAccessor,<\/b> \n    <b class=\"fm-bold\">HttpContextAccessor&gt;();<\/b>\n        \nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.UseSession();\n\napp.MapControllerRoute(\"catpage\",\n    \"{category}\/Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\" });\n        \napp.MapControllerRoute(\"page\", \"Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"category\", \"{category}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"pagination\",\n    \"Products\/Page{productPage}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nSeedData.EnsurePopulated(app);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddScoped<\/code> method specifies that the same object should be used to satisfy related requests for <code class=\"fm-code-in-text\">Cart<\/code> instances. How requests are related can be configured, but by default, it means that any <code class=\"fm-code-in-text\">Cart<\/code> required by components handling the same HTTP request will receive the same object.<\/p>\n<p class=\"body\"><a id=\"calibre_link-1610\"><\/a>Rather than provide the <code class=\"fm-code-in-text\">AddScoped<\/code> method with a type mapping, as I did for the repository, I have specified a lambda expression that will be invoked to satisfy <code class=\"fm-code-in-text\">Cart<\/code> requests. The expression receives the collection of services that have been registered and passes the collection to the <code class=\"fm-code-in-text\">GetCart<\/code> method of the <code class=\"fm-code-in-text\">SessionCart<\/code> class. The result is that requests for the <code class=\"fm-code-in-text\">Cart<\/code> service will be handled by creating <code class=\"fm-code-in-text\">SessionCart<\/code> objects, which will serialize themselves as session data when they are modified.<\/p>\n<p class=\"body\">I also added a service using the <code class=\"fm-code-in-text\">AddSingleton<\/code> method, which specifies that the same object should always be used. The service I created tells ASP.NET Core to use the <code class=\"fm-code-in-text\">HttpContextAccessor<\/code> class when implementations of the <code class=\"fm-code-in-text\">IHttpContextAccessor<\/code> interface are required. This service is required so I can access the current session in the <code class=\"fm-code-in-text\">SessionCart<\/code> class.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-172\">9.1.3 Simplifying the cart Razor Page<\/h3>\n<p class=\"body\">The benefit of creating this kind of service is that it allows me to simplify the code where <code class=\"fm-code-in-text\">Cart<\/code> objects are used. In listing 9.4, I have reworked the page model class for the <code class=\"fm-code-in-text\">Cart<\/code> Razor Page to take advantage of the new service.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.4 Using the service in the Cart.cshtml.cs file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\nusing SportsStore.Infrastructure;\nusing SportsStore.Models;\n\nnamespace SportsStore.Pages {\n\n    public class CartModel : PageModel {\n        private IStoreRepository repository;\n        <b class=\"fm-bold\">public CartModel(IStoreRepository repo, Cart cartService) {<\/b>\n            repository = repo;\n            <b class=\"fm-bold\">Cart = cartService;<\/b>\n        }\n                \n        <b class=\"fm-bold\">public Cart Cart { get; set; }<\/b>\n        public string ReturnUrl { get; set; } = \"\/\";\n                \n        public void OnGet(string returnUrl) {\n            ReturnUrl = returnUrl ?? \"\/\";\n            <b class=\"fm-bold\">\/\/Cart = HttpContext.Session.GetJson&lt;Cart&gt;(\"cart\")<\/b> \n            <b class=\"fm-bold\">\/\/    ?? new Cart();<\/b>\n        }\n                \n        <b class=\"fm-bold\">public IActionResult OnPost(long productId, string returnUrl) {<\/b>\n            <b class=\"fm-bold\">Product? product = repository.Products<\/b>\n                <b class=\"fm-bold\">.FirstOrDefault(p =&gt; p.ProductID == productId);<\/b>\n            <b class=\"fm-bold\">if (product != null) {<\/b>\n                <b class=\"fm-bold\">Cart.AddItem(product, 1);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return RedirectToPage(new { returnUrl = returnUrl });<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The page model class indicates that it needs a <code class=\"fm-code-in-text\">Cart<\/code> object by declaring a constructor argument, which has allowed me to remove the statements that load and store sessions from the handler methods. The result is a simpler page model class that focuses on its role in the application without having to worry about how <code class=\"fm-code-in-text\">Cart<\/code> objects are created or persisted. And, since services are available throughout the application, any component can get hold of the user\u2019s cart using the same technique.<a id=\"calibre_link-1611\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Updating the unit tests<\/p>\n<p class=\"fm-sidebar-text\">The simplification of the <code class=\"fm-code-in-text1\">CartModel<\/code> class in listing 9.4 requires a corresponding change to the unit tests in the <code class=\"fm-code-in-text1\">CartPageTests.cs<\/code> file in the unit test project so that the <code class=\"fm-code-in-text1\">Cart<\/code> is provided as a constructor argument and not accessed through the context objects. Here is the change to the test for reading the cart:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Load_Cart() {\n\n    \/\/ Arrange\n    \/\/ - create a mock repository\n    Product p1 = new Product { ProductID = 1, Name = \"P1\" };\n    Product p2 = new Product { ProductID = 2, Name = \"P2\" };\n    Mock&lt;IStoreRepository&gt; mockRepo = new Mock&lt;IStoreRepository&gt;();\n    mockRepo.Setup(m =&gt; m.Products).Returns((new Product[] {\n        p1, p2\n    }).AsQueryable&lt;Product&gt;());\n        \n    \/\/ - create a cart \n    Cart testCart = new Cart();\n    testCart.AddItem(p1, 2);\n    testCart.AddItem(p2, 1);\n        \n    \/\/ Action\n    CartModel cartModel = new CartModel(mockRepo.Object, testCart);\n    cartModel.OnGet(\"myUrl\");\n        \n    \/\/Assert\n    Assert.Equal(2, cartModel.Cart.Lines.Count());\n    Assert.Equal(\"myUrl\", cartModel.ReturnUrl);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">I applied the same change to the unit test that checks changes to the cart:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Update_Cart() {\n    \/\/ Arrange\n    \/\/ - create a mock repository\n    Mock&lt;IStoreRepository&gt; mockRepo = new Mock&lt;IStoreRepository&gt;();\n    mockRepo.Setup(m =&gt; m.Products).Returns((new Product[] {\n        new Product { ProductID = 1, Name = \"P1\" }\n    }).AsQueryable&lt;Product&gt;());\n        \n    Cart testCart = new Cart();\n        \n    \/\/ Action\n    CartModel cartModel = new CartModel(mockRepo.Object, testCart);\n    cartModel.OnPost(1, \"myUrl\");\n        \n    \/\/Assert\n    Assert.Single(testCart.Lines);\n    Assert.Equal(\"P1\", testCart.Lines.First().Product.Name);\n    Assert.Equal(1, testCart.Lines.First().Quantity);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">Using services simplifies the testing process and makes it much easier to provide the class being tested with its dependencies.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-173\">9.2 Completing the cart functionality<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1612\"><\/a>Now that I have introduced the <code class=\"fm-code-in-text\">Cart<\/code> service, it is time to complete the cart functionality by adding two new features. The first will allow the customer to remove an item from the cart. The second feature will display a summary of the cart at the top of the page.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-174\">9.2.1 Removing items from the cart<\/h3>\n<p class=\"body\">To remove items from the cart, I need to add a Remove button to the content rendered by the <code class=\"fm-code-in-text\">Cart<\/code> Razor Page that will submit an HTTP POST request. The changes are shown in listing 9.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.5 Removing cart items in the Cart.cshtml file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">@page\n@model CartModel\n\n&lt;h2&gt;Your cart&lt;\/h2&gt;\n&lt;table class=\"table table-bordered table-striped\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;Quantity&lt;\/th&gt;\n            &lt;th&gt;Item&lt;\/th&gt;\n            &lt;th class=\"text-right\"&gt;Price&lt;\/th&gt;\n            &lt;th class=\"text-right\"&gt;Subtotal&lt;\/th&gt;\n            <b class=\"fm-bold\">&lt;th&gt;&lt;\/th&gt;<\/b>\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (var line in Model.Cart?.Lines\n                ?? Enumerable.Empty&lt;CartLine&gt;()) {\n            &lt;tr&gt;\n                &lt;td class=\"text-center\"&gt;@line.Quantity&lt;\/td&gt;\n                &lt;td class=\"text-left\"&gt;@line.Product.Name&lt;\/td&gt;\n                &lt;td class=\"text-right\"&gt;\n                    @line.Product.Price.ToString(\"c\")\n                &lt;\/td&gt;\n                &lt;td class=\"text-right\"&gt;\n                    @((line.Quantity * line.Product.Price).ToString(\"c\"))\n                &lt;\/td&gt;\n                <b class=\"fm-bold\">&lt;td class=\"text-center\"&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;form asp-page-handler=\"Remove\" method=\"post\"&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;input type=\"hidden\" name=\"ProductID\"<\/b>\n                           <b class=\"fm-bold\">value=\"@line.Product.ProductID\" \/&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;input type=\"hidden\" name=\"returnUrl\"<\/b>\n                           <b class=\"fm-bold\">value=\"@Model?.ReturnUrl\" \/&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;button type=\"submit\"<\/b> \n                                <b class=\"fm-bold\">class=\"btn btn-sm btn-danger\"&gt;<\/b>\n                            <b class=\"fm-bold\">Remove<\/b>\n                        <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;\/form&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/td&gt;<\/b>\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n    &lt;tfoot&gt;\n        &lt;tr&gt;\n            &lt;td colspan=\"3\" class=\"text-right\"&gt;Total:&lt;\/td&gt;\n            &lt;td class=\"text-right\"&gt;\n                @Model.Cart?.ComputeTotalValue().ToString(\"c\")\n            &lt;\/td&gt;\n        &lt;\/tr&gt;\n    &lt;\/tfoot&gt;\n&lt;\/table&gt;\n\n&lt;div class=\"text-center\"&gt;\n    &lt;a class=\"btn btn-primary\" href=\"@Model.ReturnUrl\"&gt;\n        Continue shopping\n    &lt;\/a&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">The button requires a new handler method in the page model class that will receive the request and modify the cart, as shown in listing 9.6.<a id=\"calibre_link-1613\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.6 Removing an item in the Cart.cshtml.cs file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\n\nusing SportsStore.Infrastructure;\nusing SportsStore.Models;\n\nnamespace SportsStore.Pages {\n\n    public class CartModel : PageModel {\n        private IStoreRepository repository;\n                \n        public CartModel(IStoreRepository repo, Cart cartService) {\n            repository = repo;\n            Cart = cartService;\n        }\n                \n        public Cart Cart { get; set; }\n        public string ReturnUrl { get; set; } = \"\/\";\n                \n        public void OnGet(string returnUrl) {\n            ReturnUrl = returnUrl ?? \"\/\";\n        }\n                \n        public IActionResult OnPost(long productId, string returnUrl) {\n            Product? product = repository.Products\n                .FirstOrDefault(p =&gt; p.ProductID == productId);\n            if (product != null) {\n                Cart.AddItem(product, 1);\n            }\n            return RedirectToPage(new { returnUrl = returnUrl });\n        }\n                \n        <b class=\"fm-bold\">public IActionResult OnPostRemove(long productId,<\/b>\n                <b class=\"fm-bold\">string returnUrl) {<\/b>\n            <b class=\"fm-bold\">Cart.RemoveLine(Cart.Lines.First(cl =&gt;<\/b>\n                <b class=\"fm-bold\">cl.Product.ProductID == productId).Product);<\/b>\n            <b class=\"fm-bold\">return RedirectToPage(new { returnUrl = returnUrl });<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1614\"><\/a>The new HTML content defines an HTML form. The handler method that will receive the request is specified with the <code class=\"fm-code-in-text\">asp-page-handler<\/code> tag helper attribute, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;form <b class=\"fm-bold\">asp-page-handler=\"Remove\"<\/b> method=\"post\"&gt;\n...<\/pre>\n<p class=\"body\">The specified name is prefixed with <code class=\"fm-code-in-text\">On<\/code> and given a suffix that matches the request type so that a value of <code class=\"fm-code-in-text\">Remove<\/code> selects the <code class=\"fm-code-in-text\">OnPostRemove<\/code> handler method. The handler method uses the value it receives to locate the item in the cart and remove it.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000. Click the Add To Cart buttons to add items to the cart and then click a Remove button. The cart will be updated to remove the item you specified, as shown in figure 9.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre69\" src=\"\/images\/proaspnetcore7\/000064.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 9.1 Removing items from the shopping cart<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-175\">9.2.2 Adding the cart summary widget<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1615\"><\/a>I may have a functioning cart, but there is an issue with the way it is integrated into the interface. Customers can tell what is in their cart only by viewing the cart summary screen. And they can view the cart summary screen only by adding a new item to the cart.<\/p>\n<p class=\"body\">To solve this problem, I am going to add a widget that summarizes the contents of the cart and that can be clicked to display the cart contents throughout the application. I will do this in much the same way that I added the navigation widget&mdash;as a view component whose output I can include in a Razor layout.<\/p>\n<p class=\"fm-head2\">Adding the Font Awesome package<\/p>\n<p class=\"body\">As part of the cart summary, I am going to display a button that allows the user to check out. Rather than display the word <i class=\"fm-italics\">checkout<\/i> in the button, I want to use a cart symbol. Since I have no artistic skills, I am going to use the Font Awesome package, which is an excellent set of open source icons that are integrated into applications as fonts, where each character in the font is a different image. You can learn more about Font Awesome, including inspecting the icons it contains, at <a class=\"url\" href=\"https:\/\/fontawesome.com\">https:\/\/fontawesome.com<\/a>.<\/p>\n<p class=\"body\">To install the client-side package, use a PowerShell command prompt to run the command shown in listing 9.7 in the SportsStore project.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.7 Installing the icon package<\/p>\n<pre class=\"programlisting\">libman install font-awesome@6.2.1 -d wwwroot\/lib\/font-awesome<\/pre>\n<p class=\"fm-head2\">Creating the view component class and view<\/p>\n<p class=\"body\">I added a class file called <code class=\"fm-code-in-text\">CartSummaryViewComponent.cs<\/code> in the <code class=\"fm-code-in-text\">Components<\/code> folder and used it to define the view component shown in listing 9.8.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.8 The CartSummaryViewComponent.cs file in the SportsStore\/ Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing SportsStore.Models;\n\nnamespace SportsStore.Components {\n\n    public class CartSummaryViewComponent : ViewComponent {\n        private Cart cart;\n                \n        public CartSummaryViewComponent(Cart cartService) {\n            cart = cartService;\n        }\n                \n        public IViewComponentResult Invoke() {\n            return View(cart);\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1616\"><\/a>This view component can take advantage of the service that I created earlier in the chapter to receive a <code class=\"fm-code-in-text\">Cart<\/code> object as a constructor argument. The result is a simple view component class that passes on the <code class=\"fm-code-in-text\">Cart<\/code> to the <code class=\"fm-code-in-text\">View<\/code> method to generate the fragment of HTML that will be included in the layout. To create the view for the component, I created the <code class=\"fm-code-in-text\">Views\/Shared\/Components\/CartSummary<\/code> folder and added to it a Razor View named <code class=\"fm-code-in-text\">Default.cshtml<\/code> with the content shown in listing 9.9.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.9 The Default.cshtml file in the Views\/Shared\/Components\/ CartSummary folder<\/p>\n<pre class=\"programlisting\">@model Cart\n\n&lt;div class=\"\"&gt;\n    @if (Model.Lines.Count() &gt; 0) {\n            &lt;small class=\"navbar-text\"&gt;\n                &lt;b&gt;Your cart:&lt;\/b&gt;\n                @Model.Lines.Sum(x =&gt; x.Quantity) item(s)\n                @Model.ComputeTotalValue().ToString(\"c\")\n            &lt;\/small&gt;\n    }\n    &lt;a class=\"btn btn-sm btn-secondary navbar-btn\" asp-page=\"\/Cart\" \n       asp-route-returnurl=\n            \"@ViewContext.HttpContext.Request.PathAndQuery()\"&gt;\n        &lt;i class=\"fa fa-shopping-cart\"&gt;&lt;\/i&gt;\n    &lt;\/a&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">The view displays a button with the Font Awesome cart icon and, if there are items in the cart, provides a snapshot that details the number of items and their total value. Now that I have a view component and a view, I can modify the layout so that the cart summary is included in the responses generated by the <code class=\"fm-code-in-text\">Home<\/code> controller, as shown in listing 9.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.10 Adding the summary in the _Layout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;SportsStore&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;    \n    <b class=\"fm-bold\">&lt;link href=\"\/lib\/font-awesome\/css\/all.min.css\" rel=\"stylesheet\" \/&gt;<\/b>\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-dark text-white p-2\"&gt;\n        <b class=\"fm-bold\">&lt;div class=\"container-fluid\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;div class=\"row\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;div class=\"col navbar-brand\"&gt;SPORTS STORE&lt;\/div&gt;<\/b>\n                <b class=\"fm-bold\">&lt;div class=\"col-6 navbar-text text-end\"&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;vc:cart-summary \/&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    &lt;\/div&gt;\n    &lt;div class=\"row m-1 p-1\"&gt;\n        &lt;div id=\"categories\" class=\"col-3\"&gt;\n            &lt;vc:navigation-menu \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col-9\"&gt;\n            @RenderBody()\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1617\"><\/a>You can see the cart summary by starting the application. When the cart is empty, only the checkout button is shown. If you add items to the cart, then the number of items and their combined cost are shown, as illustrated in figure 9.2. With this addition, customers know what is in their cart and have an obvious way to check out from the store.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre70\" src=\"\/images\/proaspnetcore7\/000065.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 9.2 Displaying a summary of the cart<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-176\">9.3 Submitting orders<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1618\"><\/a>I have now reached the final customer feature in SportsStore: the ability to check out and complete an order. In the following sections, I will extend the data model to provide support for capturing the shipping details from a user and add the application support to process those details.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-177\">9.3.1 <a id=\"calibre_link-1619\"><\/a>Creating the model class<\/h3>\n<p class=\"body\">I added a class file called <code class=\"fm-code-in-text\">Order.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and used it to define the class shown in listing 9.11. This is the class I will use to represent the shipping details for a customer.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.11 The contents of the Order.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\n\nnamespace SportsStore.Models {\n\n    public class Order {\n        \n        [BindNever]\n        public int OrderID { get; set; }\n        [BindNever]\n        public ICollection&lt;CartLine&gt; Lines { get; set; } \n            = new List&lt;CartLine&gt;();\n                        \n        [Required(ErrorMessage = \"Please enter a name\")]\n        public string? Name { get; set; }\n                \n        [Required(ErrorMessage = \"Please enter the first address line\")]\n        public string? Line1 { get; set; }\n        public string? Line2 { get; set; }\n        public string? Line3 { get; set; }\n                \n        [Required(ErrorMessage = \"Please enter a city name\")]\n        public string? City { get; set; }\n                \n        [Required(ErrorMessage = \"Please enter a state name\")]\n        public string? State { get; set; }\n                \n        public string? Zip { get; set; }\n                \n        [Required(ErrorMessage = \"Please enter a country name\")]\n        public string? Country { get; set; }\n                \n        public bool GiftWrap { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">I am using the validation attributes from the <code class=\"fm-code-in-text\">System.ComponentModel.DataAnnotations<\/code> namespace, just as I did in chapter 3. I describe validation further in chapter 29.<\/p>\n<p class=\"body\">I also use the <code class=\"fm-code-in-text\">BindNever<\/code> attribute, which prevents the user from supplying values for these properties in an HTTP request. This is a feature of the model binding system, which I describe in chapter 28, and it stops ASP.NET Core using values from the HTTP request to populate sensitive or important model properties.<a id=\"calibre_link-1156\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-178\">9.3.2 Adding the checkout process<\/h3>\n<p class=\"body\">The goal is to reach the point where users can enter their shipping details and submit an order. To start, I need to add a Checkoutbutton to the cart view, as shown in listing 9.12.<a id=\"calibre_link-1620\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.12 Adding a button in the Cart.cshtml file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"text-center\"&gt;\n    &lt;a class=\"btn btn-primary\" href=\"@Model.ReturnUrl\"&gt;\n        Continue shopping\n    &lt;\/a&gt;\n    <b class=\"fm-bold\">&lt;a class=\"btn btn-primary\" asp-action=\"Checkout\"<\/b> \n            <b class=\"fm-bold\">asp-controller=\"Order\"&gt;<\/b>\n        <b class=\"fm-bold\">Checkout<\/b>\n    <b class=\"fm-bold\">&lt;\/a&gt;<\/b>\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">This change generates a link that I have styled as a button and that, when clicked, calls the <code class=\"fm-code-in-text\">Checkout<\/code> action method of the <code class=\"fm-code-in-text\">Order<\/code> controller, which I create in the following section. To show how Razor Pages and controllers can work together, I am going to handle the order processing in a controller and then return to a Razor Page at the end of the process. To see the Checkout button, restart ASP.NET Core, request http:\/\/localhost:5000, and click one of the Add To Cart buttons. The new button is shown as part of the cart summary, as shown in figure 9.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre71\" src=\"\/images\/proaspnetcore7\/000066.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 9.3 The Checkout button<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-179\">9.3.3 Creating the controller and view<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1621\"><\/a>I now need to define the controller that will deal with the order. I added a class file called <code class=\"fm-code-in-text\">OrderController.cs<\/code> to the <code class=\"fm-code-in-text\">Controllers<\/code> folder and used it to define the class shown in listing 9.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.13 The OrderController.cs file in the SportsStore\/Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing SportsStore.Models;\n\nnamespace SportsStore.Controllers {\n\n    public class OrderController : Controller {\n        \n        public ViewResult Checkout() =&gt; View(new Order());\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Checkout<\/code> method returns the default view and passes a new <code class=\"fm-code-in-text\">Order<\/code> object as the view model. To create the view, I created the <code class=\"fm-code-in-text\">Views\/Order<\/code> folder and added to it a Razor View called <code class=\"fm-code-in-text\">Checkout.cshtml<\/code> with the markup shown in listing 9.14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.14 The Checkout.cshtml file in the SportsStore\/Views\/Order folder<\/p>\n<pre class=\"programlisting\">@model Order\n\n&lt;h2&gt;Check out now&lt;\/h2&gt;\n&lt;p&gt;Please enter your details, and we'll ship your goods right away!&lt;\/p&gt;\n\n&lt;form asp-action=\"Checkout\" method=\"post\"&gt;\n    &lt;h3&gt;Ship to&lt;\/h3&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name:&lt;\/label&gt;\n        &lt;input asp-for=\"Name\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;h3&gt;Address&lt;\/h3&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Line 1:&lt;\/label&gt;\n        &lt;input asp-for=\"Line1\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Line 2:&lt;\/label&gt;\n        &lt;input asp-for=\"Line2\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Line 3:&lt;\/label&gt;\n        &lt;input asp-for=\"Line3\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;City:&lt;\/label&gt;\n        &lt;input asp-for=\"City\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;State:&lt;\/label&gt;\n        &lt;input asp-for=\"State\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Zip:&lt;\/label&gt;\n        &lt;input asp-for=\"Zip\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Country:&lt;\/label&gt;\n        &lt;input asp-for=\"Country\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;h3&gt;Options&lt;\/h3&gt;\n    &lt;div class=\"checkbox\"&gt;\n        &lt;label&gt;\n            &lt;input asp-for=\"GiftWrap\" \/&gt; Gift wrap these items\n        &lt;\/label&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"text-center\"&gt;\n        &lt;input class=\"btn btn-primary\" type=\"submit\" \n            value=\"Complete Order\" \/&gt;\n    &lt;\/div&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">For each of the properties in the model, I have created a <code class=\"fm-code-in-text\">label<\/code> and <code class=\"fm-code-in-text\">input<\/code> elements to capture the user input, styled with Bootstrap, and configured using a tag helper. The <code class=\"fm-code-in-text\">asp-for<\/code> attribute on the <code class=\"fm-code-in-text\">input<\/code> elements is handled by a built-in tag helper that generates the <code class=\"fm-code-in-text\">type<\/code>, <code class=\"fm-code-in-text\">id<\/code>, <code class=\"fm-code-in-text\">name<\/code>, and <code class=\"fm-code-in-text\">value<\/code> attributes based on the specified model property, as described in chapter 27.<\/p>\n<p class=\"body\">You can see the form, shown in figure 9.4, by restarting ASP.NET Core, requesting http:\/\/localhost:5000, adding an item to the basket, and clicking the Checkout button. Or, more directly, you can request http:\/\/localhost:5000\/order\/checkout.<a id=\"calibre_link-1622\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre72\" src=\"\/images\/proaspnetcore7\/000067.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 9.4 The shipping details form<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-180\">9.3.4 Implementing order processing<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1623\"><\/a>I will process orders by writing them to the database. Most e-commerce sites would not simply stop there, of course, and I have not provided support for processing credit cards or other forms of payment. But I want to keep things focused on ASP.NET Core, so a simple database entry will do.<\/p>\n<p class=\"fm-head2\">Extending the database<\/p>\n<p class=\"body\">Adding a new kind of model to the database is simple because of the initial setup I went through in chapter 7. First, I added a new property to the database context class, as shown in listing 9.15.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.15 Adding a property in the StoreDbContext.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n\nnamespace SportsStore.Models {\n    public class StoreDbContext : DbContext {\n        \n        public StoreDbContext(DbContextOptions&lt;StoreDbContext&gt; options)\n            : base(options) { }\n                        \n        public DbSet&lt;Product&gt; Products =&gt; Set&lt;Product&gt;();\n        <b class=\"fm-bold\">public DbSet&lt;Order&gt; Orders =&gt; Set&lt;Order&gt;();<\/b>\n    }\n}<\/pre>\n<p class=\"body\">This change is enough for Entity Framework Core to create a database migration that will allow <code class=\"fm-code-in-text\">Order<\/code> objects to be stored in the database. To create the migration, use a PowerShell command prompt to run the command shown in listing 9.16 in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.16 Creating a migration<\/p>\n<pre class=\"programlisting\">dotnet ef migrations add Orders<\/pre>\n<p class=\"body\">This command tells Entity Framework Core to take a new snapshot of the application data model, work out how it differs from the previous database version, and generate a new migration called <code class=\"fm-code-in-text\">Orders<\/code>. The new migration will be applied automatically when the application starts because the <code class=\"fm-code-in-text\">SeedData<\/code> calls the <code class=\"fm-code-in-text\">Migrate<\/code> method provided by Entity Framework Core.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Resetting the database<\/p>\n<p class=\"fm-sidebar-text\">When you are making frequent changes to the model, there will come a point when your migrations and your database schema get out of sync. The easiest thing to do is delete the database and start over. However, this applies only during development, of course, because you will lose any data you have stored. Run this command to delete the database:<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force --context StoreDbContext<\/pre>\n<p class=\"fm-sidebar-text\">Once the database has been removed, run the following command from the <code class=\"fm-code-in-text1\">SportsStore<\/code> folder to re-create the database and apply the migrations you have created by running the following command:<\/p>\n<pre class=\"programlisting\">dotnet ef database update --context StoreDbContext<\/pre>\n<p class=\"fm-sidebar-text\">The migrations will also be applied by the <code class=\"fm-code-in-text1\">SeedData<\/code> class if you just start the application. Either way, the database will be reset so that it accurately reflects your data model and allows you to return to developing your application.<\/p>\n<\/div>\n<p class=\"fm-head2\">Creating the order repository<\/p>\n<p class=\"body\"><a id=\"calibre_link-1624\"><\/a>I am going to follow the same pattern I used for the product repository to provide access to the <code class=\"fm-code-in-text\">Order<\/code> objects. I added a class file called <code class=\"fm-code-in-text\">IOrderRepository.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and used it to define the interface shown in listing 9.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.17 The IOrderRepository.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Models {\n\n    public interface IOrderRepository {\n        \n        IQueryable&lt;Order&gt; Orders { get; }\n        void SaveOrder(Order order);\n    }\n}<\/pre>\n<p class=\"body\">To implement the order repository interface, I added a class file called <code class=\"fm-code-in-text\">EFOrderRepository.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and defined the class shown in listing 9.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.18 The EFOrderRepository.cs File in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n\nnamespace SportsStore.Models {\n\n    public class EFOrderRepository : IOrderRepository {\n        private StoreDbContext context;\n                \n        public EFOrderRepository(StoreDbContext ctx) {\n            context = ctx;\n        }\n                \n        public IQueryable&lt;Order&gt; Orders =&gt; context.Orders\n                            .Include(o =&gt; o.Lines)\n                            .ThenInclude(l =&gt; l.Product);\n                                                        \n        public void SaveOrder(Order order) {\n            context.AttachRange(order.Lines.Select(l =&gt; l.Product));\n            if (order.OrderID == 0) {\n                context.Orders.Add(order);\n            }\n            context.SaveChanges();\n        }\n    }\n}<\/pre>\n<p class=\"body\">This class implements the <code class=\"fm-code-in-text\">IOrderRepository<\/code> interface using Entity Framework Core, allowing the set of <code class=\"fm-code-in-text\">Order<\/code> objects that have been stored to be retrieved and allowing for orders to be created or changed.<a id=\"calibre_link-1625\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding the order repository<\/p>\n<p class=\"fm-sidebar-text\">Entity Framework Core requires instruction to load related data if it spans multiple tables. In listing 9.18, I used the <code class=\"fm-code-in-text1\">Include<\/code> and <code class=\"fm-code-in-text1\">ThenInclude<\/code> methods to specify that when an <code class=\"fm-code-in-text1\">Order<\/code> object is read from the database, the collection associated with the <code class=\"fm-code-in-text1\">Lines<\/code> property should also be loaded along with each <code class=\"fm-code-in-text1\">Product<\/code> object associated with each collection object.<\/p>\n<pre class=\"programlisting\">...\npublic IQueryable&lt;Order&gt; Orders =&gt; context.Orders\n    .<b class=\"fm-bold\">Include<\/b>(o =&gt; o.Lines)\n    .<b class=\"fm-bold\">ThenInclude<\/b>(l =&gt; l.Product);\n...<\/pre>\n<p class=\"fm-sidebar-text\">This ensures that I receive all the data objects that I need without having to perform separate queries and then assemble the data myself.<\/p>\n<p class=\"fm-sidebar-text\">An additional step is also required when I store an <code class=\"fm-code-in-text1\">Order<\/code> object in the database. When the user\u2019s cart data is de-serialized from the session store, new objects are created that are not known to Entity Framework Core, which then tries to write all the objects into the database. For the <code class=\"fm-code-in-text1\">Product<\/code> objects associated with an <code class=\"fm-code-in-text1\">Order<\/code>, this means that Entity Framework Core tries to write objects that have already been stored, which causes an error. To avoid this problem, I notify Entity Framework Core that the objects exist and shouldn\u2019t be stored in the database unless they are modified, as follows:<\/p>\n<pre class=\"programlisting\">...\ncontext.<b class=\"fm-bold\">AttachRange<\/b>(order.Lines.Select(l =&gt; l.Product));\n...<\/pre>\n<p class=\"fm-sidebar-text\">This ensures that Entity Framework Core won\u2019t try to write the de-serialized <code class=\"fm-code-in-text1\">Product<\/code> objects that are associated with the <code class=\"fm-code-in-text1\">Order<\/code> object.<\/p>\n<\/div>\n<p class=\"body\">In listing 9.19, I have registered the order repository as a service in the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.19 Registering the service in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;IOrderRepository, EFOrderRepository&gt;();<\/b>\n\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddDistributedMemoryCache();\nbuilder.Services.AddSession();\nbuilder.Services.AddScoped&lt;Cart&gt;(sp =&gt; SessionCart.GetCart(sp));\nbuilder.Services.AddSingleton&lt;IHttpContextAccessor, \n    HttpContextAccessor&gt;();\n        \nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.UseSession();\n\napp.MapControllerRoute(\"catpage\",\n    \"{category}\/Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\" });\n        \napp.MapControllerRoute(\"page\", \"Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"category\", \"{category}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"pagination\",\n    \"Products\/Page{productPage}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nSeedData.EnsurePopulated(app);\n\napp.Run();<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-181\">9.3.5 Completing the order controller<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1626\"><\/a>To complete the <code class=\"fm-code-in-text\">OrderController<\/code> class, I need to modify the constructor so that it receives the services it requires to process an order and add an action method that will handle the HTTP form <code class=\"fm-code-in-text\">POST<\/code> request when the user clicks the Complete Order button. Listing 9.20 shows both changes.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.20 Completing the controller in the OrderController.cs file in the SportsStore\/Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing SportsStore.Models;\n\nnamespace SportsStore.Controllers {\n\n    public class OrderController : Controller {\n        <b class=\"fm-bold\">private IOrderRepository repository;<\/b>\n        <b class=\"fm-bold\">private Cart cart;<\/b>\n                \n        <b class=\"fm-bold\">public OrderController(IOrderRepository repoService,<\/b>\n                <b class=\"fm-bold\">Cart cartService) {<\/b>\n            <b class=\"fm-bold\">repository = repoService;<\/b>\n            <b class=\"fm-bold\">cart = cartService;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        public ViewResult Checkout() =&gt; View(new Order());\n                \n        <b class=\"fm-bold\">[HttpPost]<\/b>\n        <b class=\"fm-bold\">public IActionResult Checkout(Order order) {<\/b>\n            <b class=\"fm-bold\">if (cart.Lines.Count() == 0) {<\/b>\n                <b class=\"fm-bold\">ModelState.AddModelError(\"\",<\/b> \n                    <b class=\"fm-bold\">\"Sorry, your cart is empty!\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">if (ModelState.IsValid) {<\/b>\n                <b class=\"fm-bold\">order.Lines = cart.Lines.ToArray();<\/b>\n                <b class=\"fm-bold\">repository.SaveOrder(order);<\/b>\n                <b class=\"fm-bold\">cart.Clear();<\/b>\n                <b class=\"fm-bold\">return RedirectToPage(\"\/Completed\",<\/b> \n                    <b class=\"fm-bold\">new { orderId = order.OrderID });<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return View();<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1627\"><\/a>The <code class=\"fm-code-in-text\">Checkout<\/code> action method is decorated with the <code class=\"fm-code-in-text\">HttpPost<\/code> attribute, which means that it will be used to handle <code class=\"fm-code-in-text\">POST<\/code> requests&mdash;in this case, when the user submits the form.<\/p>\n<p class=\"body\">In chapter 8, I use the ASP.NET Core model binding feature to receive simple data values from the request. This same feature is used in the new action method to receive a completed <code class=\"fm-code-in-text\">Order<\/code> object. When a request is processed, the model binding system tries to find values for the properties defined by the <code class=\"fm-code-in-text\">Order<\/code> class. This works on a best-effort basis, which means I may receive an <code class=\"fm-code-in-text\">Order<\/code> object lacking property values if there is no corresponding data item in the request.<\/p>\n<p class=\"body\">To ensure I have the data I require, I applied validation attributes to the <code class=\"fm-code-in-text\">Order<\/code> class. ASP.NET Core checks the validation constraints that I applied to the <code class=\"fm-code-in-text\">Order<\/code> class and provides details of the result through the <code class=\"fm-code-in-text\">ModelState<\/code> property. I can see whether there are any problems by checking the <code class=\"fm-code-in-text\">ModelState.IsValid<\/code> property. I call the <code class=\"fm-code-in-text\">ModelState.AddModelError<\/code> method to register an error message if there are no items in the cart. I will explain how to display such errors shortly, and I have much more to say about model binding and validation in chapters 28 and 29.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Unit test: order processing<\/p>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1628\"><\/a>To perform unit testing for the <code class=\"fm-code-in-text1\">OrderController<\/code> class, I need to test the behavior of the <code class=\"fm-code-in-text1\">POST<\/code> version of the <code class=\"fm-code-in-text1\">Checkout<\/code> method. Although the method looks short and simple, the use of model binding means that a lot is going on behind the scenes that needs to be tested.<\/p>\n<p class=\"fm-sidebar-text\">I want to process an order only if there are items in the cart <i class=\"fm-italics\">and<\/i> the customer has provided valid shipping details. Under all other circumstances, the customer should be shown an error. Here is the first test method, which I defined in a class file called <code class=\"fm-code-in-text1\">OrderControllerTests.cs<\/code> in the <code class=\"fm-code-in-text1\">SportsStore.Tests<\/code> project:<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Moq;\nusing SportsStore.Controllers;\nusing SportsStore.Models;\nusing Xunit;\n\nnamespace SportsStore.Tests {\n\n    public class OrderControllerTests {\n        \n        [Fact]\n        public void Cannot_Checkout_Empty_Cart() {\n            \/\/ Arrange - create a mock repository\n            Mock&lt;IOrderRepository&gt; mock = new Mock&lt;IOrderRepository&gt;();\n            \/\/ Arrange - create an empty cart\n            Cart cart = new Cart();\n            \/\/ Arrange - create the order\n            Order order = new Order();\n            \/\/ Arrange - create an instance of the controller\n            OrderController target = \n                new OrderController(mock.Object, cart);\n                                \n            \/\/ Act\n            ViewResult? result = target.Checkout(order) as ViewResult;\n                        \n            \/\/ Assert - check that the order hasn't been stored \n            mock.Verify(m =&gt; m.SaveOrder(It.IsAny&lt;Order&gt;()), Times.Never);\n            \/\/ Assert - check that the method is returning the default view\n            Assert.True(string.IsNullOrEmpty(result?.ViewName));\n            \/\/ Assert - check I am passing an invalid model to the view\n            Assert.False(result?.ViewData.ModelState.IsValid);\n        }\n    }\n}<\/pre>\n<p class=\"fm-sidebar-text\">This test ensures that I cannot check out with an empty cart. I check this by ensuring that the <code class=\"fm-code-in-text1\">SaveOrder<\/code> of the mock <code class=\"fm-code-in-text1\">IOrderRepository<\/code> implementation is never called, that the view the method returns is the default view (which will redisplay the data entered by customers and give them a chance to correct it), and that the model state being passed to the view has been marked as invalid. This may seem like a belt-and-braces set of assertions, but I need all three to be sure that I have the right behavior. The next test method works in much the same way but injects an error into the view model to simulate a problem reported by the model binder (which would happen in production when the customer enters invalid shipping data):<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Cannot_Checkout_Invalid_ShippingDetails() {\n\n    \/\/ Arrange - create a mock order repository\n    Mock&lt;IOrderRepository&gt; mock = new Mock&lt;IOrderRepository&gt;();\n    \/\/ Arrange - create a cart with one item\n    Cart cart = new Cart();\n    cart.AddItem(new Product(), 1);\n    \/\/ Arrange - create an instance of the controller\n    OrderController target = new OrderController(mock.Object, cart);\n    \/\/ Arrange - add an error to the model\n    target.ModelState.AddModelError(\"error\", \"error\");\n        \n    \/\/ Act - try to checkout\n    ViewResult? result = target.Checkout(new Order()) as ViewResult;\n        \n    \/\/ Assert - check that the order hasn't been passed stored\n    mock.Verify(m =&gt; m.SaveOrder(It.IsAny&lt;Order&gt;()), Times.Never);\n    \/\/ Assert - check that the method is returning the default view\n    Assert.True(string.IsNullOrEmpty(result?.ViewName));\n    \/\/ Assert - check that I am passing an invalid model to the view\n    Assert.False(result?.ViewData.ModelState.IsValid);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1629\"><\/a>Having established that an empty cart or invalid details will prevent an order from being processed, I need to ensure that I process orders when appropriate. Here is the test:<\/p>\n<pre class=\"programlisting\">...\n[Fact]\npublic void Can_Checkout_And_Submit_Order() {\n    \/\/ Arrange - create a mock order repository\n    Mock&lt;IOrderRepository&gt; mock = new Mock&lt;IOrderRepository&gt;();\n    \/\/ Arrange - create a cart with one item\n    Cart cart = new Cart();\n    cart.AddItem(new Product(), 1);\n    \/\/ Arrange - create an instance of the controller\n    OrderController target = new OrderController(mock.Object, cart);\n        \n    \/\/ Act - try to checkout\n    RedirectToPageResult? result =\n            target.Checkout(new Order()) as RedirectToPageResult;\n                        \n    \/\/ Assert - check that the order has been stored\n    mock.Verify(m =&gt; m.SaveOrder(It.IsAny&lt;Order&gt;()), Times.Once);\n    \/\/ Assert - check the method is redirecting to the Completed action\n    Assert.Equal(\"\/Completed\", result?.PageName);\n} \n...<\/pre>\n<p class=\"fm-sidebar-text\">I did not need to test that I can identify valid shipping details. This is handled for me automatically by the model binder using the attributes applied to the properties of the <code class=\"fm-code-in-text1\">Order<\/code> class.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-182\">9.3.6 Displaying validation errors<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1168\"><\/a>ASP.NET Core uses the validation attributes applied to the <code class=\"fm-code-in-text\">Order<\/code> class to validate user data, but I need to make a simple change to display any problems. This relies on another built-in tag helper that inspects the validation state of the data provided by the user and adds warning messages for each problem that has been discovered. Listing 9.21 shows the addition of an HTML element that will be processed by the tag helper to the <code class=\"fm-code-in-text\">Checkout.cshtml<\/code> file.<a id=\"calibre_link-1630\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.21 Adding a validation summary to the Checkout.cshtml file in the SportsStore\/Views\/Order folder<\/p>\n<pre class=\"programlisting\">@model Order\n\n&lt;h2&gt;Check out now&lt;\/h2&gt;\n&lt;p&gt;Please enter your details, and we'll ship your goods right away!&lt;\/p&gt;\n\n<b class=\"fm-bold\">&lt;div asp-validation-summary=\"All\" class=\"text-danger\"&gt;&lt;\/div&gt;<\/b>\n\n&lt;form asp-action=\"Checkout\" method=\"post\"&gt;\n    &lt;h3&gt;Ship to&lt;\/h3&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name:&lt;\/label&gt;\n        &lt;input asp-for=\"Name\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;h3&gt;Address&lt;\/h3&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Line 1:&lt;\/label&gt;\n        &lt;input asp-for=\"Line1\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Line 2:&lt;\/label&gt;\n        &lt;input asp-for=\"Line2\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Line 3:&lt;\/label&gt;\n        &lt;input asp-for=\"Line3\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;City:&lt;\/label&gt;\n        &lt;input asp-for=\"City\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;State:&lt;\/label&gt;\n        &lt;input asp-for=\"State\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Zip:&lt;\/label&gt;\n        &lt;input asp-for=\"Zip\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Country:&lt;\/label&gt;\n        &lt;input asp-for=\"Country\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;h3&gt;Options&lt;\/h3&gt;\n    &lt;div class=\"checkbox\"&gt;\n        &lt;label&gt;\n            &lt;input asp-for=\"GiftWrap\" \/&gt; Gift wrap these items\n        &lt;\/label&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"text-center\"&gt;\n        &lt;input class=\"btn btn-primary\" type=\"submit\" \n            value=\"Complete Order\" \/&gt;\n    &lt;\/div&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1631\"><\/a>With this simple change, validation errors are reported to the user. To see the effect, restart ASP.NET Core, request http:\/\/localhost:5000\/Order\/Checkout, and click the Complete Order button without filling out the form. ASP.NET Core will process the form data, detect that the required values were not found, and generate the validation errors shown in figure 9.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre73\" src=\"\/images\/proaspnetcore7\/000068.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 9.5 Displaying validation messages<\/p>\n<\/div>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The data submitted by the user is sent to the server before it is validated, which is known as <i class=\"fm-italics\">server-side validation<\/i> and for which ASP.NET Core has excellent support. The problem with server-side validation is that the user isn\u2019t told about errors until after the data has been sent to the server and processed and the result page has been generated&mdash;something that can take a few seconds on a busy server. For this reason, server-side validation is usually complemented by <i class=\"fm-italics\">client-side validation<\/i>, where JavaScript is used to check the values that the user has entered before the form data is sent to the server. I describe client-side validation in chapter 29.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-183\">9.3.7 Displaying a summary page<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1632\"><\/a>To complete the checkout process, I am going to create a Razor Page that displays a thank-you message with a summary of the order. Add a Razor Page named <code class=\"fm-code-in-text\">Completed.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the contents shown in listing 9.22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 9.22 The contents of the Completed.cshtml file in the SportsStore\/Pages folder<\/p>\n<pre class=\"programlisting\">@page\n\n&lt;div class=\"text-center\"&gt;\n    &lt;h2&gt;Thanks!&lt;\/h2&gt;\n    &lt;p&gt;Thanks for placing order #@OrderId&lt;\/p&gt;\n    &lt;p&gt;We'll ship your goods as soon as possible.&lt;\/p&gt;\n    &lt;a class=\"btn btn-primary\" asp-controller=\"Home\"&gt;Return to Store&lt;\/a&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    [BindProperty(SupportsGet = true)]\n    public string? OrderId { get; set; }\n}<\/pre>\n<p class=\"body\">Although Razor Pages usually have page model classes, they are not a requirement, and simple features can be developed without them. In this example, I have defined a property named <code class=\"fm-code-in-text\">OrderId<\/code> and decorated it with the <code class=\"fm-code-in-text\">BindProperty<\/code> attribute, which specifies that a value for this property should be obtained from the request by the model binding system.<\/p>\n<p class=\"body\">Now customers can go through the entire process, from selecting products to checking out. If they provide valid shipping details (and have items in their cart), they will see the summary page when they click the Complete Order button, as shown in figure 9.6.<\/p>\n<p class=\"body\">Notice the way the application moves between controllers and Razor Pages. The application features that ASP.NET Core provides are complementary and can be mixed freely in projects.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre74\" src=\"\/images\/proaspnetcore7\/000069.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 9.6 The completed order summary view<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-184\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><a class=\"calibre58\" id=\"calibre_link-1633\"><\/a>Representations of user data can be written to persist themselves as session data.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">View components are used to present content that is not directly related to the view model for the current response, such as a summary of a shopping cart.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">View components can access services via dependency injection to get the data they require.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">User data can be received using HTTP POST requests, which are transformed into C# objects by model binding.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core provides integrated support for validating user data and displaying details of validation problems to the user.Revisit and enhance the URL scheme<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Create a category list that will go into the sidebar of the site, highlighting the current category and linking to others<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-185\">\n<div class=\"calibre1\" id=\"calibre_link-1634\">\n<h1 class=\"tochead\" id=\"calibre_link-1635\">10 SportsStore: Administration<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-1636\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Building an interactive feature using Blazor<\/li>\n<li class=\"co-summary-bullet\">Implementing application features with Razor Components<\/li>\n<li class=\"co-summary-bullet\">Aligning component and service lifecycles<\/li>\n<li class=\"co-summary-bullet\">Validating data in a Razor Component<\/li>\n<li class=\"co-summary-bullet\">Performing create, read, update, and delete operations with Blazor<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-1152\"><\/a>In this chapter, I continue to build the SportsStore application to give the site administrator a way to manage orders and products. In this chapter, I use Blazor to create administration features. Blazor combines client-side JavaScript code with server-side code executed by ASP.NET Core, connected by a persistent HTTP connection. I describe Blazor in detail in chapters 32&ndash;35, but it is important to understand that the Blazor model is not suited to all projects. (I use Blazor Server in this chapter, which is a supported part of the ASP.NET Core platform. There is also Blazor WebAssembly, which is, at the time of writing, experimental and runs entirely in the browser. I describe Blazor WebAssembly in chapter 36.)<a id=\"calibre_link-1637\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-186\">10.1 Preparing Blazor Server<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1638\"><\/a>The first step is to enable the services and middleware for Blazor, as shown in listing 10.1.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.1 Enabling Blazor in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\nbuilder.Services.AddScoped&lt;IOrderRepository, EFOrderRepository&gt;();\n\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddDistributedMemoryCache();\nbuilder.Services.AddSession();\nbuilder.Services.AddScoped&lt;Cart&gt;(sp =&gt; SessionCart.GetCart(sp));\nbuilder.Services.AddSingleton&lt;IHttpContextAccessor, \n    HttpContextAccessor&gt;();\n<b class=\"fm-bold\">builder.Services.AddServerSideBlazor();<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.UseSession();\n\napp.MapControllerRoute(\"catpage\",\n    \"{category}\/Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\" });\n        \napp.MapControllerRoute(\"page\", \"Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"category\", \"{category}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"pagination\",\n    \"Products\/Page{productPage}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n<b class=\"fm-bold\">app.MapBlazorHub();<\/b>\n<b class=\"fm-bold\">app.MapFallbackToPage(\"\/admin\/{*catchall}\", \"\/Admin\/Index\");<\/b>\n\nSeedData.EnsurePopulated(app);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddServerSideBlazor<\/code> method creates the services that Blazor uses, and the <code class=\"fm-code-in-text\">MapBlazorHub<\/code> method registers the Blazor middleware components. The final addition is to finesse the routing system to ensure that Blazor works seamlessly with the rest of the application.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-187\">10.1.1 Creating the imports file<\/h3>\n<p class=\"body\">Blazor requires its own imports file to specify the namespaces that it uses. Create the <code class=\"fm-code-in-text\">Pages\/Admin<\/code> folder and add to it a file named <code class=\"fm-code-in-text\">_Imports.razor<\/code> with the content shown in listing 10.2. (If you are using Visual Studio, you can use the Razor Components template to create this file.)<a id=\"calibre_link-1639\"><\/a><a id=\"calibre_link-1153\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The conventional location for Blazor files is within the <code class=\"fm-code-in-text1\">Pages<\/code> folder, but Blazor files can be defined anywhere in the project. In part 4, for example, I used a folder named <code class=\"fm-code-in-text1\">Blazor<\/code> to help emphasize which features were provided by Blazor and which by Razor Pages.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.2 The _Imports.razor file in the SportsStore\/Pages\/Admin folder<\/p>\n<pre class=\"programlisting\">@using Microsoft.AspNetCore.Components\n@using Microsoft.AspNetCore.Components.Forms\n@using Microsoft.AspNetCore.Components.Routing\n@using Microsoft.AspNetCore.Components.Web\n@using Microsoft.EntityFrameworkCore\n@using SportsStore.Models<\/pre>\n<p class=\"body\">The first four <code class=\"fm-code-in-text\">@using<\/code> expressions are for the namespaces required for Blazor. The last two expressions are for convenience in the examples that follow because they will allow me to use Entity Framework Core and the classes in the <code class=\"fm-code-in-text\">Models<\/code> namespace.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-188\">10.1.2 Creating the startup Razor Page<\/h3>\n<p class=\"body\">Blazor relies on a Razor Page to provide the initial content to the browser, which includes the JavaScript code that connects to the server and renders the Blazor HTML content. Add a Razor Page named <code class=\"fm-code-in-text\">Index.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Admin<\/code> folder with the contents shown in listing 10.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.3 The Index.cshtml File in the SportsStore\/Pages\/Admin Folder<\/p>\n<pre class=\"programlisting\">@page \"\/admin\"\n@{  Layout = null; }\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;SportsStore Admin&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    &lt;base href=\"\/\" \/&gt;  \n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;component type=\"typeof(Routed)\" render-mode=\"Server\" \/&gt;\n    &lt;script src=\"\/_framework\/blazor.server.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">component<\/code> element is used to insert a Razor Component in the output from the Razor Page. Razor Components are the confusingly named Blazor building blocks, and the <code class=\"fm-code-in-text\">component<\/code> element applied in listing 10.3 is named <code class=\"fm-code-in-text\">Routed<\/code> and will be created shortly. The Razor Page also contains a <code class=\"fm-code-in-text\">script<\/code> element that tells the browser to load the JavaScript file that Blazor Server uses. Requests for this file are intercepted by the Blazor Server middleware, and you don\u2019t need to explicitly add the JavaScript file to the project.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-189\">10.1.3 Creating the routing and layout components<\/h3>\n<p class=\"body\">Add a Razor Component named <code class=\"fm-code-in-text\">Routed.razor<\/code> to the <code class=\"fm-code-in-text\">Pages\/Admin<\/code> folder and add the content shown in listing 10.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.4 The Routed.razor File in the SportsStore\/Pages\/Admin Folder<\/p>\n<pre class=\"programlisting\">&lt;Router AppAssembly=\"typeof(Program).Assembly\"&gt;\n    &lt;Found&gt;\n        &lt;RouteView RouteData=\"@context\" \n            DefaultLayout=\"typeof(AdminLayout)\" \/&gt;\n    &lt;\/Found&gt;\n    &lt;NotFound&gt;\n        &lt;h4 class=\"bg-danger text-white text-center p-2\"&gt;\n            No Matching Route Found\n        &lt;\/h4&gt;\n    &lt;\/NotFound&gt;\n&lt;\/Router&gt;<\/pre>\n<p class=\"body\">The content of this component is described in detail in part 4 of this book, but, for this chapter, it is enough to know that the component will use the browser\u2019s current URL to locate a Razor Component that can be displayed to the user. If no matching component can be found, then an error message is displayed.<\/p>\n<p class=\"body\">Blazor has its own system of layouts. To create the layout for the administration tools, add a Razor Component named <code class=\"fm-code-in-text\">AdminLayout.razor<\/code> to the <code class=\"fm-code-in-text\">Pages\/Admin<\/code> folder with the content shown in listing 10.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.5 The AdminLayout.razor File in the SportsStore\/Pages\/Admin Folder<\/p>\n<pre class=\"programlisting\">@inherits LayoutComponentBase\n\n&lt;div class=\"bg-info text-white p-2\"&gt;\n    &lt;span class=\"navbar-brand ml-2\"&gt;SPORTS STORE Administration&lt;\/span&gt;\n&lt;\/div&gt;\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row p-2\"&gt;\n        &lt;div class=\"col-3\"&gt;\n            &lt;div class=\"d-grid gap-1\"&gt;\n                &lt;NavLink class=\"btn btn-outline-primary\"\n                            href=\"\/admin\/products\" \n                            ActiveClass=\"btn-primary text-white\" \n                            Match=\"NavLinkMatch.Prefix\"&gt;\n                    Products\n                &lt;\/NavLink&gt;\n                &lt;NavLink class=\"btn btn-outline-primary\"\n                            href=\"\/admin\/orders\" \n                            ActiveClass=\"btn-primary text-white\" \n                            Match=\"NavLinkMatch.Prefix\"&gt;\n                    Orders\n                &lt;\/NavLink&gt;\n            &lt;\/div&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            @Body\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">Blazor uses Razor syntax to generate HTML but introduces its own directives and features. This layout renders a two-column display with Product and Order navigation buttons, which are created using <code class=\"fm-code-in-text\">NavLink<\/code> elements. These elements apply a built-in Razor Component that changes the URL without triggering a new HTTP request, which allows Blazor to respond to user interaction without losing the application state.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-190\">10.1.4 Creating the Razor Components<\/h3>\n<p class=\"body\">To complete the initial setup, I need to add the components that will provide the administration tools, although they will contain placeholder messages at first. Add a Razor Component named <code class=\"fm-code-in-text\">Products.razor<\/code> to the <code class=\"fm-code-in-text\">Pages\/Admin<\/code> folder with the content shown in listing 10.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.6 &nbsp;The Products.razor File in the SportsStore\/Pages\/Admin Folder<\/p>\n<pre class=\"programlisting\">@page \"\/admin\/products\"\n@page \"\/admin\"\n\n&lt;h4&gt;This is the products component&lt;\/h4&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@page<\/code> directives specify the URLs for which this component will be displayed, which are <code class=\"fm-code-in-text\">\/admin\/products<\/code> and <code class=\"fm-code-in-text\">\/admin<\/code>. Next, add a Razor Component named <code class=\"fm-code-in-text\">Orders.razor<\/code> to the <code class=\"fm-code-in-text\">Pages\/Admin<\/code> folder with the content shown in listing 10.7.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.7 &nbsp;The Orders.razor File in the SportsStore\/Pages\/Admin Folder<\/p>\n<pre class=\"programlisting\">@page \"\/admin\/orders\"\n\n&lt;h4&gt;This is the orders component&lt;\/h4&gt;<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-191\">10.1.5 Checking the Blazor setup<\/h3>\n<p class=\"body\">To make sure that Blazor is working correctly, start ASP.NET Core and request http:\/\/localhost:5000\/admin. This request will be handled by the <code class=\"fm-code-in-text\">Index<\/code> Razor Page in the <code class=\"fm-code-in-text\">Pages\/Admin<\/code> folder, which will include the Blazor JavaScript file in the content it sends to the browser. The JavaScript code will open a persistent HTTP connection to the ASP.NET Core server, and the initial Blazor content will be rendered, as shown in figure 10.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Microsoft has not released tools for testing Razor Components, which is why there are no unit testing examples in this chapter.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre75\" src=\"\/images\/proaspnetcore7\/000070.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 10.1 The Blazor application<\/p>\n<\/div>\n<p class=\"body\">Click the Orders button, and content generated by the <code class=\"fm-code-in-text\">Orders<\/code> Razor Component will be displayed, as shown in figure 10.2. Unlike the other ASP.NET Core application frameworks I used in earlier chapters, the new content is displayed without a new HTTP request being sent, even though the URL displayed by the browser changes.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre76\" src=\"\/images\/proaspnetcore7\/000071.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 10.2 Navigating in the Blazor application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-192\">10.2 Managing orders<\/h2>\n<p class=\"body\">Now that Blazor has been set up and tested, I am going to start implementing administration features. In the previous chapter, I added support for receiving orders from customers and storing them in a database. In this section, I am going to create a simple administration tool that will let me view the orders that have been received and mark them as shipped.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-193\">10.2.1 Enhancing the model<\/h3>\n<p class=\"body\">The first change I need to make is to enhance the data model so that I can record which orders have been shipped. Listing 10.8 shows the addition of a new property to the <code class=\"fm-code-in-text\">Order<\/code> class, which is defined in the <code class=\"fm-code-in-text\">Order.cs<\/code> file in the <code class=\"fm-code-in-text\">Models<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.8 Adding a property in the Order.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\n\nnamespace SportsStore.Models {\n\n    public class Order {\n        \n        [BindNever]\n        public int OrderID { get; set; }\n        [BindNever]\n        public ICollection&lt;CartLine&gt; Lines { get; set; }\n            = new List&lt;CartLine&gt;();\n                        \n        [Required(ErrorMessage = \"Please enter a name\")]\n        public string? Name { get; set; }\n\n        [Required(ErrorMessage = \"Please enter the first address line\")]\n        public string? Line1 { get; set; }\n        public string? Line2 { get; set; }\n        public string? Line3 { get; set; }\n                \n        [Required(ErrorMessage = \"Please enter a city name\")]\n        public string? City { get; set; }\n                \n        [Required(ErrorMessage = \"Please enter a state name\")]\n        public string? State { get; set; }\n        public string? Zip { get; set; }\n                \n        [Required(ErrorMessage = \"Please enter a country name\")]\n        public string? Country { get; set; }\n                \n        public bool GiftWrap { get; set; }\n                \n        <b class=\"fm-bold\">[BindNever]<\/b>\n        <b class=\"fm-bold\">public bool Shipped { get; set; }<\/b>\n    }\n}<\/pre>\n<p class=\"body\">This iterative approach of extending and adapting the data model to support different features is typical of ASP.NET Core development. In an ideal world, you would be able to completely define the data model at the start of the project and just build the application around it, but that happens only for the simplest of projects, and, in practice, iterative development is to be expected as the understanding of what is required develops and evolves.<\/p>\n<p class=\"body\">Entity Framework Core migrations make this process easier because you don\u2019t have to manually keep the database schema synchronized to the model class by writing your own SQL commands. To update the database to reflect the addition of the <code class=\"fm-code-in-text\">Shipped<\/code> property to the <code class=\"fm-code-in-text\">Order<\/code> class, open a new PowerShell window and run the command shown in listing 10.9 in the SportsStore project.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.9 Creating a new migration<\/p>\n<pre class=\"programlisting\">dotnet ef migrations add ShippedOrders<\/pre>\n<p class=\"body\">The migration will be applied automatically when the application is started and the <code class=\"fm-code-in-text\">SeedData<\/code> class calls the <code class=\"fm-code-in-text\">Migrate<\/code> method provided by Entity Framework Core.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-194\">10.2.2 Displaying orders to the administrator<\/h3>\n<p class=\"body\">I am going to display two tables, one of which shows the orders waiting to be shipped and the other the shipped orders. Each order will be presented with a button that changes the shipping state. This is not entirely realistic because orders processing is typically more complex than simply updating a field in the database, but integration with warehouse and fulfillment systems is well beyond the scope of this book.<\/p>\n<p class=\"body\">To avoid duplicating code and content, I am going to create a Razor Component that displays a table without knowing which category of order it is dealing with. Add a Razor Component named <code class=\"fm-code-in-text\">OrderTable.razor<\/code> to the <code class=\"fm-code-in-text\">Pages\/Admin<\/code> folder with the content shown in listing 10.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.10 The OrderTable.razor file in the SportsStore\/Pages\/Admin folder<\/p>\n<pre class=\"programlisting\">&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;&lt;th colspan=\"5\" class=\"text-center\"&gt;@TableTitle&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @if (Orders?.Count() &gt; 0) {\n            @foreach (Order o in Orders) {\n                &lt;tr&gt;\n                    &lt;td&gt;@o.Name&lt;\/td&gt;\n                    &lt;td&gt;@o.Zip&lt;\/td&gt;\n                    &lt;th&gt;Product&lt;\/th&gt;\n                    &lt;th&gt;Quantity&lt;\/th&gt;\n                    &lt;td&gt;\n                        &lt;button class=\"btn btn-sm btn-danger\"\n                        @onclick=\"@(e =&gt; \n                                OrderSelected.InvokeAsync(o.OrderID))\"&gt;\n                            @ButtonLabel\n                        &lt;\/button&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n                @foreach (CartLine line in o.Lines) {\n                    &lt;tr&gt;\n                        &lt;td colspan=\"2\"&gt;&lt;\/td&gt;\n                        &lt;td&gt;@line.Product.Name&lt;\/td&gt;\n                        &lt;td&gt;@line.Quantity&lt;\/td&gt;\n                        &lt;td&gt;&lt;\/td&gt;\n                    &lt;\/tr&gt;\n                }\n            }\n        } else {\n            &lt;tr&gt;&lt;td colspan=\"5\" class=\"text-center\"&gt;No Orders&lt;\/td&gt;&lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n@code {\n\n    [Parameter]\n    public string TableTitle { get; set; } = \"Orders\";\n        \n    [Parameter]\n    public IEnumerable&lt;Order&gt; Orders { get; set; } \n        = Enumerable.Empty&lt;Order&gt;();\n                \n    [Parameter]\n    public string ButtonLabel { get; set; } = \"Ship\";\n        \n    [Parameter]\n    public EventCallback&lt;int&gt; OrderSelected { get; set; }\n}<\/pre>\n<p class=\"body\">Razor Components, as the name suggests, rely on the Razor approach to annotated HTML elements. The view part of the component is supported by the statements in the <code class=\"fm-code-in-text\">@code<\/code> section. The <code class=\"fm-code-in-text\">@code<\/code> section in this component defines four properties that are decorated with the <code class=\"fm-code-in-text\">Parameter<\/code> attribute, which means the values will be provided at runtime by the parent component, which I will create shortly. The values provided for the parameters are used in the view section of the component to display details of a sequence of <code class=\"fm-code-in-text\">Order<\/code> objects.<\/p>\n<p class=\"body\">Blazor adds expressions to the Razor syntax. The view section of this component includes this <code class=\"fm-code-in-text\">button<\/code> element, which has an <code class=\"fm-code-in-text\">@onclick<\/code> attribute:<\/p>\n<pre class=\"programlisting\">...\n&lt;button class=\"btn btn-sm btn-danger\" \n        <b class=\"fm-bold\">@onclick=\"@(e =&gt; OrderSelected.InvokeAsync(o.OrderID))\"<\/b>&gt;\n    @ButtonLabel\n&lt;\/button&gt;\n...<\/pre>\n<p class=\"body\">This tells Blazor how to react when the user clicks the button. In this case, the expression tells Razor to call the <code class=\"fm-code-in-text\">InvokeAsync<\/code> method of the <code class=\"fm-code-in-text\">OrderSelected<\/code> property. This is how the table will communicate with the rest of the Blazor application and will become clearer as I build out additional features.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> I describe Blazor in-depth in part 4 of this book, so don\u2019t worry if the Razor Components in this chapter do not make immediate sense. The purpose of the SportsStore example is to show the overall development process, even if individual features are not understood.<\/p>\n<p class=\"body\">The next step is to create a component that will get the <code class=\"fm-code-in-text\">Order<\/code> data from the database and use the <code class=\"fm-code-in-text\">OrderTable<\/code> component to display it to the user. Remove the placeholder content in the <code class=\"fm-code-in-text\">Orders<\/code> component and replace it with the code and content shown in listing 10.11.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.11 The revised contents of the Orders.razor file in the SportsStore\/Pages\/Admin folder<\/p>\n<pre class=\"programlisting\">@page \"\/admin\/orders\"\n@inherits OwningComponentBase&lt;IOrderRepository&gt;\n\n&lt;OrderTable TableTitle=\"Unshipped Orders\" Orders=\"UnshippedOrders\" \n            ButtonLabel=\"Ship\" OrderSelected=\"ShipOrder\" \/&gt;\n&lt;OrderTable TableTitle=\"Shipped Orders\" Orders=\"ShippedOrders\" \n            ButtonLabel=\"Reset\" OrderSelected=\"ResetOrder\" \/&gt;\n&lt;button class=\"btn btn-info\" @onclick=\"@(e =&gt; UpdateData())\"&gt;\n    Refresh Data\n&lt;\/button&gt;\n\n@code {\n\n    public IOrderRepository Repository =&gt; Service;\n        \n    public IEnumerable&lt;Order&gt; AllOrders { get; set; } \n        = Enumerable.Empty&lt;Order&gt;();\n    public IEnumerable&lt;Order&gt; UnshippedOrders { get; set; }\n        = Enumerable.Empty&lt;Order&gt;();\n    public IEnumerable&lt;Order&gt; ShippedOrders { get; set; }\n        = Enumerable.Empty&lt;Order&gt;();\n                \n    protected async override Task OnInitializedAsync() {\n        await UpdateData();\n    }\n        \n    public async Task UpdateData() {\n        AllOrders = await Repository.Orders.ToListAsync();\n        UnshippedOrders = AllOrders.Where(o =&gt; !o.Shipped);\n        ShippedOrders = AllOrders.Where(o =&gt; o.Shipped);\n    }\n        \n    public void ShipOrder(int id) =&gt; UpdateOrder(id, true);\n    public void ResetOrder(int id) =&gt; UpdateOrder(id, false);\n        \n    private void UpdateOrder(int id, bool shipValue) {\n        Order? o = Repository.Orders.FirstOrDefault(o =&gt; o.OrderID == id);\n        if (o != null) {\n            o.Shipped = shipValue;\n            Repository.SaveOrder(o);\n        }\n    }\n}<\/pre>\n<p class=\"body\">Blazor Components are not like the other application framework building blocks used for the user-facing sections of the SportsStore application. Instead of dealing with individual requests, components can be long-lived and deal with multiple user interactions over a longer period. This requires a different style of development, especially when it comes to dealing with data using Entity Framework Core. The <code class=\"fm-code-in-text\">@inherits<\/code> expression ensures that this component gets its own repository object, which ensures its operations are separate from those performed by other components displayed to the same user. And to avoid repeatedly querying the database&mdash;which can be a serious problem in Blazor, as I explain in part 4&mdash;the repository is used only when the component is initialized, when Blazor invokes the <code class=\"fm-code-in-text\">OnInitializedAsync<\/code> method, or when the user clicks a Refresh Data button.<\/p>\n<p class=\"body\">To display its data to the user, the <code class=\"fm-code-in-text\">OrderTable<\/code> component is used, which is applied as an HTML element, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;OrderTable TableTitle=\"Unshipped Orders\" \n    Orders=\"UnshippedOrders\" ButtonLabel=\"Ship\"     OrderSelected=\"ShipOrder\" \/&gt;\n...<\/pre>\n<p class=\"body\">The values assigned to the <code class=\"fm-code-in-text\">OrderTable<\/code> element\u2019s attributes are used to set the properties decorated with the <code class=\"fm-code-in-text\">Parameter<\/code> attribute in listing 10.10. In this way, a single component can be configured to present two different sets of data without the need to duplicate code and content.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ShipOrder<\/code> and <code class=\"fm-code-in-text\">ResetOrder<\/code> methods are used as the values for the <code class=\"fm-code-in-text\">OrderSelected<\/code> attributes, which means they are invoked when the user clicks one of the buttons presented by the <code class=\"fm-code-in-text\">OrderTable<\/code> component, updating the data in the database through the repository.<\/p>\n<p class=\"body\">To see the new features, restart ASP.NET Core, request http:\/\/localhost:5000, and create an order. Once you have at least one order in the database, request http:\/\/localhost:5000\/admin\/orders, and you will see a summary of the order you created displayed in the Unshipped Orders table. Click the Ship button, and the order will be updated and moved to the Shipped Orders table, as shown in figure 10.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre77\" src=\"\/images\/proaspnetcore7\/000072.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 10.3 Administering orders<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-195\">10.3 Adding catalog management<\/h2>\n<p class=\"body\">The convention for managing more complex collections of items is to present the user with two interfaces: a <i class=\"fm-italics\">list<\/i> interface and an <i class=\"fm-italics\">edit<\/i> interface, as shown in figure 10.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre78\" src=\"\/images\/proaspnetcore7\/000073.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 10.4 Sketch of a CRUD UI for the product catalog<\/p>\n<\/div>\n<p class=\"body\">Together, these interfaces allow a user to create, read, update, and delete items in the collection. Collectively, these actions are known as <i class=\"fm-italics\">CRUD<\/i>. In this section, I will implement these interfaces using Blazor.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Developers need to implement CRUD so often that Visual Studio scaffolding includes scenarios for creating CRUD controllers or Razor Pages. But, like all Visual Studio scaffolding, I think it is better to learn how to create these features directly, which is why I demonstrate CRUD operations for all the ASP.NET Core application frameworks in later chapters.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-196\">10.3.1 Expanding the repository<\/h3>\n<p class=\"body\">The first step is to add features to the repository that will allow <code class=\"fm-code-in-text\">Product<\/code> objects to be created, modified, and deleted. Listing 10.12 adds new methods to the <code class=\"fm-code-in-text\">IStoreRepository<\/code> interface.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.12 Adding methods in the IStoreRepository.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Models {\n    public interface IStoreRepository {\n        \n        IQueryable&lt;Product&gt; Products { get; }\n                \n        <b class=\"fm-bold\">void SaveProduct(Product p);<\/b>\n        <b class=\"fm-bold\">void CreateProduct(Product p);<\/b>\n        <b class=\"fm-bold\">void DeleteProduct(Product p);<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Listing 10.13 adds implementations of these methods to the Entity Framework Core repository class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.13 Implementing methods in the EFStoreRepository.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">namespace SportsStore.Models {\n    public class EFStoreRepository : IStoreRepository {\n        private StoreDbContext context;\n                \n        public EFStoreRepository(StoreDbContext ctx) {\n            context = ctx;\n        }\n                \n        public IQueryable&lt;Product&gt; Products =&gt; context.Products;\n                \n        <b class=\"fm-bold\">public void CreateProduct(Product p) {<\/b>\n            <b class=\"fm-bold\">context.Add(p);<\/b>\n            <b class=\"fm-bold\">context.SaveChanges();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">public void DeleteProduct(Product p) {<\/b>\n            <b class=\"fm-bold\">context.Remove(p);<\/b>\n            <b class=\"fm-bold\">context.SaveChanges();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">public void SaveProduct(Product p) {<\/b>\n            <b class=\"fm-bold\">context.SaveChanges();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-197\">10.3.2 Applying validation attributes to the data model<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1640\"><\/a>I want to validate the values the user provides when editing or creating <code class=\"fm-code-in-text\">Product<\/code> objects, just as I did for the customer checkout process. In listing 10.14, I have added validation attributes to the <code class=\"fm-code-in-text\">Product<\/code> data model class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.14 Adding validation in the Product.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations.Schema;\n<b class=\"fm-bold\">using System.ComponentModel.DataAnnotations;<\/b>\n\nnamespace SportsStore.Models {\n\n    public class Product {\n        \n        public long? ProductID { get; set; }\n                \n        <b class=\"fm-bold\">[Required(ErrorMessage = \"Please enter a product name\")]<\/b>\n        public string Name { get; set; } = String.Empty;\n                \n        <b class=\"fm-bold\">[Required(ErrorMessage = \"Please enter a description\")]<\/b>\n        public string Description { get; set; } = String.Empty;\n                \n        <b class=\"fm-bold\">[Required]<\/b>\n        <b class=\"fm-bold\">[Range(0.01, double.MaxValue,<\/b>\n            <b class=\"fm-bold\">ErrorMessage = \"Please enter a positive price\")]<\/b>\n        [Column(TypeName = \"decimal(8, 2)\")]\n        public decimal Price { get; set; }\n                \n        <b class=\"fm-bold\">[Required(ErrorMessage = \"Please specify a category\")]<\/b>\n        public string Category { get; set; } = String.Empty;\n    }\n}<\/pre>\n<p class=\"body\">Blazor uses the same approach to validation as the rest of ASP.NET Core but, as you will see, applies it in a different way to deal with the more interactive nature of Razor Components.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-198\">10.3.3 Creating the list component<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1641\"><\/a>I am going to start by creating the table that will present the user with a table of products and the links that will allow them to be inspected and edited. Replace the contents of the <code class=\"fm-code-in-text\">Products.razor<\/code> file with those shown in listing 10.15.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.15 The revised contents of the Products.razor file in the SportsStore\/Pages\/Admin folder<\/p>\n<pre class=\"programlisting\">@page \"\/admin\/products\"\n@page \"\/admin\"\n@inherits OwningComponentBase&lt;IStoreRepository&gt;\n\n&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;\n            &lt;th&gt;Name&lt;\/th&gt;\n            &lt;th&gt;Category&lt;\/th&gt;\n            &lt;th&gt;Price&lt;\/th&gt;\n            &lt;td \/&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @if (ProductData?.Count() &gt; 0) {\n            @foreach (Product p in ProductData) {\n                &lt;tr&gt;\n                    &lt;td&gt;@p.ProductID&lt;\/td&gt;\n                    &lt;td&gt;@p.Name&lt;\/td&gt;\n                    &lt;td&gt;@p.Category&lt;\/td&gt;\n                    &lt;td&gt;@p.Price.ToString(\"c\")&lt;\/td&gt;\n                    &lt;td&gt;\n                        &lt;NavLink class=\"btn btn-info btn-sm\"\n                         href=\"@GetDetailsUrl(p.ProductID ?? 0)\"&gt;\n                            Details\n                        &lt;\/NavLink&gt;\n                        &lt;NavLink class=\"btn btn-warning btn-sm\"\n                         href=\"@GetEditUrl(p.ProductID ?? 0)\"&gt;\n                            Edit\n                        &lt;\/NavLink&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        } else {\n            &lt;tr&gt;\n                &lt;td colspan=\"5\" class=\"text-center\"&gt;No Products&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n&lt;NavLink class=\"btn btn-primary\" href=\"\/admin\/products\/create\"&gt;\n    Create\n&lt;\/NavLink&gt;\n\n@code {\n\n    public IStoreRepository Repository =&gt; Service;\n        \n    public IEnumerable&lt;Product&gt; ProductData { get; set; }\n        = Enumerable.Empty&lt;Product&gt;();\n                \n    protected async override Task OnInitializedAsync() {\n        await UpdateData();\n    }\n        \n    public async Task UpdateData() {\n        ProductData = await Repository.Products.ToListAsync();\n    }\n        \n    public string GetDetailsUrl(long id) =&gt; \n        $\"\/admin\/products\/details\/{id}\";\n    public string GetEditUrl(long id) =&gt; \n        $\"\/admin\/products\/edit\/{id}\";\n}<\/pre>\n<p class=\"body\">The component presents each <code class=\"fm-code-in-text\">Product<\/code> object in the repository in a table row with <code class=\"fm-code-in-text\">NavLink<\/code> components that will navigate to the components that will provide a detailed view and an editor. There is also a button that navigates to the component that will allow new <code class=\"fm-code-in-text\">Product<\/code> objects to be created and stored in the database. Restart ASP.NET Core and request http:\/\/localhost:5000\/admin\/products, and you will see the content shown in figure 10.5, although none of the buttons presented by the <code class=\"fm-code-in-text\">Products<\/code> component work currently because I have yet to create the components they target.<a id=\"calibre_link-1642\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre79\" src=\"\/images\/proaspnetcore7\/000074.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 10.5 Presenting a list of products<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-199\">10.3.4 Creating the detail component<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1643\"><\/a>The job of the detail component is to display all the fields for a single <code class=\"fm-code-in-text\">Product<\/code> object. Add a Razor Component named <code class=\"fm-code-in-text\">Details.razor<\/code> to the <code class=\"fm-code-in-text\">Pages\/Admin<\/code> folder with the content shown in listing 10.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.16 &nbsp;The Details.razor file in the SportsStore\/Pages\/Admin folder<\/p>\n<pre class=\"programlisting\">@page \"\/admin\/products\/details\/{id:long}\"\n@inherits OwningComponentBase&lt;IStoreRepository&gt;\n\n&lt;h3 class=\"bg-info text-white text-center p-1\"&gt;Details&lt;\/h3&gt;\n\n&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    &lt;tbody&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;td&gt;@Product?.ProductID&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Product?.Name&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Description&lt;\/th&gt;&lt;td&gt;@Product?.Description&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Category&lt;\/th&gt;&lt;td&gt;@Product?.Category&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;@Product?.Price.ToString(\"C\")&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n&lt;NavLink class=\"btn btn-warning\" href=\"@EditUrl\"&gt;Edit&lt;\/NavLink&gt;\n&lt;NavLink class=\"btn btn-secondary\" href=\"\/admin\/products\"&gt;Back&lt;\/NavLink&gt;\n\n@code {\n\n    [Inject]\n    public IStoreRepository? Repository { get; set; }\n        \n    [Parameter]\n    public long Id { get; set; }\n        \n    public Product? Product { get; set; }\n        \n    protected override void OnParametersSet() {\n        Product = \n            Repository?.Products.FirstOrDefault(p =&gt; p.ProductID == Id);\n    }\n        \n    public string EditUrl =&gt; $\"\/admin\/products\/edit\/{Product?.ProductID}\";\n}<\/pre>\n<p class=\"body\">The component uses the <code class=\"fm-code-in-text\">Inject<\/code> attribute to declare that it requires an implementation of the <code class=\"fm-code-in-text\">IStoreRepository<\/code> interface, which is one of the ways that Blazor provides access to the application\u2019s services. The value of the <code class=\"fm-code-in-text\">Id<\/code> property will be populated from the URL that has been used to navigate to the component, which is used to retrieve the <code class=\"fm-code-in-text\">Product<\/code> object from the database. To see the detail view, restart ASP.NET Core, request http:\/\/localhost:5000\/admin\/products, and click one of the Details buttons, as shown in figure 10.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre80\" src=\"\/images\/proaspnetcore7\/000075.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 10.6 Displaying details of a product<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-200\">10.3.5 Creating the editor component<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1644\"><\/a>The operations to create and edit data will be handled by the same component. Add a Razor Component named <code class=\"fm-code-in-text\">Editor.razor<\/code> to the <code class=\"fm-code-in-text\">Pages\/Admin<\/code> folder with the content shown in listing 10.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.17 &nbsp;The Editor.razor file in the SportsStore\/Pages\/Admin folder<\/p>\n<pre class=\"programlisting\">@page \"\/admin\/products\/edit\/{id:long}\"\n@page \"\/admin\/products\/create\"\n@inherits OwningComponentBase&lt;IStoreRepository&gt;\n\n&lt;style&gt;\n    div.validation-message { color: rgb(220, 53, 69); font-weight: 500 }\n&lt;\/style&gt;\n\n&lt;h3 class=\"bg-@ThemeColor text-white text-center p-1\"&gt;\n    @TitleText a Product\n&lt;\/h3&gt;\n&lt;EditForm Model=\"Product\" OnValidSubmit=\"SaveProduct\"&gt;\n    &lt;DataAnnotationsValidator \/&gt;\n    @if(Product.ProductID.HasValue &amp;&amp; Product.ProductID.Value != 0) {\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;ID&lt;\/label&gt;\n            &lt;input class=\"form-control\" disabled \n                value=\"@Product.ProductID\" \/&gt;\n        &lt;\/div&gt;\n    }\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        &lt;ValidationMessage For=\"@(() =&gt; Product.Name)\" \/&gt;\n        &lt;InputText class=\"form-control\" @bind-Value=\"Product.Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Description&lt;\/label&gt;\n        &lt;ValidationMessage For=\"@(() =&gt; Product.Description)\" \/&gt;\n        &lt;InputText class=\"form-control\" \n            @bind-Value=\"Product.Description\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Category&lt;\/label&gt;\n        &lt;ValidationMessage For=\"@(() =&gt; Product.Category)\" \/&gt;\n        &lt;InputText class=\"form-control\" @bind-Value=\"Product.Category\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Price&lt;\/label&gt;\n        &lt;ValidationMessage For=\"@(() =&gt; Product.Price)\" \/&gt;\n        &lt;InputNumber class=\"form-control\" @bind-Value=\"Product.Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"mt-2\"&gt;\n        &lt;button type=\"submit\" class=\"btn btn-@ThemeColor\"&gt;Save&lt;\/button&gt;\n        &lt;NavLink class=\"btn btn-secondary\" href=\"\/admin\/products\"&gt;\n            Cancel\n        &lt;\/NavLink&gt;\n    &lt;\/div&gt;\n&lt;\/EditForm&gt;\n\n@code {\n\n    public IStoreRepository Repository =&gt; Service;\n        \n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    [Parameter]\n    public long Id { get; set; } = 0;\n        \n    public Product Product { get; set; } = new Product();\n        \n    protected override void OnParametersSet() {\n        if (Id != 0) {\n            Product = Repository.Products\n                .FirstOrDefault(p =&gt; p.ProductID == Id) ?? new();\n        } \n    }\n        \n    public void SaveProduct() {\n        if (Id == 0) {\n            Repository.CreateProduct(Product);\n        } else {\n            Repository.SaveProduct(Product);\n        }\n        NavManager?.NavigateTo(\"\/admin\/products\");\n    }\n        \n    public string ThemeColor =&gt; Id == 0 ? \"primary\" : \"warning\";\n    public string TitleText =&gt; Id == 0 ? \"Create\" : \"Edit\";\n}<a id=\"calibre_link-1645\"><\/a><\/pre>\n<p class=\"body\">Blazor provides a set of built-in Razor Components that are used to display and validate forms, which is important because the browser can\u2019t submit data using a <code class=\"fm-code-in-text\">POST<\/code> request in a Blazor Component. The <code class=\"fm-code-in-text\">EditForm<\/code> component is used to render a Blazor-friendly form, and the <code class=\"fm-code-in-text\">InputText<\/code> and <code class=\"fm-code-in-text\">InputNumber<\/code> components render <code class=\"fm-code-in-text\">input<\/code> elements that accept string and number values and that automatically update a model property when the user makes a change.<a id=\"calibre_link-1646\"><\/a><\/p>\n<p class=\"body\">Data validation is integrated into these built-in components, and the <code class=\"fm-code-in-text\">OnValidSubmit<\/code> attribute on the <code class=\"fm-code-in-text\">EditForm<\/code> component is used to specify a method that is invoked only if the data entered into the form conforms to the rules defined by the validation attributes.<\/p>\n<p class=\"body\">Blazor also provides the <code class=\"fm-code-in-text\">NavigationManager<\/code> class, which is used to programmatically navigate between components without triggering a new HTTP request. The <code class=\"fm-code-in-text\">Editor<\/code> component uses <code class=\"fm-code-in-text\">NavigationManager<\/code>, which is obtained as a service, to return to the <code class=\"fm-code-in-text\">Products<\/code> component after the database has been updated.<\/p>\n<p class=\"body\">To see the editor, restart ASP.NET Core, request http:\/\/localhost:5000\/admin, and click the Create button. Click the Save button without filling out the form fields, and you will see the validation errors that Blazor produces automatically, as shown in figure 10.7. Fill out the form and click Save again, and you will see the product you created displayed in the table, also as shown in figure 10.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre81\" src=\"\/images\/proaspnetcore7\/000076.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 10.7 Using the Editor component<\/p>\n<\/div>\n<p class=\"body\">Click the Edit button for one of the products, and the same component will be used to edit the selected <code class=\"fm-code-in-text\">Product<\/code> object\u2019s properties. Click the Save button, and any changes you made&mdash;if they pass validation&mdash;will be stored in the database, as shown in figure 10.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre82\" src=\"\/images\/proaspnetcore7\/000077.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 10.8 Editing products<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-201\">10.3.6 Deleting products<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1647\"><\/a>The final CRUD feature is deleting products, which is easily implemented in the <code class=\"fm-code-in-text\">Products<\/code> component, as shown in listing 10.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 10.18 Adding delete support in the Products.razor file in the SportsStore\/Pages\/Admin folder<\/p>\n<pre class=\"programlisting\">@page \"\/admin\/products\"\n@page \"\/admin\"\n@inherits OwningComponentBase&lt;IStoreRepository&gt;\n\n&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;\n            &lt;th&gt;Name&lt;\/th&gt;\n            &lt;th&gt;Category&lt;\/th&gt;\n            &lt;th&gt;Price&lt;\/th&gt;\n            &lt;td \/&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @if (ProductData?.Count() &gt; 0) {\n            @foreach (Product p in ProductData) {\n                &lt;tr&gt;\n                    &lt;td&gt;@p.ProductID&lt;\/td&gt;\n                    &lt;td&gt;@p.Name&lt;\/td&gt;\n                    &lt;td&gt;@p.Category&lt;\/td&gt;\n                    &lt;td&gt;@p.Price.ToString(\"c\")&lt;\/td&gt;\n                    &lt;td&gt;\n                        &lt;NavLink class=\"btn btn-info btn-sm\"\n                         href=\"@GetDetailsUrl(p.ProductID ?? 0)\"&gt;\n                            Details\n                        &lt;\/NavLink&gt;\n                        &lt;NavLink class=\"btn btn-warning btn-sm\"\n                         href=\"@GetEditUrl(p.ProductID ?? 0)\"&gt;\n                            Edit\n                        &lt;\/NavLink&gt;\n                        <b class=\"fm-bold\">&lt;button class=\"btn btn-danger btn-sm\"<\/b>\n                                 <b class=\"fm-bold\">@onclick=\"@(e =&gt; DeleteProduct(p))\"&gt;<\/b>\n                            <b class=\"fm-bold\">Delete<\/b>\n                        <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        } else {\n            &lt;tr&gt;\n                &lt;td colspan=\"5\" class=\"text-center\"&gt;No Products&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n&lt;NavLink class=\"btn btn-primary\" href=\"\/admin\/products\/create\"&gt;\n    Create\n&lt;\/NavLink&gt;\n\n@code {\n\n    public IStoreRepository Repository =&gt; Service;\n        \n    public IEnumerable&lt;Product&gt; ProductData { get; set; }\n        = Enumerable.Empty&lt;Product&gt;();\n                \n    protected async override Task OnInitializedAsync() {\n        await UpdateData();\n    }\n        \n    public async Task UpdateData() {\n        ProductData = await Repository.Products.ToListAsync();\n    }\n        \n    <b class=\"fm-bold\">public async Task DeleteProduct(Product p) {<\/b>\n        <b class=\"fm-bold\">Repository.DeleteProduct(p);<\/b>\n        <b class=\"fm-bold\">await UpdateData();<\/b>\n    <b class=\"fm-bold\">}<\/b>\n        \n    public string GetDetailsUrl(long id) =&gt; \n        $\"\/admin\/products\/details\/{id}\";\n    public string GetEditUrl(long id) =&gt; \n        $\"\/admin\/products\/edit\/{id}\";\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1648\"><\/a>The new <code class=\"fm-code-in-text\">button<\/code> element is configured with the <code class=\"fm-code-in-text\">@onclick<\/code> attribute, which invokes the <code class=\"fm-code-in-text\">DeleteProduct<\/code> method. The selected <code class=\"fm-code-in-text\">Product<\/code> object is removed from the database, and the data displayed by the component is updated. Restart ASP.NET Core, request http:\/\/localhost:5000\/admin\/products, and click a Delete button to remove an object from the database, as shown in figure 10.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre83\" src=\"\/images\/proaspnetcore7\/000078.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 10.9 Deleting objects from the database<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-202\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Blazor creates ASP.NET Core applications that use JavaScript to respond to user interaction, handled by C# code running in the ASP.NET Core server.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Blazor functionality is created using Razor Components, which have a similar syntax to Razor Pages and views.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Requests are directed to components using the <code class=\"fm-code-in-text\">@page<\/code> directive.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The lifecycle of repository objects is aligned the to the component lifecycle using the <code class=\"fm-code-in-text\">@inherits OwningComponentBase&lt;T&gt;<\/code> expression.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Blazor provides built-in components for common tasks, such as receiving user input, defining layouts, and navigating between pages.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-203\">\n<div class=\"calibre1\" id=\"calibre_link-1649\">\n<h1 class=\"tochead\" id=\"calibre_link-1650\"><a id=\"calibre_link-1651\"><\/a>11 SportsStore: Security and deployment<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Authenticating users with ASP.NET Core Identity<\/li>\n<li class=\"co-summary-bullet\">Authorizing user access to ASP.NET Core resources<\/li>\n<li class=\"co-summary-bullet\">Preparing and publishing an application<\/li>\n<li class=\"co-summary-bullet\">Creating a Docker container image for the SportsStore application<\/li>\n<\/ul>\n<p class=\"body\">Authentication and authorization are provided by the ASP.NET Core Identity system, which integrates neatly into the ASP.NET Core platform and the individual application frameworks. In the sections that follow, I will create a basic security setup that allows one user, called <code class=\"fm-code-in-text\">Admin<\/code>, to authenticate and access the administration features in the application. ASP.NET Core Identity provides many more features for authenticating users and authorizing access to application features and data, and you can find more information in chapters 37 and 38, where I show you how to create and manage user accounts and how to perform authorization using roles. But, as I noted previously, ASP.NET Core Identity is a large framework in its own right, and I cover only the basic features in this book.<\/p>\n<p class=\"body\">My goal in this chapter is just to get enough functionality in place to prevent customers from being able to access the sensitive parts of the SportsStore application and, in doing so, give you a flavor of how authentication and authorization fit into an ASP.NET Core application.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-204\">11.1 Creating the Identity database<\/h2>\n<p class=\"body\">The ASP.NET Identity system is endlessly configurable and extensible and supports lots of options for how its user data is stored. I am going to use the most common, which is to store the data using Microsoft SQL Server accessed using Entity Framework Core.<a id=\"calibre_link-1652\"><\/a><a id=\"calibre_link-1165\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-205\">11.1.1 Installing the Identity package for Entity Framework Core<\/h3>\n<p class=\"body\">To add the package that contains the ASP.NET Core Identity support for Entity Framework Core, use a PowerShell command prompt to run the command shown in listing 11.1 in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.1 Installing the Entity Framework Core package<\/p>\n<pre class=\"programlisting\">dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore\n    --version 7.0.0<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-206\">11.1.2 Creating the context class<\/h3>\n<p class=\"body\">I need to create a database context file that will act as the bridge between the database and the Identity model objects it provides access to. I added a class file called <code class=\"fm-code-in-text\">AppIdentityDbContext.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and used it to define the class shown in listing 11.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.2 The AppIdentityDbContext.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Identity;\nusing Microsoft.AspNetCore.Identity.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace SportsStore.Models {\n\n    public class AppIdentityDbContext : IdentityDbContext&lt;IdentityUser&gt; {\n        \n        public AppIdentityDbContext(\n                DbContextOptions&lt;AppIdentityDbContext&gt; options)\n            : base(options) { }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AppIdentityDbContext<\/code> class is derived from <code class=\"fm-code-in-text\">IdentityDbContext<\/code>, which provides Identity-specific features for Entity Framework Core. For the type parameter, I used the <code class=\"fm-code-in-text\">IdentityUser<\/code> class, which is the built-in class used to represent users.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-207\">11.1.3 Defining the connection string<\/h3>\n<p class=\"body\">The next step is to define the connection string for the database. Listing 11.3 shows the addition of the connection string to the <code class=\"fm-code-in-text\">appsettings.json<\/code> file of the SportsStore project, which follows the same format as the connection string that I defined for the product database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.3 Defining a connection string in the appsettings.json file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\",\n  \"ConnectionStrings\": {\n    \"SportsStoreConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>SportsStore;MultipleActiveResultSets=true\",\n    <b class=\"fm-bold\">\"IdentityConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=Identity<\/b>\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span><b class=\"fm-bold\">;MultipleActiveResultSets=true\"<\/b>\n  }\n}<\/pre>\n<p class=\"body\">Remember that the connection string has to be defined in a single unbroken line in the <code class=\"fm-code-in-text\">appsettings.json<\/code> file and is shown across multiple lines in the listing only because of the fixed width of a book page. The addition in the listing defines a connection string called <code class=\"fm-code-in-text\">IdentityConnection<\/code> that specifies a LocalDB database called <code class=\"fm-code-in-text\">Identity<\/code>.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-208\">11.1.4 Configuring the application<\/h3>\n<p class=\"body\">Like other ASP.NET Core features, Identity is configured in the <code class=\"fm-code-in-text\">Program.cs<\/code> file. Listing 11.4 shows the additions I made to set up Identity in the SportsStore project, using the context class and connection string defined previously.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.4 Configuring identity in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Identity;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\nbuilder.Services.AddScoped&lt;IOrderRepository, EFOrderRepository&gt;();\n\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddDistributedMemoryCache();\nbuilder.Services.AddSession();\nbuilder.Services.AddScoped&lt;Cart&gt;(sp =&gt; SessionCart.GetCart(sp));\nbuilder.Services.AddSingleton&lt;IHttpContextAccessor, \n    HttpContextAccessor&gt;();\nbuilder.Services.AddServerSideBlazor();\n\n<b class=\"fm-bold\">builder.Services.AddDbContext&lt;AppIdentityDbContext&gt;(options =&gt;<\/b>\n    <b class=\"fm-bold\">options.UseSqlServer(<\/b>\n        <b class=\"fm-bold\">builder.Configuration[\"ConnectionStrings:IdentityConnection\"]));<\/b>\n<b class=\"fm-bold\">builder.Services.AddIdentity&lt;IdentityUser, IdentityRole&gt;()<\/b>\n    <b class=\"fm-bold\">.AddEntityFrameworkStores&lt;AppIdentityDbContext&gt;();<\/b>\n        \nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.UseSession();\n\n<b class=\"fm-bold\">app.UseAuthentication();<\/b>\n<b class=\"fm-bold\">app.UseAuthorization();<\/b>\n\napp.MapControllerRoute(\"catpage\",\n    \"{category}\/Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\" });\n        \napp.MapControllerRoute(\"page\", \"Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"category\", \"{category}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"pagination\",\n    \"Products\/Page{productPage}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapDefaultControllerRoute();\napp.MapRazorPages();\napp.MapBlazorHub();\napp.MapFallbackToPage(\"\/admin\/{*catchall}\", \"\/Admin\/Index\");\n\nSeedData.EnsurePopulated(app);\n\napp.Run();<\/pre>\n<p class=\"body\">In the listing, I extended the Entity Framework Core configuration to register the context class and used the <code class=\"fm-code-in-text\">AddIdentity<\/code> method to set up the Identity services using the built-in classes to represent users and roles. I called the <code class=\"fm-code-in-text\">UseAuthentication<\/code> and <code class=\"fm-code-in-text\">UseAuthorization<\/code> methods to set up the middleware components that implement the security policy.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-209\">11.1.5 Creating and applying the database migration<\/h3>\n<p class=\"body\">The basic configuration is in place, and it is time to use the Entity Framework Core migrations feature to define the schema and apply it to the database. Open a new command prompt or PowerShell window and run the command shown in listing 11.5 in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder to create a new migration for the Identity database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.5 Creating the identity migration<\/p>\n<pre class=\"programlisting\">dotnet ef migrations add Initial --context AppIdentityDbContext <\/pre>\n<p class=\"body\">The important difference from previous database commands is that I have used the <code class=\"fm-code-in-text\">-context<\/code> argument to specify the name of the context class associated with the database that I want to work with, which is <code class=\"fm-code-in-text\">AppIdentityDbContext<\/code>. When you have multiple databases in the application, it is important to ensure that you are working with the right context class.<\/p>\n<p class=\"body\">Once Entity Framework Core has generated the initial migration, run the command shown in listing 11.6 in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder to create the database and apply the migration.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.6 Applying the identity migration<\/p>\n<pre class=\"programlisting\">dotnet ef database update --context AppIdentityDbContext<\/pre>\n<p class=\"body\">The result is a new LocalDB database called <code class=\"fm-code-in-text\">Identity<\/code> that you can inspect using the Visual Studio SQL Server Object Explorer.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-210\">11.1.6 Defining the seed data<\/h3>\n<p class=\"body\">I am going to explicitly create the <code class=\"fm-code-in-text\">Admin<\/code> user by seeding the database when the application starts. I added a class file called <code class=\"fm-code-in-text\">IdentitySeedData.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and defined the static class shown in listing 11.7.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.7 The IdentitySeedData.cs file in the SportsStore\/Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Identity;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace SportsStore.Models {\n\n    public static class IdentitySeedData {\n        private const string adminUser = \"Admin\";\n        private const string adminPassword = \"Secret123$\";\n                \n        public static async void EnsurePopulated(\n            IApplicationBuilder app) {\n                        \n            AppIdentityDbContext context = app.ApplicationServices\n                .CreateScope().ServiceProvider\n                .GetRequiredService&lt;AppIdentityDbContext&gt;();\n            if (context.Database.GetPendingMigrations().Any()) {\n                context.Database.Migrate();\n            }\n                        \n            UserManager&lt;IdentityUser&gt; userManager = \n                app.ApplicationServices\n                .CreateScope().ServiceProvider\n                .GetRequiredService&lt;UserManager&lt;IdentityUser&gt;&gt;();\n                                \n            IdentityUser? user = \n                await userManager.FindByNameAsync(adminUser);\n            if (user == null) {\n                user = new IdentityUser(\"Admin\");\n                user.Email = \"admin@example.com\";\n                user.PhoneNumber = \"555-1234\";\n                await userManager.CreateAsync(user, adminPassword);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">This code ensures the database is created and up-to-date and uses the <code class=\"fm-code-in-text\">UserManager&lt;T&gt;<\/code> class, which is provided as a service by ASP.NET Core Identity for managing users, as described in chapter 38. The database is searched for the <code class=\"fm-code-in-text\">Admin<\/code> user account, which is created&mdash;with a password of <code class=\"fm-code-in-text\">Secret123$<\/code>&mdash;if it is not present. Do not change the hard-coded password in this example because Identity has a validation policy that requires passwords to contain a number and range of characters. See chapter 38 for details of how to change the validation settings.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Hard-coding the details of an administrator account is often required so that you can log into an application once it has been deployed and start administering it. When you do this, you must remember to change the password for the account you have created. See chapter 38 for details of how to change passwords using Identity. See chapter 15 for how to keep sensitive data, such as default passwords, out of source code control.<\/p>\n<p class=\"body\">To ensure that the Identity database is seeded when the application starts, I added the statement shown in listing 11.8 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.8 Seeding the identity database in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Identity;\nusing Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\nbuilder.Services.AddScoped&lt;IOrderRepository, EFOrderRepository&gt;();\n\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddDistributedMemoryCache();\nbuilder.Services.AddSession();\nbuilder.Services.AddScoped&lt;Cart&gt;(sp =&gt; SessionCart.GetCart(sp));\nbuilder.Services.AddSingleton&lt;IHttpContextAccessor, \n    HttpContextAccessor&gt;();\nbuilder.Services.AddServerSideBlazor();\n\nbuilder.Services.AddDbContext&lt;AppIdentityDbContext&gt;(options =&gt;\n    options.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:IdentityConnection\"]));\nbuilder.Services.AddIdentity&lt;IdentityUser, IdentityRole&gt;()\n    .AddEntityFrameworkStores&lt;AppIdentityDbContext&gt;();\n        \nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.UseSession();\n\napp.UseAuthentication();\napp.UseAuthorization();\n\napp.MapControllerRoute(\"catpage\",\n    \"{category}\/Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\" });\n        \napp.MapControllerRoute(\"page\", \"Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"category\", \"{category}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"pagination\",\n    \"Products\/Page{productPage}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapDefaultControllerRoute();\napp.MapRazorPages();\napp.MapBlazorHub();\napp.MapFallbackToPage(\"\/admin\/{*catchall}\", \"\/Admin\/Index\");\n\nSeedData.EnsurePopulated(app);\n<b class=\"fm-bold\">IdentitySeedData.EnsurePopulated(app);<\/b>\n\napp.Run();<\/pre>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Deleting and re-creating the ASP.NET Core Identity database<\/p>\n<p class=\"fm-sidebar-text\">If you need to reset the Identity database, then run the following command:<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force --context AppIdentityDbContext<\/pre>\n<p class=\"fm-sidebar-text\">Restart the application, and the database will be re-created and populated with seed data.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-211\">11.2 Adding a conventional administration feature<\/h2>\n<p class=\"body\">In chapter 10, I used Blazor to create the administration features so that I could demonstrate a wide range of ASP.NET Core features in the SportsStore project. Although Blazor is useful, it is not suitable for all projects&mdash;as I explain in part 4&mdash;and most projects are likely to use controllers or Razor Pages for their administration features. I describe the way that ASP.NET Core Identity works with all the application frameworks in chapter 38, but just to provide a balance to the all-Blazor tools created in chapter 10, I am going to create a Razor Page that will display the list of users in the ASP.NET Core Identity database. I describe how to manage the Identity database in more detail in chapter 38, and this Razor Page is just to add a sensitive feature to the SportsStore application that isn\u2019t created with Blazor. Add a Razor Page named <code class=\"fm-code-in-text\">IdentityUsers.cshtml<\/code> to the <code class=\"fm-code-in-text\">SportsStore\/Pages\/Admin<\/code> folder with the contents shown in listing 11.9.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.9 The IdentityUsers.cshtml file in the SportsStore\/Pages\/Admin folder<\/p>\n<pre class=\"programlisting\">@page\n@model IdentityUsersModel\n@using Microsoft.AspNetCore.Identity\n\n&lt;h3 class=\"bg-primary text-white text-center p-2\"&gt;Admin User&lt;\/h3&gt;\n\n&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;tbody&gt;\n        &lt;tr&gt;&lt;th&gt;User&lt;\/th&gt;&lt;td&gt;@Model.AdminUser?.UserName&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Email&lt;\/th&gt;&lt;td&gt;@Model.AdminUser?.Email&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Phone&lt;\/th&gt;&lt;td&gt;@Model.AdminUser?.PhoneNumber&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n@functions {\n\n    public class IdentityUsersModel : PageModel {\n        private UserManager&lt;IdentityUser&gt; userManager;\n                \n        public IdentityUsersModel(UserManager&lt;IdentityUser&gt; mgr) {\n            userManager = mgr;\n        }\n                \n        public IdentityUser? AdminUser { get; set; } = new();\n                \n        public async Task OnGetAsync() {\n            AdminUser = await userManager.FindByNameAsync(\"Admin\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/admin\/identityusers to see the content generated by the Razor Page, which is shown in figure 11.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre84\" src=\"\/images\/proaspnetcore7\/000079.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 11.1 A Razor Page administration feature<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-212\">11.3 Applying a basic authorization policy<\/h2>\n<p class=\"body\">Now that I have configured ASP.NET Core Identity, I can apply an authorization policy to the parts of the application that I want to protect. I am going to use the most basic authorization policy possible, which is to allow access to any authenticated user. Although this can be a useful policy in real applications as well, there are also options for creating finer-grained authorization controls, as described in chapters 37 and 38, but since the SportsStore application has only one user, distinguishing between anonymous and authenticated requests is sufficient.<\/p>\n<p class=\"body\">For controllers and Razor pages, the <code class=\"fm-code-in-text\">Authorize<\/code> attribute is used to restrict access, as shown in listing 11.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.10 Restricting access in the IdentityUsers.cshtml file in the SportsStore\/Pages\/Admin folder<\/p>\n<pre class=\"programlisting\">@page\n@model IdentityUsersModel\n@using Microsoft.AspNetCore.Identity\n<b class=\"fm-bold\">@using Microsoft.AspNetCore.Authorization<\/b>\n\n&lt;h3 class=\"bg-primary text-white text-center p-2\"&gt;Admin User&lt;\/h3&gt;\n\n&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;tbody&gt;\n        &lt;tr&gt;&lt;th&gt;User&lt;\/th&gt;&lt;td&gt;@Model.AdminUser?.UserName&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Email&lt;\/th&gt;&lt;td&gt;@Model.AdminUser?.Email&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Phone&lt;\/th&gt;&lt;td&gt;@Model.AdminUser?.PhoneNumber&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n@functions {\n\n    <b class=\"fm-bold\">[Authorize]<\/b>\n    public class IdentityUsersModel : PageModel {\n        private UserManager&lt;IdentityUser&gt; userManager;\n        public IdentityUsersModel(UserManager&lt;IdentityUser&gt; mgr) {\n            userManager = mgr;\n        }\n                \n        public IdentityUser? AdminUser { get; set; } = new();\n                \n        public async Task OnGetAsync() {\n            AdminUser = await userManager.FindByNameAsync(\"Admin\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">When there are only authorized and unauthorized users, the <code class=\"fm-code-in-text\">Authorize<\/code> attribute can be applied to the Razor Page that acts as the entry point for the Blazor part of the application, as shown in listing 11.11.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.11 Applying authorization in the Index.cshtml file in the SportsStore\/Pages\/Admin folder<\/p>\n<pre class=\"programlisting\">@page \"\/admin\"\n@{  Layout = null; }\n<b class=\"fm-bold\">@using Microsoft.AspNetCore.Authorization<\/b>\n<b class=\"fm-bold\">@attribute [Authorize]<\/b>\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;SportsStore Admin&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    &lt;base href=\"\/\" \/&gt;  \n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;component type=\"typeof(Routed)\" render-mode=\"Server\" \/&gt;\n    &lt;script src=\"\/_framework\/blazor.server.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Since this Razor Page has been configured without a page model class, I can apply the attribute with an <code class=\"fm-code-in-text\">@attribute<\/code> expression.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-213\">11.4 Creating the account controller and views<\/h2>\n<p class=\"body\">When an unauthenticated user sends a request that requires authorization, the user is redirected to the <code class=\"fm-code-in-text\">\/Account\/Login<\/code> URL, which the application can use to prompt the user for their credentials. In chapters 38 and 39, I show you how to handle authentication using Razor Pages, so, for variety, I am going to use controllers and views for SportsStore. In preparation, I added a view model to represent the user\u2019s credentials by adding a class file called <code class=\"fm-code-in-text\">LoginModel.cs<\/code> to the <code class=\"fm-code-in-text\">Models\/ViewModels<\/code> folder and using it to define the class shown in listing 11.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.12 The LoginModel.cs file in the SportsStore\/Models\/ViewModels folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\n\nnamespace SportsStore.Models.ViewModels {\n\n    public class LoginModel {\n        \n        public required string Name { get; set; }\n                \n        public required string Password { get; set; }\n                \n        public string ReturnUrl { get; set; } = \"\/\";\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Password<\/code> properties have been decorated with the <code class=\"fm-code-in-text\">Required<\/code> attribute, which uses model validation to ensure that values have been provided. Next, I added a class file called <code class=\"fm-code-in-text\">AccountController.cs<\/code> to the <code class=\"fm-code-in-text\">Controllers<\/code> folder and used it to define the controller shown in listing 11.13. This is the controller that will respond to requests to the <code class=\"fm-code-in-text\">\/Account\/Login<\/code> URL.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.13 The AccountController.cs file in the SportsStore\/Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Authorization;\nusing Microsoft.AspNetCore.Identity;\nusing Microsoft.AspNetCore.Mvc;\nusing SportsStore.Models.ViewModels;\n\nnamespace SportsStore.Controllers {\n\n    public class AccountController : Controller {\n        private UserManager&lt;IdentityUser&gt; userManager;\n        private SignInManager&lt;IdentityUser&gt; signInManager;\n                \n        public AccountController(UserManager&lt;IdentityUser&gt; userMgr,\n                SignInManager&lt;IdentityUser&gt; signInMgr) {\n            userManager = userMgr;\n            signInManager = signInMgr;\n        }\n                \n        public ViewResult Login(string returnUrl) {\n            return View(new LoginModel {\n                Name = string.Empty, Password = string.Empty,\n                ReturnUrl = returnUrl\n            });\n        }\n                \n        [HttpPost]\n        [ValidateAntiForgeryToken]\n        public async Task&lt;IActionResult&gt; Login(LoginModel loginModel) {\n            if (ModelState.IsValid) {\n                IdentityUser? user =\n                    await userManager.FindByNameAsync(loginModel.Name);\n                if (user != null) {\n                    await signInManager.SignOutAsync();\n                    if ((await signInManager.PasswordSignInAsync(user,\n                        loginModel.Password, false, false)).Succeeded) {\n                            return Redirect(loginModel?.ReturnUrl \n                                ?? \"\/Admin\");\n                    }\n                }\n                ModelState.AddModelError(\"\", \"Invalid name or password\");\n            }\n            return View(loginModel);\n        }\n                \n        [Authorize]\n        public async Task&lt;RedirectResult&gt; Logout(string returnUrl = \"\/\") {\n            await signInManager.SignOutAsync();\n            return Redirect(returnUrl);\n        }\n    }\n}<\/pre>\n<p class=\"body\">When the user is redirected to the <code class=\"fm-code-in-text\">\/Account\/Login<\/code> URL, the GET version of the <code class=\"fm-code-in-text\">Login<\/code> action method renders the default view for the page, providing a view model object that includes the URL to which the browser should be redirected if the authentication request is successful.<\/p>\n<p class=\"body\">Authentication credentials are submitted to the POST version of the <code class=\"fm-code-in-text\">Login<\/code> method, which uses the <code class=\"fm-code-in-text\">UserManager&lt;IdentityUser&gt;<\/code> and <code class=\"fm-code-in-text\">SignInManager&lt;IdentityUser&gt;<\/code> services that have been received through the controller\u2019s constructor to authenticate the user and log them into the system. I explain how these classes work in chapters 37 and 38, but for now, it is enough to know that if there is an authentication failure, then I create a model validation error and render the default view; however, if authentication is successful, then I redirect the user to the URL that they want to access before they are prompted for their credentials.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> In general, using client-side data validation is a good idea. It offloads some of the work from your server and gives users immediate feedback about the data they are providing. However, you should not be tempted to perform authentication at the client, as this would typically involve sending valid credentials to the client so they can be used to check the username and password that the user has entered, or at least trusting the client\u2019s report of whether they have successfully authenticated. Authentication should always be done at the server.<\/p>\n<p class=\"body\">To provide the <code class=\"fm-code-in-text\">Login<\/code> method with a view to render, I created the <code class=\"fm-code-in-text\">Views\/Account<\/code> folder and added a Razor View file called <code class=\"fm-code-in-text\">Login.cshtml<\/code> with the contents shown in listing 11.14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.14 The Login.cshtml file in the SportsStore\/Views\/Account folder<\/p>\n<pre class=\"programlisting\">@model LoginModel\n@{\n    Layout = null;\n}\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;title&gt;SportsStore&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-dark text-white p-2\"&gt;\n        &lt;span class=\"navbar-brand ml-2\"&gt;SPORTS STORE&lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"m-1 p-1\"&gt;\n        &lt;div class=\"text-danger\" asp-validation-summary=\"All\"&gt;&lt;\/div&gt;\n        &lt;form asp-action=\"Login\" asp-controller=\"Account\" method=\"post\"&gt;\n            &lt;input type=\"hidden\" asp-for=\"ReturnUrl\" \/&gt;\n            &lt;div class=\"form-group\"&gt;\n                &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n                &lt;input asp-for=\"Name\" class=\"form-control\" \/&gt;\n            &lt;\/div&gt;\n            &lt;div class=\"form-group\"&gt;\n                &lt;label asp-for=\"Password\"&gt;&lt;\/label&gt;\n                &lt;input asp-for=\"Password\" type=\"password\" \n                    class=\"form-control\" \/&gt;\n            &lt;\/div&gt;\n            &lt;button class=\"btn btn-primary mt-2\" type=\"submit\"&gt;\n                Log In\n            &lt;\/button&gt;\n        &lt;\/form&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The final step is a change to the shared administration layout to add a button that will log out the current user by sending a request to the <code class=\"fm-code-in-text\">Logout<\/code> action, as shown in listing 11.15. This is a useful feature that makes it easier to test the application, without which you would need to clear the browser\u2019s cookies to return to the unauthenticated state.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.15 Adding a logout button in the AdminLayout.razor file in the SportsStore\/Pages\/Admin Folder<\/p>\n<pre class=\"programlisting\">@inherits LayoutComponentBase\n\n&lt;div class=\"bg-info text-white p-2\"&gt;\n    <b class=\"fm-bold\">&lt;div class=\"container-fluid\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"row\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;div class=\"col\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;span class=\"navbar-brand ml-2\"&gt;<\/b>\n                    <b class=\"fm-bold\">SPORTS STORE Administration<\/b>\n                <b class=\"fm-bold\">&lt;\/span&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;div class=\"col-2 text-right\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;a class=\"btn btn-sm btn-primary\" href=\"\/account\/logout\"&gt;<\/b>\n                    <b class=\"fm-bold\">Log Out<\/b>\n                <b class=\"fm-bold\">&lt;\/a&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n&lt;\/div&gt;\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row p-2\"&gt;\n        &lt;div class=\"col-3\"&gt;\n            &lt;div class=\"d-grid gap-1\"&gt;\n                &lt;NavLink class=\"btn btn-outline-primary\"\n                         href=\"\/admin\/products\"\n                         ActiveClass=\"btn-primary text-white\"\n                         Match=\"NavLinkMatch.Prefix\"&gt;\n                    Products\n                &lt;\/NavLink&gt;\n                &lt;NavLink class=\"btn btn-outline-primary\"\n                         href=\"\/admin\/orders\"\n                         ActiveClass=\"btn-primary text-white\"\n                         Match=\"NavLinkMatch.Prefix\"&gt;\n                    Orders\n                &lt;\/NavLink&gt;\n            &lt;\/div&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            @Body\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-214\">11.5 Testing the security policy<\/h2>\n<p class=\"body\">Everything is in place, and you can test the security policy by restarting ASP.NET Core and requesting http:\/\/localhost:5000\/admin or http:\/\/localhost:5000\/admin\/identityusers.<\/p>\n<p class=\"body\">Since you are presently unauthenticated and you are trying to target an action that requires authorization, your browser will be redirected to the <code class=\"fm-code-in-text\">\/Account\/Login<\/code> URL. Enter <code class=\"fm-code-in-text\">Admin<\/code> and <code class=\"fm-code-in-text\">Secret123$<\/code> as the name and password and submit the form. The <code class=\"fm-code-in-text\">Account<\/code> controller will check the credentials you provided with the seed data added to the Identity database and&mdash;assuming you entered the right details&mdash;authenticate you and redirect you to the URL you requested, to which you now have access. Figure 11.2 illustrates the process.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre85\" src=\"\/images\/proaspnetcore7\/000080.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 11.2 The administration authentication\/authorization process<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-215\">11.6 Preparing ASP.NET Core for deployment<\/h2>\n<p class=\"body\">In this section, I will prepare SportsStore and create a container that can be deployed into production. There is a wide range of deployment models available for ASP.NET Core applications, but I have picked Docker containers because they can be run on most hosting platforms or be deployed into a private data center. This is not a complete guide to deployment, but it will give you a sense of the process to prepare an application.<a id=\"calibre_link-1653\"><\/a><a id=\"calibre_link-1654\"><\/a><a id=\"calibre_link-1655\"><\/a><a id=\"calibre_link-890\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-216\">11.6.1 Configuring error handling<\/h3>\n<p class=\"body\">At the moment, the application is configured to use the developer-friendly error pages, which provide helpful information when a problem occurs. This is not information that end users should see, so I added a Razor Page named <code class=\"fm-code-in-text\">Error.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 11.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.16 The contents of the Error.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/error\"\n@{\n    Layout = null;\n}\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width\" \/&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;    \n    &lt;title&gt;Error&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body class=\"text-center\"&gt;\n    &lt;h2 class=\"text-danger\"&gt;Error.&lt;\/h2&gt;\n    &lt;h3 class=\"text-danger\"&gt;\n        An error occurred while processing your request\n    &lt;\/h3&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">This kind of error page is the last resort, and it is best to keep it as simple as possible and not to rely on shared views, view components, or other rich features. In this case, I have disabled shared layouts and defined a simple HTML document that explains that there has been an error, without providing any information about what has happened.<\/p>\n<p class=\"body\">In listing 11.17, I have reconfigured the application so that the <code class=\"fm-code-in-text\">Error<\/code> page is used for unhandled exceptions when the application is in the production environment. I have also set the locale, which is required when deploying to a Docker container. The locale I have chosen is <code class=\"fm-code-in-text\">en-US<\/code>, which represents the language and currency conventions of English as it is spoken in the United States.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.17 Configuring error handling in the Program.cs file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Identity;\nusing Microsoft.EntityFrameworkCore;\nusing SportsStore.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\n\nbuilder.Services.AddDbContext&lt;StoreDbContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:SportsStoreConnection\"]);\n});\n\nbuilder.Services.AddScoped&lt;IStoreRepository, EFStoreRepository&gt;();\nbuilder.Services.AddScoped&lt;IOrderRepository, EFOrderRepository&gt;();\n\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddDistributedMemoryCache();\nbuilder.Services.AddSession();\nbuilder.Services.AddScoped&lt;Cart&gt;(sp =&gt; SessionCart.GetCart(sp));\nbuilder.Services.AddSingleton&lt;IHttpContextAccessor, \n    HttpContextAccessor&gt;();\nbuilder.Services.AddServerSideBlazor();\n\nbuilder.Services.AddDbContext&lt;AppIdentityDbContext&gt;(options =&gt;\n    options.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:IdentityConnection\"]));\nbuilder.Services.AddIdentity&lt;IdentityUser, IdentityRole&gt;()\n    .AddEntityFrameworkStores&lt;AppIdentityDbContext&gt;();\n        \nvar app = builder.Build();\n\n<b class=\"fm-bold\">if (app.Environment.IsProduction()) {<\/b>\n    <b class=\"fm-bold\">app.UseExceptionHandler(\"\/error\");<\/b>\n<b class=\"fm-bold\">}<\/b>\n\n<b class=\"fm-bold\">app.UseRequestLocalization(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.AddSupportedCultures(\"en-US\")<\/b>\n    <b class=\"fm-bold\">.AddSupportedUICultures(\"en-US\")<\/b>\n    <b class=\"fm-bold\">.SetDefaultCulture(\"en-US\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.UseStaticFiles();\napp.UseSession();\n\napp.UseAuthentication();\napp.UseAuthorization();\n\napp.MapControllerRoute(\"catpage\",\n    \"{category}\/Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\" });\n        \napp.MapControllerRoute(\"page\", \"Page{productPage:int}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"category\", \"{category}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapControllerRoute(\"pagination\",\n    \"Products\/Page{productPage}\",\n    new { Controller = \"Home\", action = \"Index\", productPage = 1 });\n        \napp.MapDefaultControllerRoute();\napp.MapRazorPages();\napp.MapBlazorHub();\napp.MapFallbackToPage(\"\/admin\/{*catchall}\", \"\/Admin\/Index\");\n\nSeedData.EnsurePopulated(app);\nIdentitySeedData.EnsurePopulated(app);\n\napp.Run();<\/pre>\n<p class=\"body\">As I explain in chapter 12, the <code class=\"fm-code-in-text\">IWebHostEnvironment<\/code> interface describes the environment in which the application is running. The changes mean that the <code class=\"fm-code-in-text\">UseExceptionHandler<\/code> method is called when the application is in production, but the developer-friendly error pages are used otherwise.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-217\">11.6.2 Creating the production configuration settings<\/h3>\n<p class=\"body\">The JSON configuration files that are used to define settings such as connection strings can be created so they apply only when the application is in a specific environment, such as development, staging, or production. The template I used to create the SportsStore project in chapter 7 created the <code class=\"fm-code-in-text\">appsettings.json<\/code> and <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> files, which are intended to be the default settings that are overridden with those that are specific for development. I am going to take the reverse approach for this chapter and define a file that contains just those settings that are specific to production. Add a JSON file named <code class=\"fm-code-in-text\">appsettings.Production.json<\/code> to the <code class=\"fm-code-in-text\">SportsStore<\/code> folder with the content shown in listing 11.18.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Do not use these connection strings in real projects. You must correctly describe the connection to your production database, which is unlikely to be the same as the ones in the listing.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.18 The appsettings.Production.json file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">{\n  \"ConnectionStrings\": {\n    \"SportsStoreConnection\": \"Server=sqlserver;Database=SportsStore; \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>MultipleActiveResultSets=true;User=sa;Password=MyDatabaseSecret123;Encrypt=False\",\n    \"IdentityConnection\": \"Server=sqlserver;Database=Identity; \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>MultipleActiveResultSets=true;User=sa;Password=MyDatabaseSecret123;Encrypt=False\"\n  }\n}<\/pre>\n<p class=\"body\">These connection strings, each of which is defined on a single line, describe connections to SQL Server running on <code class=\"fm-code-in-text\">sqlserver<\/code>, which is another Docker container running SQL Server. For simplicity, I have disabled encryption for the connections to the database.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-218\">11.6.3 Creating the Docker image<\/h3>\n<p class=\"body\">In the sections that follow, I configure and create the Docker image for the application that can be deployed into a container environment such as Microsoft Azure or Amazon Web Services. Bear in mind that containers are only one style of deployment and there are many others available if this approach does not suit you.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Bear in mind that I am going to connect to a database running on the development machine, which is not how most real applications are configured. Be sure to configure the database connection strings and the container networking settings to match your production environment.<\/p>\n<p class=\"fm-head2\">Installing Docker Desktop<\/p>\n<p class=\"body\">Go to <a class=\"url\" href=\"https:\/\/docker.com\">https:\/\/docker.com<\/a> and download and install the Docker Desktop package. Follow the installation process, reboot your Windows machine, and run the command shown in listing 11.19 to check that Docker has been installed and is in your path. (The Docker installation process seems to change often, which is why I have not been more specific about the process.)<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> You will have to create an account on Docker.com to download the installer.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.19 Checking the Docker Desktop installation<\/p>\n<pre class=\"programlisting\">docker --version<\/pre>\n<p class=\"fm-head2\">Creating the Docker configuration files<\/p>\n<p class=\"body\">Docker is configured using a file named <code class=\"fm-code-in-text\">Dockerfile<\/code>. There is no Visual Studio item template for this file, so use the Text File template to add a file named <code class=\"fm-code-in-text\">Dockerfile.text<\/code> to the project and then rename the file to <code class=\"fm-code-in-text\">Dockerfile<\/code>. If you are using Visual Studio Code, you can just create a file named <code class=\"fm-code-in-text\">Dockerfile<\/code> without the extension. Use the configuration settings shown in listing 11.20 as the contents for the new file.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Creating container images without Docker files.<\/p>\n<p class=\"fm-sidebar-text\">Microsoft has introduced features in .NET for creating application images without needing a Dockerfile, with the details of the configuration specified in the CSPROJ file instead. This is an interesting idea, but my view is that the configuration of the image is too important to leave to automated tools, and is something that should be understood by the developer because misconfiguration can impact seriously the performance of the application. For this reason, I have created a Dockerfile in this chapter, and this is what I do in my own projects. You can learn more about this feature at <a class=\"url\" href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-builtin-container-support-for-the-dotnet-sdk\">https:\/\/devblogs.microsoft.com\/dotnet\/announcing-builtin-container-support-for-the-dotnet-sdk<\/a>.<\/p>\n<\/div>\n<p class=\"fm-code-listing-caption\">Listing 11.20 The contents of the Dockerfile File in the SportsStore folder<\/p>\n<pre class=\"programlisting\">FROM mcr.microsoft.com\/dotnet\/aspnet:7.0\n\nCOPY \/bin\/Release\/net7.0\/publish\/ SportsStore\/\n\nENV ASPNETCORE_ENVIRONMENT Production\nENV Logging__Console__FormatterName=Simple\n\nEXPOSE 5000\nWORKDIR \/SportsStore\nENTRYPOINT [\"dotnet\", \"SportsStore.dll\",  \"--urls=http:\/\/0.0.0.0:5000\"]<\/pre>\n<p class=\"body\">These instructions copy the SportsStore application into a Docker image and configure its execution. Next, create a file called <code class=\"fm-code-in-text\">docker-compose.yml<\/code> with the content shown in listing 11.21. Visual Studio doesn\u2019t have a template for this type of file, but if you select the Text File template and enter the complete file name, it will create the file. Visual Studio Code users can simply create a file named <code class=\"fm-code-in-text\">docker-compose.yml<\/code>.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The configuration file in listing 11.21 is used by Docker Compose, which creates containers and lets them work together. Docker Compose has been eclipsed by Kubernetes, which has become the most popular choice for deploying containers into production. Kubernetes is incredibly complex, however, and Docker Compose remains a good choice for working with simple applications, especially during development and testing.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.21 The contents of the docker-compose.yml file in the SportsStore folder<\/p>\n<pre class=\"programlisting\">version: \"3\"\nservices:\n    sportsstore:\n        build: .\n        ports:\n            - \"5000:5000\"\n        environment:\n            - ASPNETCORE_ENVIRONMENT=Production\n        depends_on:\n            - sqlserver\n    sqlserver:\n        image: \"mcr.microsoft.com\/mssql\/server\"\n        environment:\n            SA_PASSWORD: \"MyDatabaseSecret123\"\n            ACCEPT_EULA: \"Y\"<\/pre>\n<p class=\"body\">The YML files are especially sensitive to formatting and indentation, and it is important to create this file exactly as shown. If you have problems, then use the <code class=\"fm-code-in-text\">docker-compose.yml<\/code> file from the GitHub repository for this book.<\/p>\n<p class=\"fm-head2\">Publishing and imaging the application<\/p>\n<p class=\"body\">Prepare the SportsStore application by using a PowerShell prompt to run the command shown listing 11.22 in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.22 Preparing the application<\/p>\n<pre class=\"programlisting\">dotnet publish -c Release<\/pre>\n<p class=\"body\">Next, run the command shown in listing 11.23 to create the Docker image for the SportsStore application. This command will take some time to complete the first time it is run because it will download the Docker images for ASP.NET Core.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.23 Performing the Docker build<\/p>\n<pre class=\"programlisting\">docker-compose build<\/pre>\n<p class=\"body\">The first time you run this command, you may be prompted to allow Docker to use the network, as shown in figure 11.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre86\" src=\"\/images\/proaspnetcore7\/000081.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 11.3 Granting network access<\/p>\n<\/div>\n<p class=\"body\">Click the Allow button, return to the PowerShell prompt, use Control+C to terminate the Docker containers, and run the command in listing 11.23 again.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-219\">11.6.4 Running the containerized application<\/h3>\n<p class=\"body\">Run the command shown in listing 11.24 in the <code class=\"fm-code-in-text\">SportsStore<\/code> folder to start the Docker containers for SQL Server.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.24 Starting the database container<\/p>\n<pre class=\"programlisting\">docker-compose up sqlserver<\/pre>\n<p class=\"body\">This command will take some time to complete the first time it is run because it will download the Docker images for SQL Server. You will see a large amount of output as SQL Server starts up. Once the database is running, use a separate command prompt to run the command shown in listing 11.25 to start the container for the SportsStore application.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 11.25 Starting the SportsStore container<\/p>\n<pre class=\"programlisting\">docker-compose up sportsstore<\/pre>\n<p class=\"body\">The application will be ready when you see output like this:<\/p>\n<pre class=\"programlisting\">...\nsportsstore_1  | info: Microsoft.Hosting.Lifetime[0]\nsportsstore_1  |       Now listening on: http:\/\/0.0.0.0:5000\nsportsstore_1  | info: Microsoft.Hosting.Lifetime[0]\nsportsstore_1  |       Application started. Press Ctrl+C to shut down.\nsportsstore_1  | info: Microsoft.Hosting.Lifetime[0]\nsportsstore_1  |       Hosting environment: Production\nsportsstore_1  | info: Microsoft.Hosting.Lifetime[0]\nsportsstore_1  |       Content root path: \/SportsStore\n...<\/pre>\n<p class=\"body\">Open a new browser window and request http:\/\/localhost:5000, and you will receive a response from the containerized version of SportsStore, as shown in figure 11.4, which is now ready for deployment. Use Control+C at the PowerShell command prompts to terminate the Docker containers.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre87\" src=\"\/images\/proaspnetcore7\/000082.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 11.4 Running the SportsStore application in a container<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-220\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core applications use ASP.NET Core Identity for user authentication and have built-in support for enforcing authorization using attributes.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Applications are published to prepare them for deployment, using the <code class=\"fm-code-in-text\">dotnet publish<\/code> command, specifying the environment name to ensure the correct configuration settings are used.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Applications can be deployed into containers, which can be used on most hosting platforms or hosted locally within a data center.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-221\">\n<div class=\"calibre1\" id=\"calibre_link-1656\">\n<h1 class=\"tochead\" id=\"calibre_link-1657\">Part 2.<\/h1>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-222\">\n<div class=\"calibre1\" id=\"calibre_link-1658\">\n<h1 class=\"tochead\" id=\"calibre_link-1659\">12 Understanding the ASP.NET Core platform<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-1660\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Understanding the basic structure of an ASP. NET Core application<\/li>\n<li class=\"co-summary-bullet\">Understanding the HTTP request processing pipeline and middleware components<\/li>\n<li class=\"co-summary-bullet\">Creating custom middleware components<\/li>\n<\/ul>\n<p class=\"body\">The ASP.NET Core platform is the foundation for creating web applications; it provides the features that make it possible to use frameworks like MVC and Blazor. In this chapter, I explain how the basic ASP.NET Core features work, describe the purpose of the files in an ASP.NET Core project, and explain how the ASP.NET Core request pipeline is used to process HTTP requests and demonstrate the different ways that it can be customized.<\/p>\n<p class=\"body\">Don\u2019t worry if not everything in this chapter makes immediate sense or appears to apply to the applications you intend to create. The features I describe in this chapter are the underpinnings for everything that ASP.NET Core does, and understanding how they work helps provide a context for understanding the features that you will use daily, as well as giving you the knowledge you need to diagnose problems when you don\u2019t get the behavior you expect. Table 12.1 puts the ASP.NET Core platform in context.<\/p>\n<p class=\"fm-table-caption\"><a id=\"calibre_link-1661\"><\/a>Table 12.1 Putting the ASP.NET Core platform in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1662\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"25%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"75%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What is it?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The ASP.NET Core platform is the foundation on which web applications are built and provides features for processing HTTP requests.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why is it useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The ASP.NET Core platform takes care of the low-level details of web applications so that developers can focus on features for the end user.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How is it used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The key building blocks are services and middleware components, both of which can be created using top-level statements in the <code class=\"fm-code-in-text1\">Program.cs<\/code> file.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The use of the <code class=\"fm-code-in-text1\">Program.cs<\/code> file can be confusing, and close attention must be paid to the order of the statements it contains.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The ASP.NET Core platform is required for ASP.NET Core applications, but you can choose not to work with the platform directly and rely on just the higher-level ASP.NET Core features, which are described in later chapters.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 12.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 12.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1663\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a middleware component<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Call the <code class=\"fm-code-in-text1\">Use<\/code> or <code class=\"fm-code-in-text1\">UseMiddleware<\/code> method to add a function or class to the request pipeline.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">6&ndash;8<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Modifying a response<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Write a middleware component that uses the return pipeline path.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">9<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Preventing other components from processing a request<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Short-circuit the request pipeline or create terminal middleware.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">10, 12&ndash;14<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using different sets of middleware<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create a pipeline branch.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">11<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Configuring middleware components<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the options pattern.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">15&ndash;18<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-223\">12.1 Preparing for this chapter<\/h2>\n<p class=\"body\">To prepare for this chapter, <a id=\"calibre_link-1664\"><\/a>I am going to create a new project named Platform, using the template that provides the minimal ASP.NET Core setup. Open a new PowerShell command prompt from the Windows Start menu and run the commands shown in listing 12.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.1 Creating the project<\/p>\n<pre class=\"programlisting\">dotnet new globaljson --sdk-version 7.0.100 --output Platform\ndotnet new web --no-https --output <a id=\"calibre_link-1665\"><\/a>Platform --framework net7.0\ndotnet new sln -o Platform\ndotnet sln Platform add Platform<\/pre>\n<p class=\"body\">If you are using Visual Studio, open the <code class=\"fm-code-in-text\">Platform.sln<\/code> file in the <code class=\"fm-code-in-text\">Platform<\/code> folder. If you are using Visual Studio Code, open the <code class=\"fm-code-in-text\">Platform<\/code> folder. Click the Yes button when prompted to add the assets required for building and debugging the project.<\/p>\n<p class=\"body\">Open the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file in the <code class=\"fm-code-in-text\">Properties<\/code> folder and change the ports that will be used to handle HTTP requests, as shown in listing 12.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.2 Setting ports in the launchSettings.json file in the Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"Platform\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-224\">12.1.1 Running the example application<\/h3>\n<p class=\"body\">To start the application, run the command shown in listing 12.3 in the <code class=\"fm-code-in-text\">Platform<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.3 Starting the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Open a new browser window and use it to request http:\/\/localhost:5000. You will see the output shown in figure 12.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre88\" src=\"\/images\/proaspnetcore7\/000083.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-225\">12.2 Understanding the ASP.NET Core platform<\/h2>\n<p class=\"body\">To understand ASP.NET Core, it is helpful to focus on just the key features: the request pipeline, middleware, and services. Understanding how these features fit together&mdash;even without going into detail&mdash;provides useful context for understanding the contents of the ASP.NET Core project and the shape of the ASP.NET Core platform.<a id=\"calibre_link-1666\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-226\">12.2.1 Understanding middleware and the request pipeline<\/h3>\n<p class=\"body\">The purpose of the ASP.NET Core platform is to receive HTTP requests and send responses to them, which ASP.NET Core delegates to <i class=\"fm-italics\">middleware components<\/i>. Middleware components are arranged in a chain, known as the <i class=\"fm-italics\">request pipeline<\/i>.<a id=\"calibre_link-1667\"><\/a><a id=\"calibre_link-1668\"><\/a><a id=\"calibre_link-1669\"><\/a><a id=\"calibre_link-903\"><\/a><\/p>\n<p class=\"body\">When a new HTTP request arrives, the ASP.NET Core platform creates an object that describes it and a corresponding object that describes the response that will be sent in return. These objects are passed to the first middleware component in the chain, which inspects the request and modifies the response. The request is then passed to the next middleware component in the chain, with each component inspecting the request and adding to the response. Once the request has made its way through the pipeline, the ASP.NET Core platform sends the response, as illustrated in figure 12.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre89\" src=\"\/images\/proaspnetcore7\/000084.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.2 The ASP.NET Core request pipeline<\/p>\n<\/div>\n<p class=\"body\">Some components focus on generating responses for requests, but others are there to provide supporting features, such as formatting specific data types or reading and writing cookies. ASP.NET Core includes middleware components that solve common problems, as described in chapters 15 and 16, and I show how to create custom middleware components later in this chapter. If no response is generated by the middleware components, then ASP.NET Core will return a response with the HTTP 404 Not Found status code.<a id=\"calibre_link-1670\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-227\">12.2.2 Understanding services<\/h3>\n<p class=\"body\">Services are objects that provide features in a web application. Any class can be used as a service, and there are no restrictions on the features that services provide. What makes services special is that they are managed by ASP.NET Core, and a feature called <i class=\"fm-italics\">dependency injection<\/i> makes it possible to easily access services anywhere in the application, including middleware components.<a id=\"calibre_link-1671\"><\/a><a id=\"calibre_link-1672\"><\/a><\/p>\n<p class=\"body\">Dependency injection can be a difficult topic to understand, and I describe it in detail in chapter 14. For now, it is enough to know that there are objects that are managed by the ASP.NET Core platform that can be shared by middleware components, either to coordinate between components or to avoid duplicating common features, such as logging or loading configuration data, as shown in figure 12.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre90\" src=\"\/images\/proaspnetcore7\/000085.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.3 Services in the ASP.NET Core platform<\/p>\n<\/div>\n<p class=\"body\">As the figure shows, middleware components use only the services they require to do their work. As you will learn in later chapters, ASP.NET Core provides some basic services that can be supplemented by additional services that are specific to an application.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-228\">12.3 Understanding the ASP.NET Core project<\/h2>\n<p class=\"body\">The <code class=\"fm-code-in-text\">web<\/code> template produces a project with just enough code and configuration to start the ASP.NET Core runtime with some basic services and middleware components. Figure 12.4 shows the files added to the project by the template.<a id=\"calibre_link-1673\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre91\" src=\"\/images\/proaspnetcore7\/000086.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.4 The files in the example project<\/p>\n<\/div>\n<p class=\"body\">Visual Studio and Visual Studio Code take different approaches to displaying files and folders. Visual Studio hides items that are not commonly used by the developer and nests related items together, while Visual Studio Code shows everything.<\/p>\n<p class=\"body\">This is why the two project views shown in the figure are different: Visual Studio has hidden the <code class=\"fm-code-in-text\">bin<\/code> and <code class=\"fm-code-in-text\">obj<\/code> folders and nested the <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> file within the <code class=\"fm-code-in-text\">appsettings.json<\/code> file. The buttons at the top of the Solution Explorer window can be used to prevent nesting and to show all the files in the project.<\/p>\n<p class=\"body\">Although there are few files in the project, they underpin ASP.NET Core development and are described in table 12.3.<a id=\"calibre_link-1674\"><\/a><a id=\"calibre_link-1675\"><\/a><a id=\"calibre_link-1676\"><\/a><a id=\"calibre_link-1677\"><\/a><a id=\"calibre_link-1086\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 12.3 The files and folders in the Example project<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1678\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">appsettings.json<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This file is used to configure the application, as described in chapter 15.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">appsettings.Development.json<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This file is used to define configuration settings that are specific to development, as explained in chapter 15.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">bin<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This folder contains the compiled application files. Visual Studio hides this folder.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">global.json<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This file is used to select a specific version of the .NET Core SDK.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Properties\/launchSettings.json<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This file is used to configure the application when it starts.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">obj<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This folder contains the intermediate output from the compiler. Visual Studio hides this folder.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Platform.csproj<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This file describes the project to the .NET Core tools, including the package dependencies and build instructions, as described in the \u201cUnderstanding the Project File\u201d section. Visual Studio hides this file, but it can be edited by right-clicking the project item in the Solution Explorer and selecting Edit Project File from the pop-up menu.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Platform.sln<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This file is used to organize projects. Visual Studio hides this folder.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Program.cs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This file is the entry point for the ASP.NET Core platform and is used to configure the platform, as described in the \u201cUnderstanding the Entry Point\u201d section<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-229\">12.3.1 Understanding the entry point<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Program.cs<\/code> file contains the code statements that are executed when the application is started and that are used to configure the ASP.NET platform and the individual frameworks it supports. Here is the content of the <code class=\"fm-code-in-text\">Program.cs<\/code> file in the example project:<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.Run();<\/pre>\n<p class=\"body\">This file contains only top-level statements. The first statement calls the <code class=\"fm-code-in-text\">WebApplication.CreateBuilder<\/code> and assigns the result to a variable named <code class=\"fm-code-in-text\">builder<\/code>:<\/p>\n<pre class=\"programlisting\">...\nvar builder = <b class=\"fm-bold\">WebApplication.CreateBuilder<\/b>(args);\n...<\/pre>\n<p class=\"body\">This method is responsible for setting up the basic features of the ASP.NET Core platform, including creating services responsible for configuration data and logging, both of which are described in chapter 15. This method also sets up the HTTP server, named Kestrel, that is used to receive HTTP requests.<\/p>\n<p class=\"body\">The result from the <code class=\"fm-code-in-text\">CreateBuilder<\/code> method is a <code class=\"fm-code-in-text\">WebApplicationBuilder<\/code> object, which is used to register additional services, although none are defined at present. The <code class=\"fm-code-in-text\">WebApplicationBuilder<\/code> class defines a <code class=\"fm-code-in-text\">Build<\/code> method that is used to finalize the initial setup:<\/p>\n<pre class=\"programlisting\">...\nvar app = builder.<b class=\"fm-bold\">Build<\/b>();\n...<\/pre>\n<p class=\"body\">The result of the <code class=\"fm-code-in-text\">Build<\/code> method is a <code class=\"fm-code-in-text\">WebApplication<\/code> object, which is used to set up middleware components. The template has set up one middleware component, using the <code class=\"fm-code-in-text\">MapGet<\/code> extension method:<\/p>\n<pre class=\"programlisting\">...\napp.<b class=\"fm-bold\">MapGet<\/b>(\"\/\", () =&gt; \"Hello World!\");\n...<\/pre>\n<p class=\"body\"><code class=\"fm-code-in-text\">MapGet<\/code> is an extension method for the <code class=\"fm-code-in-text\">IEndpointRouteBuilder<\/code> interface, which is implemented by the <code class=\"fm-code-in-text\">WebApplication<\/code> class, and which sets up a function that will handle HTTP requests with a specified URL path. In this case, the function responds to requests for the default URL path, which is denoted by <code class=\"fm-code-in-text\">\/<\/code>, and the function responds to all requests by returning a simple <code class=\"fm-code-in-text\">string<\/code> response, which is how the output shown in figure 12.1 was produced.<\/p>\n<p class=\"body\">Most projects need a more sophisticated set of responses, and Microsoft provides middleware as part of ASP.NET Core that deals with the most common features required by web applications, which I describe in chapters 15 and 16. You can also create your own middleware, as described in the \u201cCreating Custom Middleware\u201d section, when the built-in features don\u2019t suit your requirements.<\/p>\n<p class=\"body\">The final statement in the <code class=\"fm-code-in-text\">Program.cs<\/code> file calls the <code class=\"fm-code-in-text\">Run<\/code> method defined by the <code class=\"fm-code-in-text\">WebApplication<\/code> class, which starts listening to HTTP requests.<\/p>\n<p class=\"body\">Even though the function used with the <code class=\"fm-code-in-text\">MapGet<\/code> method returns a string, ASP.NET Core is clever enough to create a valid HTTP response that will be understood by browsers. While ASP.NET Core is still running, open a new PowerShell command prompt and run the command shown in listing 12.4 to send an HTTP request to the ASP.NET Core server.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.4 Sending an HTTP Request<\/p>\n<pre class=\"programlisting\">(Invoke-WebRequest http:\/\/localhost:5000).RawContent <\/pre>\n<p class=\"body\">The output from this command shows that the response sent by ASP.NET Core contains an HTTP status code and the set of basic headers, like this:<\/p>\n<pre class=\"programlisting\">HTTP\/1.1 200 OK\nTransfer-Encoding: chunked\nContent-Type: text\/plain; charset=utf-8\nDate: Wed, 14 Dec 2022 08:09:13 GMT\nServer: Kestrel<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-230\">12.3.2 Understanding the project file<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Platform.csproj<\/code> file, known as the <i class=\"fm-italics\">project file<\/i>, contains the information that .NET Core uses to build the project and keep track of dependencies. Here is the content that was added to the file by the Empty template when the project was created:<\/p>\n<pre class=\"programlisting\">&lt;Project Sdk=\"Microsoft.NET.Sdk.Web\"&gt;\n\n  &lt;PropertyGroup&gt;\n    &lt;TargetFramework&gt;net7.0&lt;\/TargetFramework&gt;\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\n  &lt;\/PropertyGroup&gt;\n\n&lt;\/Project&gt;<\/pre>\n<p class=\"body\"><a id=\"calibre_link-870\"><\/a>The <code class=\"fm-code-in-text\">csproj<\/code> file is hidden when using Visual Studio; you can edit it by right-clicking the Platform project item in the Solution Explorer and selecting Edit Project File from the pop-up menu.<\/p>\n<p class=\"body\">The project file contains XML elements that describe the project to MSBuild, the Microsoft build engine. MSBuild can be used to create complex build processes and is described in detail at <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/msbuild\/msbuild\">https:\/\/docs.microsoft.com\/en-us\/visualstudio\/msbuild\/msbuild<\/a>.<\/p>\n<p class=\"body\">There is no need to edit the project file directly in most projects. The most common change to the file is to add dependencies on other .NET packages, but these are typically added using the command-line tools or the interface provided by Visual Studio.<a id=\"calibre_link-1679\"><\/a><a id=\"calibre_link-1680\"><\/a><\/p>\n<p class=\"body\">To add a package to the project using the command line, open a new PowerShell command prompt, navigate to the <code class=\"fm-code-in-text\">Platform<\/code> project folder (the one that contains the <code class=\"fm-code-in-text\">csproj<\/code> file), and run the command shown in listing 12.5.<a id=\"calibre_link-1681\"><\/a><a id=\"calibre_link-1682\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.5 Adding a package to the project<\/p>\n<pre class=\"programlisting\">dotnet add package Swashbuckle.AspNetCore --version 6.4.0 <\/pre>\n<p class=\"body\"><a id=\"calibre_link-1024\"><\/a>This command adds the <code class=\"fm-code-in-text\">Swashbuckle.AspNetCore<\/code> package to the project. You will see this package used in chapter 20, but for now, it is the effect of the <code class=\"fm-code-in-text\">dotnet add package<\/code> command that is important.<\/p>\n<p class=\"body\">The new dependency will be shown in the <code class=\"fm-code-in-text\">Platform.csproj<\/code> file:<\/p>\n<pre class=\"programlisting\">&lt;Project Sdk=\"Microsoft.NET.Sdk.Web\"&gt;\n\n  &lt;PropertyGroup&gt;\n    &lt;TargetFramework&gt;net7.0&lt;\/TargetFramework&gt;\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\n  &lt;\/PropertyGroup&gt;\n  \n  <b class=\"fm-bold\">&lt;ItemGroup&gt;<\/b>\n    <b class=\"fm-bold\">&lt;PackageReference Include=\"Swashbuckle.AspNetCore\" Version=\"6.4.0\" \/&gt;<\/b>\n  <b class=\"fm-bold\">&lt;\/ItemGroup&gt;<\/b>\n  \n&lt;\/Project&gt;<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-231\">12.4 Creating custom middleware<\/h2>\n<p class=\"body\">As mentioned, Microsoft provides various middleware components for ASP.NET Core that handle the features most commonly required by web applications. You can also create your own middleware, which is a useful way to understand how ASP.NET Core works, even if you use only the standard components in your projects. The key method for creating middleware is <code class=\"fm-code-in-text\">Use<\/code>, as shown in listing 12.6.<a id=\"calibre_link-1683\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.6 Creating custom middleware in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.Use(async (context, next) =&gt; {<\/b>\n    <b class=\"fm-bold\">if (context.Request.Method == HttpMethods.Get<\/b>\n            <b class=\"fm-bold\">&amp;&amp; context.Request.Query[\"custom\"] == \"true\") {<\/b>\n        <b class=\"fm-bold\">context.Response.ContentType = \"text\/plain\";<\/b>\n        <b class=\"fm-bold\">await context.Response.WriteAsync(\"Custom Middleware \\n\");<\/b>\n    <b class=\"fm-bold\">}<\/b> \n    <b class=\"fm-bold\">await next();<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Use<\/code> method registers a middleware component that is typically expressed as a lambda function that receives each request as it passes through the pipeline (there is another method used for classes, as described in the next section).<\/p>\n<p class=\"body\">The arguments to the lambda function are an <code class=\"fm-code-in-text\">HttpContext<\/code> object and a function that is invoked to tell ASP.NET Core to pass the request to the next middleware component in the pipeline.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HttpContext<\/code> object describes the HTTP request and the HTTP response and provides additional context, including details of the user associated with the request. Table 12.4 describes the most useful members provided by the <code class=\"fm-code-in-text\">HttpContext<\/code> class, which is defined in the <code class=\"fm-code-in-text\">Microsoft.AspNetCore.Http<\/code> namespace.<a id=\"calibre_link-1684\"><\/a><a id=\"calibre_link-1685\"><\/a><a id=\"calibre_link-1686\"><\/a><a id=\"calibre_link-1004\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 12.4 Useful HttpContext members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1687\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"25%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"75%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Connection<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">ConnectionInfo<\/code> object that provides information about the network connection underlying the HTTP request, including details of local and remote IP addresses and ports.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Request<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">HttpRequest<\/code> object that describes the HTTP request being processed.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RequestServices<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property provides access to the services available for the request, as described in chapter 14.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Response<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">HttpResponse<\/code> object that is used to create a response to the HTTP request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Session<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the session data associated with the request. The session data feature is described in chapter 16.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">User<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns details of the user associated with the request, as described in chapters 37 and 38.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Features<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property provides access to request features, which allow access to the low-level aspects of request handling. See chapter 16 for an example of using a request feature.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The ASP.NET Core platform is responsible for processing the HTTP request to create the <code class=\"fm-code-in-text\">HttpRequest<\/code> object, which means that middleware and endpoints don\u2019t have to worry about the raw request data. Table 12.5 describes the most useful members of the <code class=\"fm-code-in-text\">HttpRequest<\/code> class.<a id=\"calibre_link-1688\"><\/a><a id=\"calibre_link-1005\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 12.5 Useful HttpRequest members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1689\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"25%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"75%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Body<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a stream that can be used to read the request body.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ContentLength<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the value of the <code class=\"fm-code-in-text1\">Content-Length<\/code> header.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ContentType<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the value of the <code class=\"fm-code-in-text1\">Content-Type<\/code> header.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Cookies<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the request cookies.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Form<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a representation of the request body as a form.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Headers<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the request headers.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsHttps<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns <code class=\"fm-code-in-text1\">true<\/code> if the request was made using HTTPS.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Method<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the HTTP verb&mdash;also known as the HTTP method&mdash;used for the request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Path<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the path section of the request URL.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Query<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the query string section of the request URL as key-value pairs.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HttpResponse<\/code> object describes the HTTP response that will be sent back to the client when the request has made its way through the pipeline. Table 12.6 describes the most useful members of the <code class=\"fm-code-in-text\">HttpResponse<\/code> class. The ASP.NET Core platform makes dealing with responses as easy as possible, sets headers automatically, and makes it easy to send content to the client.<a id=\"calibre_link-1690\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 12.6 Useful HttpResponse members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1691\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"25%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"75%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ContentLength<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property sets the value of the <code class=\"fm-code-in-text1\">Content-Length<\/code> header.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ContentType<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property sets the value of the <code class=\"fm-code-in-text1\">Content-Type<\/code> header.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Cookies<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property allows cookies to be associated with the response.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HasStarted<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns <code class=\"fm-code-in-text1\">true<\/code> if ASP.NET Core has started to send the response headers to the client, after which it is not possible to make changes to the status code or headers.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Headers<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property allows the response headers to be set.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">StatusCode<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property sets the status code for the response.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">WriteAsync(data)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This asynchronous method writes a data string to the response body.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Redirect(url)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sends a redirection response.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\"><a id=\"calibre_link-1028\"><\/a>When creating custom middleware, the <code class=\"fm-code-in-text\">HttpContext<\/code>, <code class=\"fm-code-in-text\">HttpRequest<\/code>, and <code class=\"fm-code-in-text\">HttpResponse<\/code> objects are used directly, but, as you will learn in later chapters, this isn\u2019t usually required when using the higher-level ASP.NET Core features such as the MVC Framework and Razor Pages.<\/p>\n<p class=\"body\">The middleware function I defined in listing 12.6 uses the <code class=\"fm-code-in-text\">HttpRequest<\/code> object to check the HTTP method and query string to identify GET requests that have a <code class=\"fm-code-in-text\">custom<\/code> parameter in the query string whose value is <code class=\"fm-code-in-text\">true<\/code>, like this:<\/p>\n<pre class=\"programlisting\">...\nif (context.<b class=\"fm-bold\">Request.Method<\/b> == HttpMethods.Get\n    &amp;&amp; context.<b class=\"fm-bold\">Request.Query<\/b>[\"custom\"] == \"true\") {\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HttpMethods<\/code> class defines static strings for each HTTP method. For GET requests with the expected query string, the middleware function uses the <code class=\"fm-code-in-text\">ContentType<\/code> property to set the <code class=\"fm-code-in-text\">Content-Type<\/code> header and uses the <code class=\"fm-code-in-text\">WriteAsync<\/code> method to add a string to the body of the response.<\/p>\n<pre class=\"programlisting\">...\ncontext.Response.<b class=\"fm-bold\">ContentType<\/b> = \"text\/plain\";\nawait context.Response.<b class=\"fm-bold\">WriteAsync<\/b>(\"Custom Middleware \\n\");\n...<\/pre>\n<p class=\"body\">Setting the <code class=\"fm-code-in-text\">Content-Type<\/code> header is important because it prevents the subsequent middleware component from trying to set the response status code and headers. ASP.NET Core will always try to make sure that a valid HTTP response is sent, and this can lead to the response headers or status code being set after an earlier component has already written content to the response body, which produces an exception (because the headers have to be sent to the client before the response body can begin).<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> In this part of the book, all the examples send simple string results to the browser. In part 3, I show you how to create web services that return JSON data and introduce the different ways that ASP.NET Core can produce HTML results.<\/p>\n<p class=\"body\">The second argument to the middleware is the function conventionally named <code class=\"fm-code-in-text\">next<\/code> that tells ASP.NET Core to pass the request to the next component in the request pipeline.<\/p>\n<pre class=\"programlisting\">...\nif (context.Request.Method == HttpMethods.Get\n        &amp;&amp; context.Request.Query[\"custom\"] == \"true\") {\n    context.Response.ContentType = \"text\/plain\";\n    await context.Response.WriteAsync(\"Custom Middleware \\n\");\n} \n<b class=\"fm-bold\">await next();<\/b>\n...<\/pre>\n<p class=\"body\">No arguments are required when invoking the next middleware component because ASP.NET Core takes care of providing the component with the <code class=\"fm-code-in-text\">HttpContext<\/code> object and its own <code class=\"fm-code-in-text\">next<\/code> function so that it can process the request. The <code class=\"fm-code-in-text\">next<\/code> function is asynchronous, which is why the <code class=\"fm-code-in-text\">await<\/code> keyword is used and why the lambda function is defined with the <code class=\"fm-code-in-text\">async<\/code> keyword.<a id=\"calibre_link-1692\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You may encounter middleware that calls <code class=\"fm-code-in-text1\">next.Invoke()<\/code> instead of <code class=\"fm-code-in-text1\">next()<\/code>. These are equivalent, and <code class=\"fm-code-in-text1\">next()<\/code> is provided as a convenience by the compiler to produce concise code.<\/p>\n<p class=\"body\">Start ASP.NET Core using the <code class=\"fm-code-in-text\">dotnet run<\/code> command and use a browser to request http:\/\/localhost:5000\/?custom=true. You will see that the new middleware function writes its message to the response before passing on the request to the next middleware component, as shown in figure 12.5. Remove the query string, or change <code class=\"fm-code-in-text\">true<\/code> to <code class=\"fm-code-in-text\">false<\/code>, and the middleware component will pass on the request without adding to the response.<a id=\"calibre_link-1025\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre92\" src=\"\/images\/proaspnetcore7\/000087.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.5 Creating custom middleware<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-232\">12.4.1 Defining middleware using a class<\/h3>\n<p class=\"body\">Defining middleware using lambda functions is convenient, but it can lead to a long and complex series of statements in the <code class=\"fm-code-in-text\">Program.cs<\/code> file and makes it hard to reuse middleware in different projects. Middleware can also be defined using classes, which keeps the code outside of the <code class=\"fm-code-in-text\">Program.cs<\/code> file. To create a middleware class, add a class file named <code class=\"fm-code-in-text\">Middleware.cs<\/code> to the <code class=\"fm-code-in-text\">Platform<\/code> folder, with the content shown in listing 12.7<a id=\"calibre_link-1693\"><\/a>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.7 The contents of the Middleware.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n\n    public class QueryStringMiddleWare {\n        private RequestDelegate next;\n                \n        public QueryStringMiddleWare(RequestDelegate nextDelegate) {\n            next = nextDelegate;\n        }\n                \n        public async Task Invoke(HttpContext context) {\n            if (context.Request.Method == HttpMethods.Get\n                        &amp;&amp; context.Request.Query[\"custom\"] == \"true\") {\n                if (!context.Response.HasStarted) {\n                    context.Response.ContentType = \"text\/plain\";\n                }\n                await context.Response.WriteAsync(\"Class Middleware \\n\");\n            }\n            await next(context);\n        }\n    }\n}<\/pre>\n<p class=\"body\">Middleware classes receive a <code class=\"fm-code-in-text\">RequestDelegate<\/code> object as a constructor parameter, which is used to forward the request to the next component in the pipeline. The <code class=\"fm-code-in-text\">Invoke<\/code> method is called by ASP.NET Core when a request is received and is given an <code class=\"fm-code-in-text\">HttpContext<\/code> object that provides access to the request and response, using the same classes that lambda function middleware receives. The <code class=\"fm-code-in-text\">RequestDelegate<\/code> returns a <code class=\"fm-code-in-text\">Task<\/code>, which allows it to work asynchronously.<\/p>\n<p class=\"body\">One important difference in class-based middleware is that the <code class=\"fm-code-in-text\">HttpContext<\/code> object must be used as an argument when invoking the <code class=\"fm-code-in-text\">RequestDelegate<\/code> to forward the request, like this:<\/p>\n<pre class=\"programlisting\">...\nawait next(<b class=\"fm-bold\">context<\/b>);\n...<\/pre>\n<p class=\"body\">Class-based middleware components are added to the pipeline with the <code class=\"fm-code-in-text\">UseMiddleware<\/code> method, which accepts the middleware as a type argument, as shown in listing 12.8.<a id=\"calibre_link-1037\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.8 Adding class-based middleware in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\napp.Use(async (context, next) =&gt; {\n    if (context.Request.Method == HttpMethods.Get\n            &amp;&amp; context.Request.Query[\"custom\"] == \"true\") {\n        context.Response.ContentType = \"text\/plain\";\n        await context.Response.WriteAsync(\"Custom Middleware \\n\");\n    }\n    await next();\n});\n\n<b class=\"fm-bold\">app.UseMiddleware&lt;Platform.QueryStringMiddleWare&gt;();<\/b>\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.Run();<\/pre>\n<p class=\"body\">When the ASP.NET Core is started, the <code class=\"fm-code-in-text\">QueryStringMiddleware<\/code> class will be instantiated, and its <code class=\"fm-code-in-text\">Invoke<\/code> method will be called to process requests as they are received.<a id=\"calibre_link-1694\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> A single middleware object is used to handle all requests, which means that the code in the <code class=\"fm-code-in-text1\">Invoke<\/code> method must be thread-safe.<\/p>\n<p class=\"body\">Use the <code class=\"fm-code-in-text\">dotnet run<\/code> command to start ASP.NET Core and use a browser to request http:\/\/localhost:5000\/?custom=true. You will see the output from both middleware components, as shown in figure 12.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre93\" src=\"\/images\/proaspnetcore7\/000088.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.6 Using a class-based middleware component<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-233\">12.4.2 Understanding the return pipeline path<\/h3>\n<p class=\"body\">Middleware components can modify the <code class=\"fm-code-in-text\">HTTPResponse<\/code> object after the <code class=\"fm-code-in-text\">next<\/code> function has been called, as shown by the new middleware in listing 12.9.<a id=\"calibre_link-1695\"><\/a><a id=\"calibre_link-1696\"><\/a><a id=\"calibre_link-1031\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.9 Adding new middleware in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.Use(async (context, next) =&gt; {<\/b>\n    <b class=\"fm-bold\">await next();<\/b>\n    <b class=\"fm-bold\">await context.Response<\/b>\n        <b class=\"fm-bold\">.WriteAsync($\"\\nStatus Code: { context.Response.StatusCode}\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.Use(async (context, next) =&gt; {\n    if (context.Request.Method == HttpMethods.Get\n            &amp;&amp; context.Request.Query[\"custom\"] == \"true\") {\n        context.Response.ContentType = \"text\/plain\";\n        await context.Response.WriteAsync(\"Custom Middleware \\n\");\n    }\n    await next();\n});\n\napp.UseMiddleware&lt;Platform.QueryStringMiddleWare&gt;();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.Run();<\/pre>\n<p class=\"body\">The new middleware immediately calls the <code class=\"fm-code-in-text\">next<\/code> method to pass the request along the pipeline and then uses the <code class=\"fm-code-in-text\">WriteAsync<\/code> method to add a string to the response body. This may seem like an odd approach, but it allows middleware to make changes to the response before and after it is passed along the request pipeline by defining statements before and after the next function is invoked, as illustrated by figure 12.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre94\" src=\"\/images\/proaspnetcore7\/000089.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.7 Passing requests and responses through the ASP.NET Core pipeline<\/p>\n<\/div>\n<p class=\"body\"><a id=\"calibre_link-1033\"><\/a>Middleware can operate before the request is passed on, after the request has been processed by other components, or both. The result is that several middleware components collectively contribute to the response that is produced, each providing some aspect of the response or providing some feature or data that is used later in the pipeline.<\/p>\n<p class=\"body\">Start ASP.NET Core using the <code class=\"fm-code-in-text\">dotnet run<\/code> command and use a browser to request http:\/\/localhost:5000, which will produce output that includes the content from the new middleware component, as shown in figure 12.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre95\" src=\"\/images\/proaspnetcore7\/000090.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.8 Modifying a response in the return path<\/p>\n<\/div>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Middleware components must not change the response status code or headers once ASP.NET Core has started to send the response to the client. Check the <code class=\"fm-code-in-text1\">HasStarted<\/code> property, described in table 12.6, to avoid exceptions.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-234\">12.4.3 Short-Circuiting the request pipeline<\/h3>\n<p class=\"body\">Components that generate complete responses can choose not to call the <code class=\"fm-code-in-text\">next<\/code> function so that the request isn\u2019t passed on. Components that don\u2019t pass on requests are said to <i class=\"fm-italics\">short-circuit<\/i> the pipeline, which is what the new middleware component shown in listing 12.10 does for requests that target the <code class=\"fm-code-in-text\">\/short<\/code> URL.<a id=\"calibre_link-1697\"><\/a><a id=\"calibre_link-1698\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.10 Short-Circuiting the pipeline in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\napp.Use(async (context, next) =&gt; {\n    await next();\n    await context.Response\n        .WriteAsync($\"\\nStatus Code: { context.Response.StatusCode}\");\n});\n\n<b class=\"fm-bold\">app.Use(async (context, next) =&gt; {<\/b>\n    <b class=\"fm-bold\">if (context.Request.Path == \"\/short\") {<\/b>\n        <b class=\"fm-bold\">await context.Response<\/b>\n            <b class=\"fm-bold\">.WriteAsync($\"Request Short Circuited\");<\/b>\n    <b class=\"fm-bold\">} else {<\/b>\n        <b class=\"fm-bold\">await next();<\/b>\n    <b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.Use(async (context, next) =&gt; {\n    if (context.Request.Method == HttpMethods.Get\n            &amp;&amp; context.Request.Query[\"custom\"] == \"true\") {\n        context.Response.ContentType = \"text\/plain\";\n        await context.Response.WriteAsync(\"Custom Middleware \\n\");\n    }\n    await next();\n});\n\napp.UseMiddleware&lt;Platform.QueryStringMiddleWare&gt;();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.Run();<\/pre>\n<p class=\"body\">The new middleware checks the <code class=\"fm-code-in-text\">Path<\/code> property of the <code class=\"fm-code-in-text\">HttpRequest<\/code> object to see whether the request is for the <code class=\"fm-code-in-text\">\/short<\/code> URL; if it is, it calls the <code class=\"fm-code-in-text\">WriteAsync<\/code> method without calling the <code class=\"fm-code-in-text\">next<\/code> function. To see the effect, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/short?custom=true, which will produce the output shown in figure 12.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre96\" src=\"\/images\/proaspnetcore7\/000091.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.9 Short-circuiting the request pipeline<\/p>\n<\/div>\n<p class=\"body\">Even though the URL has the query string parameter that is expected by the next component in the pipeline, the request isn\u2019t forwarded, so that subsequent middleware doesn\u2019t get used. Notice, however, that the previous component in the pipeline has added its message to the response. That\u2019s because the short-circuiting only prevents components further along the pipeline from being used and doesn\u2019t affect earlier components, as illustrated in figure 12.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre97\" src=\"\/images\/proaspnetcore7\/000092.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.10 Short-circuiting the request pipeline<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-235\">12.4.4 Creating pipeline branches<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1023\"><\/a>The <code class=\"fm-code-in-text\">Map<\/code> method is used to create a section of pipeline that is used to process requests for specific URLs, creating a separate sequence of middleware components, as shown in listing 12.11.<a id=\"calibre_link-1699\"><\/a><a id=\"calibre_link-1700\"><\/a><a id=\"calibre_link-1701\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.11 Creating a pipeline branch in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.Map(\"\/branch\", branch =&gt; {<\/b>\n\n    <b class=\"fm-bold\">branch.UseMiddleware&lt;Platform.QueryStringMiddleWare&gt;();<\/b>\n        \n    <b class=\"fm-bold\">branch.Use(async (HttpContext context, Func&lt;Task&gt; next) =&gt; {<\/b>\n        <b class=\"fm-bold\">await context.Response.WriteAsync($\"Branch Middleware\");<\/b>\n    <b class=\"fm-bold\">});<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.UseMiddleware&lt;Platform.QueryStringMiddleWare&gt;();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.Run();<\/pre>\n<p class=\"body\">The first argument to the <code class=\"fm-code-in-text\">Map<\/code> method specifies the string that will be used to match URLs. The second argument is the branch of the pipeline, to which middleware components are added with the <code class=\"fm-code-in-text\">Use<\/code> and <code class=\"fm-code-in-text\">UseMiddleware<\/code> methods.<\/p>\n<p class=\"body\">The statements in listing 12.11 create a branch that is used for URLs that start with <code class=\"fm-code-in-text\">\/branch<\/code> and that pass requests through the <code class=\"fm-code-in-text\">QueryStringMiddleware<\/code> class defined in listing 12.7 and a middleware lambda expression that adds a message to the response. Figure 12.11 shows the effect of the branch on the request pipeline.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre98\" src=\"\/images\/proaspnetcore7\/000093.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.11 Adding a branch to the request pipeline<\/p>\n<\/div>\n<p class=\"body\"><a id=\"calibre_link-1027\"><\/a>When a URL is matched by the <code class=\"fm-code-in-text\">Map<\/code> method, it follows the branch. In this example, the final component in the middleware branch doesn\u2019t invoke the next delegate, which means that requests do not pass through the middleware components on the main path through the pipeline.<\/p>\n<p class=\"body\">The same middleware can be used in different parts of the pipeline, which can be seen in listing 12.11, where the <code class=\"fm-code-in-text\">QueryStringMiddleWare<\/code> class is used in both the main part of the pipeline and the branch.<\/p>\n<p class=\"body\">To see the different ways that requests are handled, restart ASP.NET Core and use a browser to request the http:\/\/localhost:5000\/?custom=true URL, which will be handled on the main part of the pipeline and will produce the output shown on the left of figure 12.12. Navigate to http:\/\/localhost:5000\/branch?custom=true, and the request will be forwarded to the middleware in the branch, producing the output shown on the right in figure 12.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre99\" src=\"\/images\/proaspnetcore7\/000094.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.12 The effect of branching the request pipeline<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Branching wIth a predicate<\/p>\n<p class=\"fm-sidebar-text\">ASP.NET Core also supports the <code class=\"fm-code-in-text1\">MapWhen<\/code> method, which can match requests using a predicate, allowing requests to be selected for a pipeline branch on criteria other than just URLs.<a id=\"calibre_link-1702\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">The arguments to the <code class=\"fm-code-in-text1\">MapWhen<\/code> method are a predicate function that receives an <code class=\"fm-code-in-text1\">HttpContext<\/code> and that returns <code class=\"fm-code-in-text1\">true<\/code> for requests that should follow the branch, and a function that receives an <code class=\"fm-code-in-text1\">IApplicationBuilder<\/code> object representing the pipeline branch, to which middleware is added. Here is an example of using the <code class=\"fm-code-in-text1\">MapWhen<\/code> method to branch the pipeline:<\/p>\n<pre class=\"programlisting\">...\napp.MapWhen(context =&gt; context.Request.Query.Keys.Contains(\"branch\"), \n   branch =&gt; {\n        \/\/ ...add middleware components here...\n});\n...<\/pre>\n<p class=\"fm-sidebar-text\">The predicate function returns <code class=\"fm-code-in-text1\">true<\/code> to branch for requests whose query string contains a parameter named <code class=\"fm-code-in-text1\">branch<\/code>. A cast to the <code class=\"fm-code-in-text1\">IApplicationBuilder<\/code> interface is not required because there only one <code class=\"fm-code-in-text1\">MapWhen<\/code> extension method has been defined.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-236\">12.4.5 Creating terminal middleware<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1035\"><\/a>Terminal middleware never forwards requests to other components and always marks the end of the request pipeline. There is a terminal middleware component in the <code class=\"fm-code-in-text\">Program.cs<\/code> file, as shown here:<a id=\"calibre_link-1703\"><\/a><\/p>\n<pre class=\"programlisting\">...\nbranch.Use(async (context, next) =&gt; {\n    await context.Response.WriteAsync($\"Branch Middleware\");\n});\n...<\/pre>\n<p class=\"body\">ASP.NET Core supports the <code class=\"fm-code-in-text\">Run<\/code> method as a convenience feature for creating terminal middleware, which makes it obvious that a middleware component won\u2019t forward requests and that a deliberate decision has been made not to call the <code class=\"fm-code-in-text\">next<\/code> function. In listing 12.12, I have used the <code class=\"fm-code-in-text\">Run<\/code> method for the terminal middleware in the pipeline branch.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.12 Using the run method in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\n((IApplicationBuilder)app).Map(\"\/branch\", branch =&gt; {\n\n    branch.UseMiddleware&lt;Platform.QueryStringMiddleWare&gt;();\n        \n    <b class=\"fm-bold\">branch.Run(async (context) =&gt; {<\/b>\n        await context.Response.WriteAsync($\"Branch Middleware\");\n    });\n});\n\napp.UseMiddleware&lt;Platform.QueryStringMiddleWare&gt;();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.Run();<\/pre>\n<p class=\"body\">The middleware function passed to the <code class=\"fm-code-in-text\">Run<\/code> method receives only an <code class=\"fm-code-in-text\">HttpContext<\/code> object and doesn\u2019t have to define a parameter that isn\u2019t used. Behind the scenes, the <code class=\"fm-code-in-text\">Run<\/code> method is implemented through the <code class=\"fm-code-in-text\">Use<\/code> method, and this feature is provided only as a convenience.<a id=\"calibre_link-1704\"><\/a><a id=\"calibre_link-1032\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Middleware added to the pipeline after a terminal component will never receive requests. ASP.NET Core won\u2019t warn you if you add a terminal component before the end of the pipeline.<\/p>\n<p class=\"body\">Class-based components can be written so they can be used as both regular and terminal middleware, as shown in listing 12.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.13 Adding terminal support in the Middleware.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n\n    public class QueryStringMiddleWare {\n        <b class=\"fm-bold\">private RequestDelegate? next;<\/b>\n                \n        <b class=\"fm-bold\">public QueryStringMiddleWare() {<\/b>\n            <b class=\"fm-bold\">\/\/ do nothing<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        public QueryStringMiddleWare(RequestDelegate nextDelegate) {\n            next = nextDelegate;\n        }\n                \n        public async Task Invoke(HttpContext context) {\n            if (context.Request.Method == HttpMethods.Get\n                        &amp;&amp; context.Request.Query[\"custom\"] == \"true\") {\n                if (!context.Response.HasStarted) {\n                    context.Response.ContentType = \"text\/plain\";\n                }\n                await context.Response.WriteAsync(\"Class Middleware\\n\");\n            }\n            <b class=\"fm-bold\">if (next != null) {<\/b>\n                <b class=\"fm-bold\">await next(context);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The component will forward requests only when the constructor has been provided with a non-<code class=\"fm-code-in-text\">null<\/code> value for the <code class=\"fm-code-in-text\">nextDelegate<\/code> parameter. listing 12.14 shows the application of the component in both standard and terminal forms.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.14 Applying middleware in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\n((IApplicationBuilder)app).Map(\"\/branch\", branch =&gt; {\n    <b class=\"fm-bold\">branch.Run(new Platform.QueryStringMiddleWare().Invoke);<\/b>\n});\n\napp.UseMiddleware&lt;Platform.QueryStringMiddleWare&gt;();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.Run();<\/pre>\n<p class=\"body\">There is no equivalent to the <code class=\"fm-code-in-text\">UseMiddleware<\/code> method for terminal middleware, so the <code class=\"fm-code-in-text\">Run<\/code> method must be used by creating a new instance of the middleware class and selecting its <code class=\"fm-code-in-text\">Invoke<\/code> method. Using the <code class=\"fm-code-in-text\">Run<\/code> method doesn\u2019t alter the output from the middleware, which you can see by restarting ASP.NET Core and navigating to the http:\/\/localhost:5000\/branch?custom=true URL, which produces the content shown in figure 12.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre100\" src=\"\/images\/proaspnetcore7\/000095.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.13. Using the Run method to create terminal middleware<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-237\">12.5 Configuring middleware<\/h2>\n<p class=\"body\">There is a common pattern for configuring middleware that is known as the <i class=\"fm-italics\">options pattern<\/i> and that is used by some of the built-in middleware components described in later chapters.<\/p>\n<p class=\"body\">The starting point is to define a class that contains the configuration options for a middleware component. Add a class file named <code class=\"fm-code-in-text\">MessageOptions.cs<\/code> to the <code class=\"fm-code-in-text\">Platform<\/code> folder with the code shown in listing 12.15.<a id=\"calibre_link-1705\"><\/a><a id=\"calibre_link-1706\"><\/a><a id=\"calibre_link-1707\"><\/a><a id=\"calibre_link-920\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.15 The contents of the MessageOptions.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n\n    public class MessageOptions {\n        \n        public string CityName { get; set; } = \"New York\";\n        public string CountryName{ get; set; } = \"USA\";\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">MessageOptions<\/code> class defines properties that detail a city and a country. In listing 12.16, I have used the options pattern to create a custom middleware component that relies on the <code class=\"fm-code-in-text\">MessageOptions<\/code> class for its configuration. I have also removed some of the middleware from previous examples for brevity.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.16 Using the options pattern in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Microsoft.Extensions.Options;<\/b>\n<b class=\"fm-bold\">using Platform;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.Configure&lt;MessageOptions&gt;(options =&gt; {<\/b>\n    <b class=\"fm-bold\">options.CityName = \"Albany\";<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapGet(\"\/location\", async (HttpContext context,<\/b>\n    <b class=\"fm-bold\">IOptions&lt;MessageOptions&gt; msgOpts) =&gt; {<\/b>\n        <b class=\"fm-bold\">Platform.MessageOptions opts = msgOpts.Value;<\/b>\n        <b class=\"fm-bold\">await context.Response.WriteAsync($\"{opts.CityName}, \"<\/b> \n            <b class=\"fm-bold\">+ opts.CountryName);<\/b>\n    <b class=\"fm-bold\">});<\/b>\n        \napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.Run();<\/pre>\n<p class=\"body\">The options are set up using the <code class=\"fm-code-in-text\">Services.Configure<\/code> method defined by the <code class=\"fm-code-in-text\">WebApplicationBuilder<\/code> class, using a generic type parameter like this:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">builder.Services.Configure&lt;MessageOptions&gt;<\/b>(options =&gt; {\n    options.CityName = \"Albany\";\n});\n...<\/pre>\n<p class=\"body\">This statement creates options using the <code class=\"fm-code-in-text\">MessageOptions<\/code> class and changes the value of the <code class=\"fm-code-in-text\">CityName<\/code> property. When the application starts, the ASP.NET Core platform will create a new instance of the <code class=\"fm-code-in-text\">MessageOptions<\/code> class and pass it to the function supplied as the argument to the <code class=\"fm-code-in-text\">Configure<\/code> method, allowing the default option values to be changed.<\/p>\n<p class=\"body\">The options will be available as a service, which means this statement must appear before the call to the <code class=\"fm-code-in-text\">Build<\/code> method is called, as shown in the listing.<\/p>\n<p class=\"body\">Middleware components can access the configuration options by defining a parameter for the function that handles the request, like this:<\/p>\n<pre class=\"programlisting\">...\napp.MapGet(\"\/location\", async (<b class=\"fm-bold\">HttpContext context,<\/b>\n    <b class=\"fm-bold\">IOptions&lt;MessageOptions&gt; msgOpts<\/b>) =&gt; {\n        Platform.MessageOptions opts = msgOpts.Value;\n        await context.Response.WriteAsync($\"{opts.CityName}, \" \n            + opts.CountryName);\n    });\n...<\/pre>\n<p class=\"body\">Some of the extension methods used to register middleware components will accept any function to handle requests. When a request is processed, the ASP.NET Core platform inspects the function to find parameters that require services, which allows the middleware component to use the configuration options in the response it generates:<\/p>\n<pre class=\"programlisting\">...\napp.MapGet(\"\/location\", async (HttpContext context,\n    IOptions&lt;MessageOptions&gt; msgOpts) =&gt; {\n        <b class=\"fm-bold\">Platform.MessageOptions opts = msgOpts.Value;<\/b>\n        <b class=\"fm-bold\">await context.Response.WriteAsync($\"{opts.CityName}, \"<\/b> \n            <b class=\"fm-bold\">+ opts.CountryName);<\/b>\n    });\n... <\/pre>\n<p class=\"body\">This is an example of dependency injection, which I describe in detail in chapter 14. For now, however, you can see how the middleware component uses the options pattern by restarting ASP.NET Core and using a browser to request http:\/\/localhost:5000\/location, which will produce the response shown in figure 12.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre100\" src=\"\/images\/proaspnetcore7\/000096.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 12.14 Using the options pattern<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-238\">12.5.1 Using the options pattern with class-based middleware<\/h3>\n<p class=\"body\">The options pattern can also be used with class-based middleware and is applied in a similar way. Add the statements shown in listing 12.17 to the <code class=\"fm-code-in-text\">Middleware.cs<\/code> file to define a class-based middleware component that uses the <code class=\"fm-code-in-text\">MessageOptions<\/code> class for configuration.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.17 Defining middleware in the Middleware.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Microsoft.Extensions.Options;<\/b>\n\nnamespace Platform {\n\n    public class QueryStringMiddleWare {\n        private RequestDelegate? next;\n\n        <b class=\"fm-bold\">\/\/ ...statements omitted for brevity...<\/b>\n    }\n        \n    <b class=\"fm-bold\">public class LocationMiddleware {<\/b>\n        <b class=\"fm-bold\">private RequestDelegate next;<\/b>\n        <b class=\"fm-bold\">private MessageOptions options;<\/b>\n                \n        <b class=\"fm-bold\">public LocationMiddleware(RequestDelegate nextDelegate,<\/b>\n                <b class=\"fm-bold\">IOptions&lt;MessageOptions&gt; opts) {<\/b>\n            <b class=\"fm-bold\">next = nextDelegate;<\/b>\n            <b class=\"fm-bold\">options = opts.Value;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">public async Task Invoke(HttpContext context) {<\/b>\n            <b class=\"fm-bold\">if (context.Request.Path == \"\/location\") {<\/b>\n                <b class=\"fm-bold\">await context.Response<\/b>\n                    <b class=\"fm-bold\">.WriteAsync($\"{options.CityName}, \"<\/b> \n                        <b class=\"fm-bold\">+ options.CountryName);<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">await next(context);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">LocationMiddleware<\/code> class defines an <code class=\"fm-code-in-text\">IOptions&lt;MessageOptions&gt;<\/code> constructor parameter, which can be used in the <code class=\"fm-code-in-text\">Invoke<\/code> method to access the options settings.<\/p>\n<p class=\"body\">Listing 12.18 reconfigures the request pipeline to replace the lambda function middleware component with the class from listing 12.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 12.18 Using class-based middleware in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">\/\/using Microsoft.Extensions.Options;<\/b>\nusing Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.Configure&lt;MessageOptions&gt;(options =&gt; {\n    options.CityName = \"Albany\";\n});\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.UseMiddleware&lt;LocationMiddleware&gt;();<\/b>\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\napp.Run();<\/pre>\n<p class=\"body\">When the <code class=\"fm-code-in-text\">UseMiddleware<\/code> statement is executed, the <code class=\"fm-code-in-text\">LocationMiddleware<\/code> constructor is inspected, and its <code class=\"fm-code-in-text\">IOptions&lt;MessageOptions&gt;<\/code> parameter will be resolved using the object created with the <code class=\"fm-code-in-text\">Services.Configure<\/code> method. This is done using the dependency injection feature that is described in chapter 14, but the immediate effect is that the options pattern can be used to easily configure class-based middleware. Restart ASP.NET Core and request http:\/\/localhost:5000\/location to test the new middleware, which will produce the same output as shown in figure 12.14.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-239\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core uses a pipeline to process HTTP requests.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Each request is passed to a series of middleware components for processing.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Once the request has reached the end of the pipeline, the same middleware components are able to inspect and modify the response before it is sent.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Middleware components can choose not to forward requests to the next component in the pipeline, known as \u201cshort-circuiting.\u201d<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core can be configured to use different sequences of middleware components to handle different request URLs.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Middleware is configured using the options pattern, which is a simple and consistent approach used throughout ASP.NET Core.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-240\">\n<div class=\"calibre1\" id=\"calibre_link-1708\">\n<h1 class=\"tochead\" id=\"calibre_link-1709\"><a id=\"calibre_link-1710\"><\/a>13 Using URL routing<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Understanding how routes can be used to match request URLs<\/li>\n<li class=\"co-summary-bullet\">Structuring URLs patterns to match requests<\/li>\n<li class=\"co-summary-bullet\">Matching requests using routes<\/li>\n<\/ul>\n<p class=\"body\">The URL routing feature makes it easier to generate responses by consolidating the processing and matching of request URLs. In this chapter, I explain how the ASP.NET Core platform supports URL routing, show its use, and explain why it can be preferable to creating custom middleware components. Table 13.1 puts URL routing in context.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> This chapter focuses on URL routing for the ASP.NET Core platform. See part 3 for details of how the higher-level parts of ASP.NET Core build on the features described in this chapter.<\/p>\n<p class=\"fm-table-caption\">Table 13.1 Putting URL routing in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1711\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What is it?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">URL routing consolidates the processing and matching of URLs, allowing components known as <i class=\"fm-italics\">endpoints<\/i> to generate responses.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why is it useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">URL routing obviates the need for each middleware component to process the URL to see whether the request will be handled or passed along the pipeline. The result is more efficient and easier to maintain.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How is it used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The URL routing middleware components are added to the request pipeline and configured with a set of routes. Each route contains a URL path and a delegate that will generate a response when a request with the matching path is received.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">It can be difficult to define the set of routes matching all the URLs supported by a complex application.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">URL routing is optional, and custom middleware components can be used instead.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 13.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 13.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1712\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Handling requests for a specific set of URLs<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define a route with a pattern that matches the required URLs.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">1&ndash;7<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Extracting values from URLs<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use segment variables.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">8&ndash;11, 15<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Generating URLs<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the link generator to produce URLs from routes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">12&ndash;14, 16<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Matching URLs with different numbers of segments<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use optional segments or catchall segments in the URL routing pattern.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">17&ndash;19<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Restricting matches<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use constraints in the URL routing pattern.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">20&ndash;22, 24&ndash;27<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Matching requests that are not otherwise handled<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define fallback routes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">23<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Seeing which endpoint will handle a request<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the routing context data.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">28<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-1713\">13.1 Preparing for this chapter<\/h2>\n<p class=\"body\">In this chapter, I continue to use the <code class=\"fm-code-in-text\">Platform<\/code> project from chapter 12. To prepare for this chapter, add a file called <code class=\"fm-code-in-text\">Population.cs<\/code> to the <code class=\"fm-code-in-text\">Platform<\/code> folder with the code shown in listing 13.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.1 The contents of the Population.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n    public class Population {\n        private RequestDelegate? next;\n                \n        public Population() { }\n                \n        public Population(RequestDelegate nextDelegate) {\n            next = nextDelegate;\n        }\n                \n        public async Task Invoke(HttpContext context) {\n            string[] parts = context.Request.Path.ToString()\n                .Split(\"\/\", StringSplitOptions.RemoveEmptyEntries);\n            if (parts.Length == 2 &amp;&amp; parts[0] == \"population\") {\n                string city = parts[1];\n                int? pop = null;\n                switch (city.ToLower()) {\n                    case \"london\":\n                        pop = 8_136_000;\n                        break;\n                    case \"paris\":\n                        pop = 2_141_000;\n                        break;\n                    case \"monaco\":\n                        pop = 39_000;\n                        break;\n                }\n                if (pop.HasValue) {\n                    await context.Response\n                        .WriteAsync($\"City: {city}, Population: {pop}\");\n                    return;\n                }\n            }\n            if (next != null) {\n                await next(context);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">This middleware component responds to requests for <code class=\"fm-code-in-text\">\/population\/&lt;city&gt;<\/code> where <code class=\"fm-code-in-text\">&lt;city&gt;<\/code> is <code class=\"fm-code-in-text\">london<\/code>, <code class=\"fm-code-in-text\">paris<\/code>, or <code class=\"fm-code-in-text\">monaco<\/code>. The middleware component splits up the URL path string, checks that it has the expected length, and uses a <code class=\"fm-code-in-text\">switch<\/code> statement to determine if it is a request for a URL that it can respond to. A response is generated if the URL matches the pattern the middleware is looking for; otherwise, the request is passed along the pipeline.<\/p>\n<p class=\"body\">Add a class file named <code class=\"fm-code-in-text\">Capital.cs<\/code> to the <code class=\"fm-code-in-text\">Platform<\/code> folder with the code shown in listing 13.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.2 The contents of the Capital.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n    public class Capital {\n        private RequestDelegate? next;\n                \n        public Capital() { }\n                \n        public Capital(RequestDelegate nextDelegate) {\n            next = nextDelegate;\n        }\n                \n        public async Task Invoke(HttpContext context) {\n            string[] parts = context.Request.Path.ToString()\n                .Split(\"\/\", StringSplitOptions.RemoveEmptyEntries);\n            if (parts.Length == 2 &amp;&amp; parts[0] == \"capital\") {\n                string? capital = null;\n                string country = parts[1];\n                switch (country.ToLower()) {\n                    case \"uk\":\n                        capital = \"London\";\n                        break;\n                    case \"france\":\n                        capital = \"Paris\";\n                        break;\n                    case \"monaco\":\n                        context.Response.Redirect(\n                            $\"\/population\/{country}\");\n                        return;\n                }\n                if (capital != null) {\n                    await context.Response.WriteAsync(\n                        $\"{capital} is the capital of {country}\");\n                    return;\n                }\n            }\n            if (next != null) {\n                await next(context);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">This middleware component is looking for requests for <code class=\"fm-code-in-text\">\/capital\/&lt;country&gt;<\/code>, where <code class=\"fm-code-in-text\">&lt;country&gt;<\/code> is <code class=\"fm-code-in-text\">uk<\/code>, <code class=\"fm-code-in-text\">france<\/code>, or <code class=\"fm-code-in-text\">monaco<\/code>. The capital cities of the United Kingdom and France are displayed, but requests for Monaco, which is a city and a state, are redirected to <code class=\"fm-code-in-text\">\/population\/monaco<\/code>.<\/p>\n<p class=\"body\">Listing 13.3 replaces the middleware examples from the previous chapter and adds the new middleware components to the request pipeline.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.3 Replacing the contents of the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\napp.UseMiddleware&lt;Population&gt;();\napp.UseMiddleware&lt;Capital&gt;();\napp.Run(async (context) =&gt; {\n    await context.Response.WriteAsync(\"Terminal Middleware Reached\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">Start ASP.NET Core by running the command shown in listing 13.4 in the <code class=\"fm-code-in-text\">Platform<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.4 Starting the ASP.NET Core Runtime<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Navigate to http:\/\/localhost:5000\/population\/london, and you will see the output on the left side of figure 13.1. Navigate to http:\/\/localhost:5000\/capital\/france to see the output from the other middleware component, which is shown on the right side of figure 13.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre101\" src=\"\/images\/proaspnetcore7\/000097.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.1 Running the example application<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-241\">13.1.1 Understanding URL routing<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1117\"><\/a>Each middleware component decides whether to act on a request as it passes along the pipeline. Some components are looking for a specific header or query string value, but most components&mdash;especially terminal and short-circuiting components&mdash;are trying to match URLs.<\/p>\n<p class=\"body\">Each middleware component has to repeat the same set of steps as the request works its way along the pipeline. You can see this in the middleware defined in the previous section, where both components go through the same process: split up the URL, check the number of parts, inspect the first part, and so on.<a id=\"calibre_link-1714\"><\/a><\/p>\n<p class=\"body\">This approach is far from ideal. It is inefficient because the same set of operations is repeated by each middleware component to process the URL. It is difficult to maintain because the URL that each component is looking for is hidden in its code. It breaks easily because changes must be carefully worked through in multiple places. For example, the <code class=\"fm-code-in-text\">Capital<\/code> component redirects requests to a URL whose path starts with <code class=\"fm-code-in-text\">\/population<\/code>, which is handled by the <code class=\"fm-code-in-text\">Population<\/code> component. If the <code class=\"fm-code-in-text\">Population<\/code> component is revised to support the <code class=\"fm-code-in-text\">\/size<\/code> URL instead, then this change must also be reflected in the <code class=\"fm-code-in-text\">Capital<\/code> component. Real applications can support complex sets of URLs and working changes fully through individual middleware components can be difficult.<\/p>\n<p class=\"body\">URL routing solves these problems by introducing middleware that takes care of matching request URLs so that components, called <i class=\"fm-italics\">endpoints<\/i>, can focus on responses. The mapping between endpoints and the URLs they require is expressed in a <i class=\"fm-italics\">route<\/i>. The routing middleware processes the URL, inspects the set of routes, and finds the endpoint to handle the request, a process known as <i class=\"fm-italics\">routing<\/i>.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-242\">13.1.2 Adding the routing middleware and defining an endpoint<\/h3>\n<p class=\"body\">The routing middleware is added using two separate methods: <code class=\"fm-code-in-text\">UseRouting<\/code> and <code class=\"fm-code-in-text\">UseEndpoints<\/code>. The <code class=\"fm-code-in-text\">UseRouting<\/code> method adds the middleware responsible for processing requests to the pipeline. The <code class=\"fm-code-in-text\">UseEndpoints<\/code> method is used to define the routes that match URLs to endpoints. URLs are matched using patterns that are compared to the path of request URLs, and each route creates a relationship between one URL pattern and one endpoint. Listing 13.5 shows the use of the routing middleware and contains a simple route.<a id=\"calibre_link-1715\"><\/a><a id=\"calibre_link-1716\"><\/a><a id=\"calibre_link-1717\"><\/a><a id=\"calibre_link-1718\"><\/a><a id=\"calibre_link-1036\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> I explain why there are two methods for routing in the \u201cAccessing the endpoint in a middleware component\u201d section.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.5 Using the routing middleware in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.UseMiddleware&lt;Population&gt;();\napp.UseMiddleware&lt;Capital&gt;();\n\n<b class=\"fm-bold\">app.UseRouting();<\/b>\n\n<b class=\"fm-bold\">#pragma warning disable ASP0014<\/b>\n\n<b class=\"fm-bold\">app.UseEndpoints(endpoints =&gt; {<\/b>\n    <b class=\"fm-bold\">endpoints.MapGet(\"routing\", async context =&gt; {<\/b>\n        <b class=\"fm-bold\">await context.Response.WriteAsync(\"Request Was Routed\");<\/b>\n    <b class=\"fm-bold\">});<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.Run(async (context) =&gt; {\n    await context.Response.WriteAsync(\"Terminal Middleware Reached\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">There are no arguments to the <code class=\"fm-code-in-text\">UseRouting<\/code> method. The <code class=\"fm-code-in-text\">UseEndpoints<\/code> method receives a function that accepts an <code class=\"fm-code-in-text\">IEndpointRouteBuilder<\/code> object and uses it to create routes using the extension methods described in table 13.3.<a id=\"calibre_link-1719\"><\/a><a id=\"calibre_link-1720\"><\/a><a id=\"calibre_link-1721\"><\/a><a id=\"calibre_link-1123\"><\/a><\/p>\n<p class=\"body\">The code in listing 13.5 contains a <code class=\"fm-code-in-text\">#pragma<\/code> directive that prevents a compiler warning, which I explain in the next section.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> There are also extension methods that set up endpoints for other parts of ASP.NET Core, such as the MVC Framework, as explained in part 3.<\/p>\n<p class=\"fm-table-caption\">Table 13.3 The IEndpointRouteBuilder extension methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1722\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MapGet(pattern, endpoint)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method routes HTTP GET requests that match the URL pattern to the endpoint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MapPost(pattern, endpoint)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method routes HTTP POST requests that match the URL pattern to the endpoint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MapPut(pattern, endpoint)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method routes HTTP PUT requests that match the URL pattern to the endpoint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MapDelete(pattern, endpoint)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method routes HTTP DELETE requests that match the URL pattern to the endpoint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MapMethods(pattern, methods, endpoint)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method routes requests made with one of the specified HTTP methods that match the URL pattern to the endpoint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Map(pattern, endpoint)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method routes all HTTP requests that match the URL pattern to the endpoint.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Endpoints are defined using <code class=\"fm-code-in-text\">RequestDelegate<\/code>, which is the same delegate used by conventional middleware, so endpoints are asynchronous methods that receive an <code class=\"fm-code-in-text\">HttpContext<\/code> object and use it to generate a response. This means that the features described in chapter 12 for middleware components can also be used in endpoints.<a id=\"calibre_link-1723\"><\/a><\/p>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/routing to test the new route. When matching a request, the routing middleware applies the route\u2019s URL pattern to the path section of the URL. The path is separated from the hostname by the <code class=\"fm-code-in-text\">\/<\/code> character, as shown in figure 13.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre102\" src=\"\/images\/proaspnetcore7\/000098.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.2 The URL path<\/p>\n<\/div>\n<p class=\"body\">The path in the URL matches the pattern specified in the route.<\/p>\n<pre class=\"programlisting\">...\nendpoints.MapGet(\"<b class=\"fm-bold\">routing<\/b>\", async context =&gt; {\n...<\/pre>\n<p class=\"body\">URL patterns are conventionally expressed without a leading <code class=\"fm-code-in-text\">\/<\/code> character, which isn\u2019t part of the URL path. When the request URL path matches the URL pattern, the request will be forwarded to the endpoint function, which generates the response shown in figure 13.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre103\" src=\"\/images\/proaspnetcore7\/000099.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.3 Using an endpoint to generate a response<\/p>\n<\/div>\n<p class=\"body\">The routing middleware short-circuits the pipeline when a route matches a URL so that the response is generated only by the route\u2019s endpoint. The request isn\u2019t forwarded to other endpoints or middleware components that appear later in the request pipeline.<\/p>\n<p class=\"body\">If the request URL isn\u2019t matched by any route, then the routing middleware passes the request to the next middleware component in the request pipeline. To test this behavior, request the http:\/\/localhost:5000\/notrouted URL, whose path doesn\u2019t match the pattern in the route defined in listing 13.5.<\/p>\n<p class=\"body\">The routing middleware can\u2019t match the URL path to a route and forwards the request, which reaches the terminal middleware, producing the response shown in figure 13.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre104\" src=\"\/images\/proaspnetcore7\/000100.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.4 Requesting a URL for which there is no matching route<\/p>\n<\/div>\n<p class=\"body\">Endpoints generate responses in the same way as the middleware components demonstrated in earlier chapters: they receive an <code class=\"fm-code-in-text\">HttpContext<\/code> object that provides access to the request and response through <code class=\"fm-code-in-text\">HttpRequest<\/code> and <code class=\"fm-code-in-text\">HttpResponse<\/code> objects. This means that any middleware component can also be used as an endpoint. Listing 13.6 adds a route that uses the <code class=\"fm-code-in-text\">Capital<\/code> and <code class=\"fm-code-in-text\">Population<\/code> middleware components as endpoints.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.6 Using components as endpoints in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">\/\/app.UseMiddleware&lt;Population&gt;();<\/b>\n<b class=\"fm-bold\">\/\/app.UseMiddleware&lt;Capital&gt;();<\/b>\n\napp.UseRouting();\n\n#pragma warning disable ASP0014\n\napp.UseEndpoints(endpoints =&gt; {\n    endpoints.MapGet(\"routing\", async context =&gt; {\n        await context.Response.WriteAsync(\"Request Was Routed\");\n    });\n    <b class=\"fm-bold\">endpoints.MapGet(\"capital\/uk\", new Capital().Invoke);<\/b>\n    <b class=\"fm-bold\">endpoints.MapGet(\"population\/paris\", new Population().Invoke);<\/b>\n});\n\napp.Run(async (context) =&gt; {\n    await context.Response.WriteAsync(\"Terminal Middleware Reached\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">Using middleware components like this is awkward because I need to create new instances of the classes to select the <code class=\"fm-code-in-text\">Invoke<\/code> method as the endpoint. The URL patterns used by the routes support only some of the URLs that the middleware components support, but it is useful to understand that endpoints rely on features that are familiar from earlier chapters. To test the new routes, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/capital\/uk and http:\/\/localhost:5000\/population\/paris, which will produce the results shown in figure 13.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre105\" src=\"\/images\/proaspnetcore7\/000101.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.5 Using middleware components as endpoints<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-243\">13.1.3 Simplifying the pipeline configuration<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1132\"><\/a>I demonstrated the use of the <code class=\"fm-code-in-text\">UseRouting<\/code> and <code class=\"fm-code-in-text\">UseEndpoints<\/code> method because I wanted to emphasize that routing builds on the standard pipeline features and is implemented using regular middleware components.<\/p>\n<p class=\"body\">However, as part of a drive to simplify the configuration of ASP.NET Core applications, Microsoft automatically applies the <code class=\"fm-code-in-text\">UseRouting<\/code> and <code class=\"fm-code-in-text\">UseEndpoints<\/code> methods to the request pipeline, which means that the methods described in table 13.3 can be used directly on the <code class=\"fm-code-in-text\">WebApplication<\/code> object returned by the <code class=\"fm-code-in-text\">WebApplication.CreateBuilder<\/code> method, as shown in listing 13.7.<\/p>\n<p class=\"body\">When you call the <code class=\"fm-code-in-text\">UseEndpoints<\/code> method, the C# code analyzer generates a warning that suggests registering routes at the top level of the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.7 Simplifying the code in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.UseRouting();\n\n<b class=\"fm-bold\">\/\/#pragma warning disable ASP0014<\/b>\n\n<b class=\"fm-bold\">\/\/app.UseEndpoints(endpoints =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/    endpoints.MapGet(\"routing\", async context =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/        await context.Response.WriteAsync(\"Request Was Routed\");<\/b>\n<b class=\"fm-bold\">\/\/    });<\/b>\n<b class=\"fm-bold\">\/\/    endpoints.MapGet(\"capital\/uk\", new Capital().Invoke);<\/b>\n<b class=\"fm-bold\">\/\/    endpoints.MapGet(\"population\/paris\", new Population().Invoke);<\/b>\n<b class=\"fm-bold\">\/\/});<\/b>\n\n<b class=\"fm-bold\">app.MapGet(\"routing\", async context =&gt; {<\/b>\n    <b class=\"fm-bold\">await context.Response.WriteAsync(\"Request Was Routed\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n<b class=\"fm-bold\">app.MapGet(\"capital\/uk\", new Capital().Invoke);<\/b>\n<b class=\"fm-bold\">app.MapGet(\"population\/paris\", new Population().Invoke);<\/b>\n<b class=\"fm-bold\">\/\/app.Run(async (context) =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/    await context.Response.WriteAsync(\"Terminal Middleware Reached\");<\/b>\n<b class=\"fm-bold\">\/\/});<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">WebApplication<\/code> class implements the <code class=\"fm-code-in-text\">IEndpointRouteBuilder<\/code> interface, which means that endpoints can be created more concisely. Behind the scenes, the routing middleware is still responsible for matching requests and selecting routes.<\/p>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/capital\/uk and http:\/\/localhost:5000\/population\/paris, which will produce the results shown in figure 13.5.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Avoiding the direct route registration pitfall<\/p>\n<p class=\"fm-sidebar-text\">Notice that I have removed the terminal middleware component from the pipeline. In the previous example, the routing middleware I added to the pipeline explicitly would forward requests along the pipeline only if none of the routes matched. Defining routes directly, as in listing 13.7, changes this behavior so that requests are always forwarded, which means the terminal middleware will be used for every request.<\/p>\n<p class=\"fm-sidebar-text\">I show you an alternative to the terminal middleware that is part of the URL routing system in the \u201cDefining Fallback Routes\u201d section, but it is important to understand that using the simplified pipeline configuration doesn\u2019t just reduce the amount of code in the <code class=\"fm-code-in-text1\">Program.cs<\/code> file and can alter the way that requests are processed.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-244\">13.1.4 Understanding URL patterns<\/h3>\n<p class=\"body\">Using middleware components as endpoints shows that URL routing builds on the standard ASP.NET Core platform features. Although the URLs that the application handles can be seen by examining the routes, not all of the URLs understood by the <code class=\"fm-code-in-text\">Capital<\/code> and <code class=\"fm-code-in-text\">Population<\/code> classes are routed, and there have been no efficiency gains since the URL is processed once by the routing middleware to select the route and again by the <code class=\"fm-code-in-text\">Capital<\/code> or <code class=\"fm-code-in-text\">Population<\/code> class to extract the data values they require.<a id=\"calibre_link-1724\"><\/a><a id=\"calibre_link-1725\"><\/a><\/p>\n<p class=\"body\">Making improvements requires understanding more about how URL patterns are used. When a request arrives, the routing middleware processes the URL to extract the segments from its path, which are the sections of the path separated by the <code class=\"fm-code-in-text\">\/<\/code> character, as shown in figure 13.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre106\" src=\"\/images\/proaspnetcore7\/000102.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.6 The URL segments<\/p>\n<\/div>\n<p class=\"body\">The routing middleware also extracts the segments from the URL routing pattern, as shown in figure 13.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre107\" src=\"\/images\/proaspnetcore7\/000103.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.7 The URL pattern segments<\/p>\n<\/div>\n<p class=\"body\">To route a request, the segments from the URL pattern are compared to those from the request to see whether they match. The request is routed to the endpoint if its path contains the same number of segments and each segment has the same content as those in the URL pattern, as summarized in table 13.4.<\/p>\n<p class=\"fm-table-caption\">Table 13.4 Matching URL segments<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1726\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">URL Path<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/capital<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No match&mdash;too few segments<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/capital\/europe\/uk<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No match&mdash;too many segments<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/name\/uk<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No match&mdash;first segment is not <code class=\"fm-code-in-text1\">capital<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/capital\/uk<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Matches<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-245\">13.1.5 Using segment variables in URL patterns<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1129\"><\/a>The URL pattern used in listing 13.7 uses <i class=\"fm-italics\">literal segments<\/i>, also known as <i class=\"fm-italics\">static segments<\/i>, which match requests using fixed strings. The first segment in the pattern will match only those requests whose path has <code class=\"fm-code-in-text\">capital<\/code> as the first segment, for example, and the second segment in the pattern will match only those requests whose second segment is <code class=\"fm-code-in-text\">uk<\/code>. Put these together, and you can see why the route matches only those requests whose path is <code class=\"fm-code-in-text\">\/capital\/uk<\/code>.<\/p>\n<p class=\"body\"><i class=\"fm-italics\">Segment variables<\/i>, also known as <i class=\"fm-italics\">route parameters<\/i>, expand the range of path segments that a pattern segment will match, allowing more flexible routing. Segment variables are given a name and are denoted by curly braces (the <code class=\"fm-code-in-text\">{<\/code> and <code class=\"fm-code-in-text\">}<\/code> characters), as shown in listing 13.8.<a id=\"calibre_link-1727\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.8 Using segment variables in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapGet(\"{first}\/{second}\/{third}\", async context =&gt; {<\/b>\n    <b class=\"fm-bold\">await context.Response.WriteAsync(\"Request Was Routed\\n\");<\/b>\n    <b class=\"fm-bold\">foreach (var kvp in context.Request.RouteValues) {<\/b>\n        <b class=\"fm-bold\">await context.Response<\/b>\n            <b class=\"fm-bold\">.WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");<\/b>\n    <b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">});<\/b>\napp.MapGet(\"capital\/uk\", new Capital().Invoke);\napp.MapGet(\"population\/paris\", new Population().Invoke);\n\napp.Run();<\/pre>\n<p class=\"body\">The URL pattern <code class=\"fm-code-in-text\">{first}\/{second}\/{third}<\/code> matches URLs whose path contains three segments, regardless of what those segments contain. When a segment variable is used, the routing middleware provides the endpoint with the contents of the URL path segment they have matched. This content is available through the <code class=\"fm-code-in-text\">HttpRequest.RouteValues<\/code> property, which returns a <code class=\"fm-code-in-text\">RouteValuesDictionary<\/code> object. Table 13.5 describes the most useful <code class=\"fm-code-in-text\">RouteValuesDictionary<\/code> members.<a id=\"calibre_link-1728\"><\/a><a id=\"calibre_link-1729\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> There are reserved words that cannot be used as the names for segment variables: <code class=\"fm-code-in-text1\">action<\/code>, <code class=\"fm-code-in-text1\">area<\/code>, <code class=\"fm-code-in-text1\">controller<\/code>, <code class=\"fm-code-in-text1\">handler<\/code>, and <code class=\"fm-code-in-text1\">page<\/code>.<a id=\"calibre_link-1730\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 13.5 Useful RouteValuesDictionary members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1731\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[key]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The class defines an indexer that allows values to be retrieved by key.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Keys<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the collection of segment variable names.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Values<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the collection of segment variable values.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Count<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the number of segment variables.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ContainsKey(key)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns <code class=\"fm-code-in-text1\">true<\/code> if the route data contains a value for the specified key.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">RouteValuesDictionary<\/code> class is enumerable, which means that it can be used in a <code class=\"fm-code-in-text\">foreach<\/code> loop to generate a sequence of <code class=\"fm-code-in-text\">KeyValuePair&lt;string, object&gt;<\/code> objects, each of which corresponds to the name of a segment variable and the corresponding value extracted from the request URL. The endpoint in listing 13.8 enumerates the <code class=\"fm-code-in-text\">HttpRequest.RouteValues<\/code> property to generate a response that lists the names and values of the segment variables matched by the URL pattern.<\/p>\n<p class=\"body\">The names of the segment variables are <code class=\"fm-code-in-text\">first<\/code>, <code class=\"fm-code-in-text\">second<\/code>, and <code class=\"fm-code-in-text\">third<\/code>, and you can see the values extracted from the URL by restarting ASP.NET Core and requesting any three-segment URL, such as http:\/\/localhost:5000\/apples\/oranges\/cherries, which produces the response shown in figure 13.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre108\" src=\"\/images\/proaspnetcore7\/000104.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.8 Using segment variables<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding route selection<\/p>\n<p class=\"fm-sidebar-text\">When processing a request, the middleware finds all the routes that can match the request and gives each a score, and the route with the lowest score is selected to handle the route. The scoring process is complex, but the effect is that the most specific route receives the request. This means literal segments are given preference over segment variables and that segment variables with constraints are given preference over those without (constraints are described in the \u201cConstraining Segment Matching\u201d section later in this chapter). The scoring system can produce surprising results, and you should check to make sure that the URLs supported by your application are matched by the routes you expect.<a id=\"calibre_link-1732\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">If two routes have the same score, meaning they are equally suited to routing the request, then an exception will be thrown, indicating an ambiguous routing selection. See the \u201cAvoiding Ambiguous Route Exceptions\u201d section later in the chapter for details of how to avoid ambiguous routes.<\/p>\n<\/div>\n<p class=\"fm-head2\">Refactoring middleware into an endpoint<\/p>\n<p class=\"body\">Endpoints usually rely on the routing middleware to provide specific segment variables, rather than enumerating all the segment variables. By relying on the URL pattern to provide a specific value, I can refactor the <code class=\"fm-code-in-text\">Capital<\/code> and <code class=\"fm-code-in-text\">Population<\/code> classes to depend on the route data, as shown in listing 13.9.<a id=\"calibre_link-1131\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.9 Depending on the route data in the Capital.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n    <b class=\"fm-bold\">public class Capital {<\/b>\n        \n        <b class=\"fm-bold\">public static async Task Endpoint(HttpContext context) {<\/b>\n            string? capital = null;\n            <b class=\"fm-bold\">string? country<\/b> \n                <b class=\"fm-bold\">= context.Request.RouteValues[\"country\"] as string;<\/b>\n            <b class=\"fm-bold\">switch ((country ?? \"\").ToLower()) {<\/b>\n                case \"uk\":\n                    capital = \"London\";\n                    break;\n                case \"france\":\n                    capital = \"Paris\";\n                    break;\n                case \"monaco\":\n                    context.Response.Redirect($\"\/population\/{country}\");\n                    return;\n            }\n            if (capital != null) {\n                <b class=\"fm-bold\">await context.Response<\/b>\n                    <b class=\"fm-bold\">.WriteAsync($\"{capital} is the capital of {country}\");<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">context.Response.StatusCode<\/b> \n                    <b class=\"fm-bold\">= StatusCodes.Status404NotFound;<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">Middleware components can be used as endpoints, but the opposite isn\u2019t true once there is a dependency on the data provided by the routing middleware. In listing 13.9, I used the route data to get the value of a segment variable named <code class=\"fm-code-in-text\">country<\/code> through the indexer defined by the <code class=\"fm-code-in-text\">RouteValuesDictionary<\/code> class.<\/p>\n<pre class=\"programlisting\">...\nstring country = <b class=\"fm-bold\">context.Request.RouteValues[\"country\"]<\/b> as string;\n...<\/pre>\n<p class=\"body\">The indexer returns an <code class=\"fm-code-in-text\">object<\/code> value that is cast to a <code class=\"fm-code-in-text\">string<\/code> using the <code class=\"fm-code-in-text\">as<\/code> keyword. The listing removes the statements that pass the request along the pipeline, which the routing middleware handles on behalf of endpoints.<\/p>\n<p class=\"body\">The use of the segment variable means that requests may be routed to the endpoint with values that are not supported, so I added a statement that returns a 404 status code for countries the endpoint doesn\u2019t understand.<\/p>\n<p class=\"body\">I also removed the constructors and replaced the <code class=\"fm-code-in-text\">Invoke<\/code> instance method with a <code class=\"fm-code-in-text\">static<\/code> method named <code class=\"fm-code-in-text\">Endpoint<\/code>, which better fits with the way that endpoints are used in routes. Listing 13.10 applies the same set of changes to the <code class=\"fm-code-in-text\">Population<\/code> class, transforming it from a standard middleware component into an endpoint that depends on the routing middleware to process URLs.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.10 Depending on route data in the Population.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n    public class Population {\n        \n        <b class=\"fm-bold\">public static async Task Endpoint(HttpContext context) {<\/b>\n            <b class=\"fm-bold\">string? city = context.Request.RouteValues[\"city\"] as string;<\/b>\n            int? pop = null;\n            <b class=\"fm-bold\">switch ((city ?? \"\").ToLower()) {<\/b>\n                case \"london\":\n                    pop = 8_136_000;\n                    break;\n                case \"paris\":\n                    pop = 2_141_000;\n                    break;\n                case \"monaco\":\n                    pop = 39_000;\n                    break;\n            }\n            if (pop.HasValue) {\n                <b class=\"fm-bold\">await context.Response<\/b>\n                    <b class=\"fm-bold\">.WriteAsync($\"City: {city}, Population: {pop}\");<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">context.Response.StatusCode<\/b> \n                    <b class=\"fm-bold\">= StatusCodes.Status404NotFound;<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The change to static methods tidies up the use of the endpoints when defining routes, as shown in listing 13.11.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.11 Updating routes in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.MapGet(\"{first}\/{second}\/{third}\", async context =&gt; {\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\n<b class=\"fm-bold\">app.MapGet(\"capital\/{country}\", Capital.Endpoint);<\/b>\n<b class=\"fm-bold\">app.MapGet(\"population\/{city}\", Population.Endpoint);<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1127\"><\/a>The new routes match URLs whose path has two segments, the first of which is <code class=\"fm-code-in-text\">capital<\/code> or <code class=\"fm-code-in-text\">population<\/code>. The contents of the second segment are assigned to the segment variables named <code class=\"fm-code-in-text\">country<\/code> and <code class=\"fm-code-in-text\">city<\/code>, allowing the endpoints to support the full set of URLs that were handled at the start of the chapter, without the need to process the URL directly. To test the new routes, restart ASP.NET Core and request http:\/\/localhost:5000\/capital\/uk and http:\/\/localhost:5000\/population\/london, which will produce the responses shown in figure 13.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre109\" src=\"\/images\/proaspnetcore7\/000105.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.9 Using segment variables in endpoints<\/p>\n<\/div>\n<p class=\"body\">These changes address two of the problems I described at the start of the chapter. Efficiency has improved because the URL is processed only once by the routing middleware and not by multiple components. And it is easier to see the URLs that each endpoint supports because the URL patterns show how requests will be matched.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-246\">13.1.6 Generating URLs from routes<\/h3>\n<p class=\"body\">The final problem was the difficulty in making changes. The <code class=\"fm-code-in-text\">Capital<\/code> endpoint still has a hardwired dependency on the URL that the <code class=\"fm-code-in-text\">Population<\/code> endpoint supports. To break this dependency, the routing system allows URLs to be generated by supplying data values for segment variables. The first step is to assign a name to the route that will be the target of the URL that is generated, as shown in listing 13.12.<a id=\"calibre_link-1733\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.12 Naming a route in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.MapGet(\"{first}\/{second}\/{third}\", async context =&gt; {\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\napp.MapGet(\"capital\/{country}\", Capital.Endpoint);\n<b class=\"fm-bold\">app.MapGet(\"population\/{city}\", Population.Endpoint)<\/b>\n    <b class=\"fm-bold\">.WithMetadata(new RouteNameMetadata(\"population\"));<\/b>\n        \napp.Run();<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1141\"><\/a>The <code class=\"fm-code-in-text\">WithMetadata<\/code> method is used on the result from the <code class=\"fm-code-in-text\">MapGet<\/code> method to assign metadata to the route. The only metadata required for generating URLs is a name, which is assigned by passing a new <code class=\"fm-code-in-text\">RouteNameMetadata<\/code> object, whose constructor argument specifies the name that will be used to refer to the route. The effect of the change in the listing is to assign the route the name <code class=\"fm-code-in-text\">population<\/code>.<a id=\"calibre_link-1734\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Naming routes helps to avoid links being generated that target a route other than the one you expect, but they can be omitted, in which case the routing system will try to find the best matching route.<\/p>\n<p class=\"body\">In listing 13.13, I have revised the <code class=\"fm-code-in-text\">Capital<\/code> endpoint to remove the direct dependency on the <code class=\"fm-code-in-text\">\/population<\/code> URL and rely on the routing features to generate a URL.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.13 Generating a URL in the Capital.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n    public class Capital {\n        \n        public static async Task Endpoint(HttpContext context) {\n            string? capital = null;\n            string? country \n                = context.Request.RouteValues[\"country\"] as string;\n            switch ((country ?? \"\").ToLower()) {\n                case \"uk\":\n                    capital = \"London\";\n                    break;\n                case \"france\":\n                    capital = \"Paris\";\n                    break;\n                <b class=\"fm-bold\">case \"monaco\":<\/b>\n                    <b class=\"fm-bold\">LinkGenerator? generator =<\/b>\n                      <b class=\"fm-bold\">context.RequestServices.GetService&lt;LinkGenerator&gt;();<\/b>\n                    <b class=\"fm-bold\">string? url = generator?.GetPathByRouteValues(context,<\/b>\n                        <b class=\"fm-bold\">\"population\", new { city = country });<\/b>\n                    <b class=\"fm-bold\">if (url != null) {<\/b>\n                        <b class=\"fm-bold\">context.Response.Redirect(url);<\/b>\n                    <b class=\"fm-bold\">}<\/b>\n                    <b class=\"fm-bold\">return;<\/b>\n            }\n            if (capital != null) {\n                await context.Response\n                    .WriteAsync($\"{capital} is the capital of {country}\");\n            } else {\n                context.Response.StatusCode \n                    = StatusCodes.Status404NotFound;\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1128\"><\/a>URLs are generated using the <code class=\"fm-code-in-text\">LinkGenerator<\/code> class. You can\u2019t just create a new <code class=\"fm-code-in-text\">LinkGenerator<\/code> instance; one must be obtained using the dependency injection feature that is described in chapter 14. For this chapter, it is enough to know that this statement obtains the <code class=\"fm-code-in-text\">LinkGenerator<\/code> object that the endpoint will use:<a id=\"calibre_link-1735\"><\/a><\/p>\n<pre class=\"programlisting\">...\nLinkGenerator? generator = \n    <b class=\"fm-bold\">context.RequestServices.GetService&lt;LinkGenerator&gt;()<\/b>;\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">LinkGenerator<\/code> class provides the <code class=\"fm-code-in-text\">GetPathByRouteValues<\/code> method, which is used to generate the URL that will be used in the redirection.<\/p>\n<pre class=\"programlisting\">...\ngenerator?.<b class=\"fm-bold\">GetPathByRouteValues<\/b>(context,\"population\", \n    new { city = country });\n...<\/pre>\n<p class=\"body\">The arguments to the <code class=\"fm-code-in-text\">GetPathByRouteValues<\/code> method are the endpoint\u2019s <code class=\"fm-code-in-text\">HttpContext<\/code> object, the name of the route that will be used to generate the link, and an object that is used to provide values for the segment variables. The <code class=\"fm-code-in-text\">GetPathByRouteValues<\/code> method returns a URL that will be routed to the <code class=\"fm-code-in-text\">Population<\/code> endpoint, which can be confirmed by restarting ASP.NET Core and requesting the http:\/\/localhost:5000\/capital\/monaco URL. The request will be routed to the <code class=\"fm-code-in-text\">Capital<\/code> endpoint, which will generate the URL and use it to redirect the browser, producing the result shown in figure 13.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre110\" src=\"\/images\/proaspnetcore7\/000106.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.10 Generating a URL<\/p>\n<\/div>\n<p class=\"body\">The benefit of this approach is that the URL is generated from the URL pattern in the named route, which means a change in the URL pattern is reflected in the generated URLs, without the need to make changes to endpoints. To demonstrate, listing 13.14 changes the URL pattern.<a id=\"calibre_link-1120\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.14 Changing a URL pattern in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.MapGet(\"{first}\/{second}\/{third}\", async context =&gt; {\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\napp.MapGet(\"capital\/{country}\", Capital.Endpoint);\n<b class=\"fm-bold\">app.MapGet(\"size\/{city}\", Population.Endpoint)<\/b>\n    <b class=\"fm-bold\">.WithMetadata(new RouteNameMetadata(\"population\"));<\/b>\n        \napp.Run();<\/pre>\n<p class=\"body\">The name assigned to the route is unchanged, which ensures that the same endpoint is targeted by the generated URL. To see the effect of the new pattern, restart ASP.NET Core and request the http:\/\/localhost:5000\/capital\/monaco URL again. The redirection is to a URL that is matched by the modified pattern, as shown in figure 13.11. This feature addresses the final problem that I described at the start of the chapter, making it easy to change the URLs that an application supports.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre111\" src=\"\/images\/proaspnetcore7\/000107.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.11 Changing the URL pattern<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">URL routing and areas<\/p>\n<p class=\"fm-sidebar-text\">The URL routing system supports a feature called <i class=\"fm-italics\">areas<\/i>, which allows separate sections of the application to have their own controllers, views, and Razor Pages. I have not described the areas feature in this book because it is not widely used, and when it is used, it tends to cause more problems than it solves. If you want to break up an application, then I recommend creating separate projects.<a id=\"calibre_link-1736\"><\/a><\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-247\">13.2 Managing URL matching<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1134\"><\/a>The previous section introduced the basic URL routing features, but most applications require more work to ensure that URLs are routed correctly, either to increase or to restrict the range of URLs that are matched by a route. In the sections that follow, I show you the different ways that URL patterns can be adjusted to fine-tune the matching process.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-248\">13.2.1 Matching multiple values from a single URL segment<\/h3>\n<p class=\"body\">Most segment variables correspond directly to a segment in the URL path, but the routing middleware is able to perform more complex matches, allowing a single segment to be matched to a variable while discarding unwanted characters. Listing 13.15 defines a route that matches only part of a URL segment to a variable.<a id=\"calibre_link-1737\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.15 Matching part of a segment in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapGet(\"files\/{filename}.{ext}\", async context =&gt; {<\/b>\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\napp.MapGet(\"capital\/{country}\", Capital.Endpoint);\napp.MapGet(\"size\/{city}\", Population.Endpoint)\n    .WithMetadata(new RouteNameMetadata(\"population\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">A URL pattern can contain as many segment variables as you need, as long as they are separated by a static string. The requirement for a static separator is so the routing middleware knows where the content for one variable ends and the content for the next starts. The pattern in listing 13.15 matches segment variables named <code class=\"fm-code-in-text\">filename<\/code> and <code class=\"fm-code-in-text\">ext<\/code>, which are separated by a period; this pattern is often used by process file names. To see how the pattern matches URLs, restart ASP.NET Core and request the http:\/\/localhost:5000\/files\/myfile.txt URL, which will produce the response shown in figure 13.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre112\" src=\"\/images\/proaspnetcore7\/000108.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.12 Matching multiple values from a single path segment<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Avoiding the complex pattern mismatching pitfall<\/p>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1138\"><\/a>The order of the segment variables in figure 13.12 shows that pattern segments that contain multiple variables are matched from right to left. This isn\u2019t important most of the time, because endpoints can\u2019t rely on a specific key order, but it does show that complex URL patterns are handled differently, which reflects the difficulty in matching them.<\/p>\n<p class=\"fm-sidebar-text\">In fact, the matching process is so difficult that there can be unexpected matching failures. The specific failures change with each release of ASP.NET Core as the matching process is adjusted to address problems, but the adjustments often introduce new issues. At the time of writing, there is a problem with URL patterns where the content that should be matched by the first variable also appears as a literal string at the start of a segment. This is easier to understand with an example, as shown here:<\/p>\n<pre class=\"programlisting\">...\napp.MapGet(\"<b class=\"fm-bold\">example\/red{color}<\/b>\", async context =&gt; {\n...<\/pre>\n<p class=\"fm-sidebar-text\">This pattern has a segment that begins with the literal string <code class=\"fm-code-in-text1\">red<\/code>, followed by a segment variable named <code class=\"fm-code-in-text1\">color<\/code>. The routing middleware will correctly match the pattern against the URL path <code class=\"fm-code-in-text1\">example\/redgreen<\/code>, and the value of the <code class=\"fm-code-in-text1\">color<\/code> route variable will be <code class=\"fm-code-in-text1\">green<\/code>. However, the URL path <code class=\"fm-code-in-text1\">example\/redredgreen<\/code> won\u2019t match because the matching process confuses the position of the literal content with the first part of the content that should be assigned to the <code class=\"fm-code-in-text1\">color<\/code> variable. This problem may be fixed by the time you read this book, but there will be other issues with complex patterns. It is a good idea to keep URL patterns as simple as possible and make sure you get the matching results you expect.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-249\">13.2.2 Using default values for segment variables<\/h3>\n<p class=\"body\">Patterns can be defined with default values that are used when the URL doesn\u2019t contain a value for the corresponding segment, increasing the range of URLs that a route can match. Listing 13.16 shows the use of default values in a pattern.<a id=\"calibre_link-1738\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.16 Using Default Values in the Program.cs File in the Platform Folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.MapGet(\"files\/{filename}.{ext}\", async context =&gt; {\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\n<b class=\"fm-bold\">app.MapGet(\"capital\/{country=France}\", Capital.Endpoint);<\/b>\napp.MapGet(\"size\/{city}\", Population.Endpoint)\n    .WithMetadata(new RouteNameMetadata(\"population\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">Default values are defined using an equal sign and the value to be used. The default value in the listing uses the value <code class=\"fm-code-in-text\">France<\/code> when there is no second segment in the URL path. The result is that the range of URLs that can be matched by the route increases, as described in table 13.6.<\/p>\n<p class=\"fm-table-caption\">Table 13.6 Matching URLs<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1739\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">URL Path<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No match&mdash;too few segments<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/<\/code><code class=\"fm-code-in-text1\">city<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No match&mdash;first segment isn\u2019t <code class=\"fm-code-in-text1\">capital<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/<\/code><code class=\"fm-code-in-text1\">capital<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Matches, <code class=\"fm-code-in-text1\">country<\/code> variable is <code class=\"fm-code-in-text1\">France<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/capital\/uk<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Matches, <code class=\"fm-code-in-text1\">country<\/code> variable is <code class=\"fm-code-in-text1\">uk<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/capital\/europe\/italy<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No match&mdash;too many segments<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">To test the default value, restart ASP.NET Core and navigate to http:\/\/localhost:5000\/capital, which will produce the result shown in figure 13.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre113\" src=\"\/images\/proaspnetcore7\/000109.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.13 Using a default value for a segment variable<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-250\">13.2.3 Using optional segments in a URL Pattern<\/h3>\n<p class=\"body\">Default values allow URLs to be matched with fewer segments, but the use of the default value isn\u2019t obvious to the endpoint. Some endpoints define their own responses to deal with URLs that omit segments, for which <i class=\"fm-italics\">optional segments<\/i> are used. To prepare, listing 13.17 updates the <code class=\"fm-code-in-text\">Population<\/code> endpoint so that it uses a default value when no <code class=\"fm-code-in-text\">city<\/code> value is available in the routing data.<a id=\"calibre_link-1740\"><\/a><a id=\"calibre_link-1139\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.17 Using a default value in the Population.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n    public class Population {\n        \n        public static async Task Endpoint(HttpContext context) {\n            <b class=\"fm-bold\">string city = context.Request.RouteValues[\"city\"]<\/b> \n                <b class=\"fm-bold\">as string ?? \"london\";<\/b>\n            int? pop = null;\n            <b class=\"fm-bold\">switch (city.ToLower()) {<\/b>\n                case \"london\":\n                    pop = 8_136_000;\n                    break;\n                case \"paris\":\n                    pop = 2_141_000;\n                    break;\n                case \"monaco\":\n                    pop = 39_000;\n                    break;\n            }\n            if (pop.HasValue) {\n                await context.Response\n                    .WriteAsync($\"City: {city}, Population: {pop}\");\n            } else {\n                context.Response.StatusCode \n                    = StatusCodes.Status404NotFound;\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The change uses <code class=\"fm-code-in-text\">london<\/code> as the default value because there is no <code class=\"fm-code-in-text\">city<\/code> segment variable available. Listing 13.18 updates the route for the <code class=\"fm-code-in-text\">Population<\/code> endpoint to make the second segment optional.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.18 Using an optional segment in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.MapGet(\"files\/{filename}.{ext}\", async context =&gt; {\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\napp.MapGet(\"capital\/{country=France}\", Capital.Endpoint);\n<b class=\"fm-bold\">app.MapGet(\"size\/{city?}\", Population.Endpoint)<\/b>\n    .WithMetadata(new RouteNameMetadata(\"population\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">Optional segments are denoted with a question mark (the <code class=\"fm-code-in-text\">?<\/code> character) after the variable name and allow the route to match URLs that don\u2019t have a corresponding path segment, as described in table 13.7.<a id=\"calibre_link-1133\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 13.7 Matching URLs<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1741\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">URL Path<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No match&mdash;too few segments.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/city<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No match&mdash;first segment isn\u2019t <code class=\"fm-code-in-text1\">size<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/size<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Matches. No value for the <code class=\"fm-code-in-text1\">city<\/code> variable is provided to the endpoint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/size\/paris<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Matches, <code class=\"fm-code-in-text1\">city<\/code> variable is <code class=\"fm-code-in-text1\">paris<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">\/size\/europe\/italy<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No match&mdash;too many segments.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">To test the optional segment, restart ASP.NET Core and navigate to http:\/\/localhost:5000\/size, which will produce the response shown in figure 13.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre114\" src=\"\/images\/proaspnetcore7\/000110.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.14 Using an optional segment<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-251\">13.2.4 Using a catchall segment variable<\/h3>\n<p class=\"body\">Optional segments allow a pattern to match shorter URL paths. A <i class=\"fm-italics\">catchall<\/i> segment does the opposite and allows routes to match URLs that contain more segments than the pattern. A catchall segment is denoted with an asterisk before the variable name, as shown in listing 13.19.<a id=\"calibre_link-1742\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.19 Using a catchall segment in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapGet(\"{first}\/{second}\/{*catchall}\", async context =&gt; {<\/b>\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\napp.MapGet(\"capital\/{country=France}\", Capital.Endpoint);\napp.MapGet(\"size\/{city?}\", Population.Endpoint)\n    .WithMetadata(new RouteNameMetadata(\"population\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">The new pattern contains two-segment variables and a catchall, and the result is that the route will match any URL whose path contains two or more segments. There is no upper limit to the number of segments that the URL pattern in this route will match, and the contents of any additional segments are assigned to the segment variable named <code class=\"fm-code-in-text\">catchall<\/code>. Restart ASP.NET Core and navigate to http:\/\/localhost:5000\/one\/two\/three\/four, which produces the response shown in figure 13.15.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Notice that the segments captured by the catchall are presented in the form <i class=\"fm-italics\">segment<\/i>\/<i class=\"fm-italics\">segment<\/i>\/<i class=\"fm-italics\">segment<\/i> and that the endpoint is responsible for processing the string to break out the individual segments.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre115\" src=\"\/images\/proaspnetcore7\/000111.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.15 Using a catchall segment variable<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-252\">13.2.5 Constraining segment matching<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1135\"><\/a>Default values, optional segments, and catchall segments all increase the range of URLs that a route will match. Constraints have the opposite effect and restrict matches. This can be useful if an endpoint can deal only with specific segment contents or if you want to differentiate matching closely related URLs for different endpoints. Constraints are applied by a colon (the <code class=\"fm-code-in-text\">:<\/code> character) and a constraint type after a segment variable name, as shown in listing 13.20.<a id=\"calibre_link-1743\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.20 Applying constraints in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapGet(\"{first:int}\/{second:bool}\", async context =&gt; {<\/b>\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\napp.MapGet(\"capital\/{country=France}\", Capital.Endpoint);\napp.MapGet(\"size\/{city?}\", Population.Endpoint)\n    .WithMetadata(new RouteNameMetadata(\"population\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">This example constrains the first segment variable so it will match only the path segments that can be parsed to an <code class=\"fm-code-in-text\">int<\/code> value, and it constrains the second segment so it will match only the path segments that can be parsed to a <code class=\"fm-code-in-text\">bool<\/code>. Values that don\u2019t match the constraints won\u2019t be matched by the route. Table 13.8 describes the URL pattern constraints.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Some of the constraints match types whose format can differ based on locale. The routing middleware doesn\u2019t handle localized formats and will match only those values that are expressed in the invariant culture format.<\/p>\n<p class=\"fm-table-caption\">Table 13.8 The URL pattern constraints<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1744\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Constraint<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">alpha<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches the letters <i class=\"fm-italics\">a<\/i> to <i class=\"fm-italics\">z<\/i> (and is case-insensitive).<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">bool<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches <code class=\"fm-code-in-text1\">true<\/code> and <code class=\"fm-code-in-text1\">false<\/code> (and is case-insensitive).<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">datetime<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches <code class=\"fm-code-in-text1\">DateTime<\/code> values, expressed in the nonlocalized invariant culture format.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">decimal<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches <code class=\"fm-code-in-text1\">decimal<\/code> values, formatted in the nonlocalized invariant culture.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">double<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches <code class=\"fm-code-in-text1\">double<\/code> values, formatted in the nonlocalized invariant culture.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">file<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches segments whose content represents a file name, in the form <code class=\"fm-code-in-text1\">name<\/code><code class=\"fm-code-in-text1\">.ext<\/code>. The existence of the file is not validated.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">float<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches <code class=\"fm-code-in-text1\">float<\/code> values, formatted in the nonlocalized invariant culture.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">guid<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches <code class=\"fm-code-in-text1\">GUID<\/code> values.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">int<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches <code class=\"fm-code-in-text1\">int<\/code> values.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">length(len)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches path segments that have the specified number of characters.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">length(min, max)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches path segments whose length falls between the lower and upper values specified.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">long<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches <code class=\"fm-code-in-text1\">long<\/code> values.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">max(val)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches path segments that can be parsed to an <code class=\"fm-code-in-text1\">int<\/code> value that is less than or equal to the specified value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">maxlength(len)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches path segments whose length is equal to or less than the specified value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">min(val)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches path segments that can be parsed to an <code class=\"fm-code-in-text1\">int<\/code> value that is more than or equal to the specified value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">minlength(len)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches path segments whose length is equal to or more than the specified value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">nonfile<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches segments that do not represent a file name, i.e., values that would not be matched by the <code class=\"fm-code-in-text1\">file<\/code> constraint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">range(min, max)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint matches path segments that can be parsed to an <code class=\"fm-code-in-text1\">int<\/code> value that falls between the inclusive range specified.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">regex(expression)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This constraint applies a regular expression to match path segments.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\"><a id=\"calibre_link-1745\"><\/a>To test the constraints, restart ASP.NET Core and request http:\/\/localhost:5000\/100\/true, which is a URL whose path segments conform to the constraints in listing 13.20 and that produces the result shown on the left side of figure 13.16. Request http:\/\/localhost:5000\/apples\/oranges, which has the right number of segments but contains values that don\u2019t conform to the constraints. None of the routes matches the request, which is forwarded to the terminal middleware, as shown on the right of figure 13.16.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre116\" src=\"\/images\/proaspnetcore7\/000112.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.16 Testing constraints<\/p>\n<\/div>\n<p class=\"body\">Constraints can be combined to further restrict matching, as shown in listing 13.21.<a id=\"calibre_link-1746\"><\/a><a id=\"calibre_link-1136\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.21 Combining URL pattern constraints in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapGet(\"{first:alpha:length(3)}\/{second:bool}\", async context =&gt; {<\/b>\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\napp.MapGet(\"capital\/{country=France}\", Capital.Endpoint);\napp.MapGet(\"size\/{city?}\", Population.Endpoint)\n    .WithMetadata(new RouteNameMetadata(\"population\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">The constraints are combined, and only path segments that can satisfy all the constraints will be matched. The combination in listing 13.21 constrains the URL pattern so that the first segment will match only three alphabetic characters. To test the pattern, restart ASP.NET Core and request http:\/\/localhost:5000\/dog\/true, which will produce the output shown in figure 13.17. Requesting the URL http:\/\/localhost:5000\/dogs\/true won\u2019t match the route because the first segment contains four characters.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre117\" src=\"\/images\/proaspnetcore7\/000113.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.17 Combining constraints<\/p>\n<\/div>\n<p class=\"fm-head2\">Constraining matching to a specific set of values<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">regex<\/code> constraint applies a regular expression, which provides the basis for one of the most commonly required restrictions: matching only a specific set of values. In listing 13.22, I have applied the regex constraint to the routes for the <code class=\"fm-code-in-text\">Capital<\/code> endpoint, so it will receive requests only for the values it can process.<a id=\"calibre_link-1747\"><\/a><a id=\"calibre_link-1140\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.22 Matching specific values in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.MapGet(\"{first:alpha:length(3)}\/{second:bool}\", async context =&gt; {\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\n<b class=\"fm-bold\">app.MapGet(\"capital\/{country:regex(^uk|france|monaco$)}\",<\/b> \n    <b class=\"fm-bold\">Capital.Endpoint);<\/b>\napp.MapGet(\"size\/{city?}\", Population.Endpoint)\n    .WithMetadata(new RouteNameMetadata(\"population\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">The route will match only those URLs with two segments. The first segment must be <code class=\"fm-code-in-text\">capital<\/code>, and the second segment must be <code class=\"fm-code-in-text\">uk<\/code>, <code class=\"fm-code-in-text\">france<\/code>, or <code class=\"fm-code-in-text\">monaco<\/code>. Regular expressions are case-insensitive, which you can confirm by restarting ASP.NET Core and requesting http:\/\/localhost:5000\/capital\/UK, which will produce the result shown in figure 13.18.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You may find that your browser requests <code class=\"fm-code-in-text1\">\/capital\/uk<\/code>, with a lowercase <code class=\"fm-code-in-text1\">uk<\/code>. If this happens, clear your browser history, and try again.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre118\" src=\"\/images\/proaspnetcore7\/000114.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.18 Matching specific values with a regular expression<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-253\">13.2.6 Defining fallback routes<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1126\"><\/a>Fallback routes direct a request to an endpoint only when no other route matches a request. Fallback routes prevent requests from being passed further along the request pipeline by ensuring that the routing system will always generate a response, as shown in listing 13.23.<a id=\"calibre_link-1748\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.23 Using a fallback route in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.MapGet(\"{first:alpha:length(3)}\/{second:bool}\", async context =&gt; {\n    await context.Response.WriteAsync(\"Request Was Routed\\n\");\n    foreach (var kvp in context.Request.RouteValues) {\n        await context.Response\n            .WriteAsync($\"{kvp.Key}: {kvp.Value}\\n\");\n    }\n});\n\napp.MapGet(\"capital\/{country:regex(^uk|france|monaco$)}\", \n    Capital.Endpoint);\napp.MapGet(\"size\/{city?}\", Population.Endpoint)\n    .WithMetadata(new RouteNameMetadata(\"population\"));\n        \n<b class=\"fm-bold\">app.MapFallback(async context =&gt; {<\/b>\n    <b class=\"fm-bold\">await context.Response.WriteAsync(\"Routed to fallback endpoint\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">MapFallback<\/code> method creates a route that will be used as a last resort and that will match any request. Table 13.9 describes the methods for creating fallback routes. (There are also methods for creating fallback routes that are specific to other parts of ASP.NET Core and that are described in part 3.)<\/p>\n<p class=\"fm-table-caption\">Table 13.9 The methods for creating fallback routes<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1749\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MapFallback(endpoint)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a fallback that routes requests to an endpoint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MapFallbackToFile(path)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a fallback that routes requests to a file.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">With the addition of the route in listing 13.23, the routing middleware will handle all requests, including those that match none of the regular routes. Restart ASP.NET Core and navigate to a URL that won\u2019t be matched by any of the routes, such as http:\/\/localhost:5000\/notmatched, and you will see the response shown in figure 13.19.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre118\" src=\"\/images\/proaspnetcore7\/000115.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.19 Using a fallback route<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-254\">13.3 Advanced routing features<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1137\"><\/a>The routing features described in the previous sections address the needs of most projects, especially since they are usually accessed through higher-level features such as the MVC Framework, described in part 3. There are some advanced features for projects that have unusual routing requirements, which I describe in the following sections.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-255\">13.3.1 Creating custom constraints<\/h3>\n<p class=\"body\">If the constraints described in table 13.8 are not sufficient, you can define your own custom constraints by implementing the <code class=\"fm-code-in-text\">IRouteConstraint<\/code> interface. To create a custom constraint, add a file named <code class=\"fm-code-in-text\">CountryRouteConstraint.cs<\/code> to the <code class=\"fm-code-in-text\">Platform<\/code> folder and add the code shown in listing 13.24.<a id=\"calibre_link-1750\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.24 The contents of the CountryRouteConstraint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n\n    public class CountryRouteConstraint : IRouteConstraint {\n        private static string[] countries = { \"uk\", \"france\", \"monaco\" };\n                \n        public bool Match(HttpContext? httpContext, IRouter? route, \n                string routeKey, RouteValueDictionary values, \n                RouteDirection routeDirection) {\n            string segmentValue = values[routeKey] as string ?? \"\";\n            return Array.IndexOf(countries, segmentValue.ToLower()) &gt; -1;\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">IRouteConstraint<\/code> interface defines the <code class=\"fm-code-in-text\">Match<\/code> method, which is called to allow a constraint to decide whether a request should be matched by the route. The parameters for the <code class=\"fm-code-in-text\">Match<\/code> method provide the <code class=\"fm-code-in-text\">HttpContext<\/code> object for the request, the route, the name of the segment, the segment variables extracted from the URL, and whether the request is to check for an incoming or outgoing URL. The <code class=\"fm-code-in-text\">Match<\/code> method returns <code class=\"fm-code-in-text\">true<\/code> if the constraint is satisfied by the request and <code class=\"fm-code-in-text\">false<\/code> if it is not. The constraint in listing 13.24 defines a set of countries that are compared to the value of the segment variable to which the constraint has been applied. The constraint is satisfied if the segment matches one of the countries. Custom constraints are set up using the options pattern, as shown in listing 13.25. (The options pattern is described in chapter 12.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.25 Using a custom constraint in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.Configure&lt;RouteOptions&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.ConstraintMap.Add(\"countryName\",<\/b>\n        <b class=\"fm-bold\">typeof(CountryRouteConstraint));<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapGet(\"capital\/{country:countryName}\", Capital.Endpoint);<\/b>\n\napp.MapGet(\"capital\/{country:regex(^uk|france|monaco$)}\", \n    Capital.Endpoint);\napp.MapGet(\"size\/{city?}\", Population.Endpoint)\n    .WithMetadata(new RouteNameMetadata(\"population\"));\n        \napp.MapFallback(async context =&gt; {\n    await context.Response.WriteAsync(\"Routed to fallback endpoint\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1118\"><\/a>The options pattern is applied to the <code class=\"fm-code-in-text\">RouteOptions<\/code> class, which defines the <code class=\"fm-code-in-text\">ConstraintMap<\/code> property. Each constraint is registered with a key that allows it to be applied in URL patterns. In listing 13.25, the key for the <code class=\"fm-code-in-text\">CountryRouteConstraint<\/code> class is <code class=\"fm-code-in-text\">countryName<\/code>, which allows me to constrain a route like this:<\/p>\n<pre class=\"programlisting\">...\nendpoints.MapGet(\"capital\/{<b class=\"fm-bold\">country:countryName<\/b>}\", Capital.Endpoint);\n...<\/pre>\n<p class=\"body\">Requests will be matched by this route only when the first segment of the URL is <code class=\"fm-code-in-text\">capital<\/code> and the second segment is one of the countries defined in listing 13.24.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-256\">13.3.2 Avoiding ambiguous route exceptions<\/h3>\n<p class=\"body\">When trying to route a request, the routing middleware assigns each route a score. As explained earlier in the chapter, precedence is given to more specific routes, and route selection is usually a straightforward process that behaves predictably, albeit with the occasional surprise if you don\u2019t think through and test the full range of URLs the application will support.<a id=\"calibre_link-1751\"><\/a><\/p>\n<p class=\"body\">If two routes have the same score, the routing system can\u2019t choose between them and throws an exception, indicating that the routes are ambiguous. In most cases, the best approach is to modify the ambiguous routes to increase specificity by introducing literal segments or a constraint. There are some situations where that won\u2019t be possible, and some extra work is required to get the routing system to work as intended. Listing 13.26 replaces the routes from the previous example with two new routes that are ambiguous, but only for some requests.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.26 Defining ambiguous routes in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.Configure&lt;RouteOptions&gt;(opts =&gt; {\n    opts.ConstraintMap.Add(\"countryName\",\n        typeof(CountryRouteConstraint));\n});\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.Map(\"{number:int}\", async context =&gt; {<\/b>\n    <b class=\"fm-bold\">await context.Response.WriteAsync(\"Routed to the int endpoint\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n<b class=\"fm-bold\">app.Map(\"{number:double}\", async context =&gt; {<\/b>\n    <b class=\"fm-bold\">await context.Response<\/b>\n        <b class=\"fm-bold\">.WriteAsync(\"Routed to the double endpoint\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.MapFallback(async context =&gt; {\n    await context.Response.WriteAsync(\"Routed to fallback endpoint\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1119\"><\/a>These routes are ambiguous only for some values. Only one route matches URLs where the first path segment can be parsed to a double, but both routes match for where the segment can be parsed as an <code class=\"fm-code-in-text\">int<\/code> or a <code class=\"fm-code-in-text\">double<\/code>. To see the issue, restart ASP.NET Core and request http:\/\/localhost:5000\/23.5. The path segment <code class=\"fm-code-in-text\">23.5<\/code> can be parsed to a <code class=\"fm-code-in-text\">double<\/code> and produces the response shown on the left side of figure 13.20. Request http:\/\/localhost:5000\/23, and you will see the exception shown on the right of figure 13.20. The segment <code class=\"fm-code-in-text\">23<\/code> can be parsed as both an <code class=\"fm-code-in-text\">int<\/code> and a <code class=\"fm-code-in-text\">double<\/code>, which means that the routing system cannot identify a single route to handle the request.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre119\" src=\"\/images\/proaspnetcore7\/000116.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.20 An occasionally ambiguous routing configuration<\/p>\n<\/div>\n<p class=\"body\">For these situations, preference can be given to a route by defining its order relative to other matching routes, as shown in listing 13.27.<a id=\"calibre_link-1752\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.27 Breaking route ambiguity in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.Configure&lt;RouteOptions&gt;(opts =&gt; {\n    opts.ConstraintMap.Add(\"countryName\",\n        typeof(CountryRouteConstraint));\n});\n\nvar app = builder.Build();\n\napp.Map(\"{number:int}\", async context =&gt; {\n    await context.Response.WriteAsync(\"Routed to the int endpoint\");\n<b class=\"fm-bold\">}).Add(b =&gt; ((RouteEndpointBuilder)b).Order = 1);<\/b>\n\napp.Map(\"{number:double}\", async context =&gt; {\n    await context.Response\n        .WriteAsync(\"Routed to the double endpoint\");\n<b class=\"fm-bold\">}).Add(b =&gt; ((RouteEndpointBuilder)b).Order = 2);<\/b>\n\napp.MapFallback(async context =&gt; {\n    await context.Response.WriteAsync(\"Routed to fallback endpoint\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1753\"><\/a>The process is awkward and requires a call to the <code class=\"fm-code-in-text\">Add<\/code> method, casting to a <code class=\"fm-code-in-text\">RouteEndpointBuilder<\/code> and setting the value of the <code class=\"fm-code-in-text\">Order<\/code> property. Precedence is given to the route with the lowest <code class=\"fm-code-in-text\">Order<\/code> value, which means that these changes tell the routing system to use the first route for URLs that both routes can handle. Restart ASP.NET Core and request the http:\/\/localhost:5000\/23 URL again, and you will see that the first route handles the request, as shown in figure 13.21.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre120\" src=\"\/images\/proaspnetcore7\/000117.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.21 Avoiding ambiguous routes<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-257\">13.3.3 Accessing the endpoint in a middleware component<\/h3>\n<p class=\"body\">As earlier chapters demonstrated, not all middleware generates responses. Some components provide features used later in the request pipeline, such as the session middleware, or enhance the response in some way, such as status code middleware.<\/p>\n<p class=\"body\">One limitation of the normal request pipeline is that a middleware component at the start of the pipeline can\u2019t tell which of the later components will generate a response. The routing middleware does something different.<\/p>\n<p class=\"body\">As I demonstrated at the start of the chapter, routing is set up by calling the <code class=\"fm-code-in-text\">UseRouting<\/code> and <code class=\"fm-code-in-text\">UseEndpoints<\/code> methods, either explicitly or relying on the ASP.NET Core platform to call them during startup.<\/p>\n<p class=\"body\">Although routes are registered in the <code class=\"fm-code-in-text\">UseEndpoints<\/code> method, the selection of a route is done in the <code class=\"fm-code-in-text\">UseRouting<\/code> method, and the endpoint is executed to generate a response in the <code class=\"fm-code-in-text\">UseEndpoints<\/code> method. Any middleware component that is added to the request pipeline between the <code class=\"fm-code-in-text\">UseRouting<\/code> method and the <code class=\"fm-code-in-text\">UseEndpoints<\/code> method can see which endpoint has been selected before the response is generated and alter its behavior accordingly.<\/p>\n<p class=\"body\">In listing 13.28, I have added a middleware component that adds different messages to the response based on the route that has been selected to handle the request.<a id=\"calibre_link-1754\"><\/a><a id=\"calibre_link-1124\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 13.28 Adding a middleware component in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.Configure&lt;RouteOptions&gt;(opts =&gt; {\n    opts.ConstraintMap.Add(\"countryName\",\n        typeof(CountryRouteConstraint));\n});\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.Use(async (context, next) =&gt; {<\/b>\n    <b class=\"fm-bold\">Endpoint? end = context.GetEndpoint();<\/b>\n    <b class=\"fm-bold\">if (end != null) {<\/b>\n        <b class=\"fm-bold\">await context.Response<\/b>\n            <b class=\"fm-bold\">.WriteAsync($\"{end.DisplayName} Selected \\n\");<\/b>\n    <b class=\"fm-bold\">} else {<\/b>\n        <b class=\"fm-bold\">await context.Response.WriteAsync(\"No Endpoint Selected \\n\");<\/b>\n    <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">await next();<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.Map(\"{number:int}\", async context =&gt; {\n    await context.Response.WriteAsync(\"Routed to the int endpoint\");\n<b class=\"fm-bold\">}).WithDisplayName(\"Int Endpoint\")<\/b>\n    <b class=\"fm-bold\">.Add(b =&gt; ((RouteEndpointBuilder)b).Order = 1);<\/b>\n        \napp.Map(\"{number:double}\", async context =&gt; {\n    await context.Response\n        .WriteAsync(\"Routed to the double endpoint\");\n<b class=\"fm-bold\">}).WithDisplayName(\"Double Endpoint\")<\/b>\n    <b class=\"fm-bold\">.Add(b =&gt; ((RouteEndpointBuilder)b).Order = 2);<\/b>\n        \napp.MapFallback(async context =&gt; {\n    await context.Response.WriteAsync(\"Routed to fallback endpoint\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">GetEndpoint<\/code> extension method on the <code class=\"fm-code-in-text\">HttpContext<\/code> class returns the endpoint that has been selected to handle the request, described through an <code class=\"fm-code-in-text\">Endpoint<\/code> object. The <code class=\"fm-code-in-text\">Endpoint<\/code> class defines the properties described in table 13.10.<a id=\"calibre_link-1755\"><\/a><a id=\"calibre_link-1125\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> There is also a <code class=\"fm-code-in-text1\">SetEndpoint<\/code> method that allows the endpoint chosen by the routing middleware to be changed before the response is generated. This should be used with caution and only when there is a compelling need to interfere with the normal route selection process.<\/p>\n<p class=\"fm-table-caption\">Table 13.10 The properties defined by the Endpoint class<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1756\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">DisplayName<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the display name associated with the endpoint, which can be set using the <code class=\"fm-code-in-text1\">WithDisplayName<\/code> method when creating a route.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Metadata<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the collection of metadata associated with the endpoint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RequestDelegate<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the delegate that will be used to generate the response.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">To make it easier to identify the endpoint that the routing middleware has selected, I used the <code class=\"fm-code-in-text\">WithDisplayName<\/code> method to assign names to the routes in listing 13.28. The new middleware component adds a message to the response reporting the endpoint that has been selected. Restart ASP.NET Core and request the http:\/\/localhost:5000\/23 URL to see the output from the middleware that shows the endpoint has been selected between the two methods that add the routing middleware to the request pipeline, as shown in figure 13.22.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre121\" src=\"\/images\/proaspnetcore7\/000118.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 13.22 Determining the endpoint<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-258\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Routes allow an endpoint to match request with a URL pattern.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">URL patterns can match variable segments whose values can be read by the endpoint.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">URL patterns can contain optional segments that match URLs when they are present.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Matching URLs can be controlled using constraints.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Routes can be used to generate URLs that can be included in responses, ensuring that subsequent requests target a given endpoint.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-259\">\n<div class=\"calibre1\" id=\"calibre_link-1757\">\n<h1 class=\"tochead\" id=\"calibre_link-1758\"><a id=\"calibre_link-1759\"><\/a><a id=\"calibre_link-1760\"><\/a>14 Using dependency injection<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Understanding how dependency injection allows components to access shared services<\/li>\n<li class=\"co-summary-bullet\">Configuring services lifecycles to control when services are instantiated<\/li>\n<li class=\"co-summary-bullet\">Understanding how to define and access services using dependency injection<\/li>\n<\/ul>\n<p class=\"body\">Services are objects that are shared between middleware components and endpoints. There are no restrictions on the features that services can provide, but they are usually used for tasks that are needed in multiple parts of the application, such as logging or database access.<\/p>\n<p class=\"body\">The ASP.NET Core <i class=\"fm-italics\">dependency injection<\/i> feature is used to create and consume services. This topic causes confusion and can be difficult to understand. In this chapter, I describe the problems that dependency injection solves and explain how dependency injection is supported by the ASP.NET Core platform. Table 14.1 puts dependency injection in context.<\/p>\n<p class=\"fm-table-caption\">Table 14.1 Putting dependency injection in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1761\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What is it?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Dependency injection makes it easy to create loosely coupled components, which typically means that components consume functionality defined by interfaces without having any firsthand knowledge of which implementation classes are being used.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why is it useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Dependency injection makes it easier to change the behavior of an application by changing the components that implement the interfaces that define application features. It also results in components that are easier to isolate for unit testing.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How is it used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">Program.cs<\/code> file is used to specify which implementation classes are used to deliver the functionality specified by the interfaces used by the application. Services can be explicitly requested through the <code class=\"fm-code-in-text1\">IServiceProvider<\/code> interface or by declaring constructor or method parameters.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">There are some differences in the way that middleware components and endpoints are handled and the way that services with different lifecycles are accessed.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">You don\u2019t have to use dependency injection in your code, but it is helpful to know how it works because it is used by the ASP.NET Core platform to provide features to developers.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 14.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 14.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1762\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Obtaining a service in a handler function defined in the <code class=\"fm-code-in-text1\">Program.cs<\/code> file<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Add a parameter to the handler function.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">15<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Obtaining a service in a middleware component<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define a constructor parameter.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">16, 30&ndash;32<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Obtaining a service in an endpoint<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Get an <code class=\"fm-code-in-text1\">IServiceProvider<\/code> object through the context objects.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">17<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Instantiating a class that has constructor dependencies<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">ActivatorUtilities<\/code> class.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">18&ndash;20<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Defining services that are instantiated for every dependency<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define transient services.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">21&ndash;25<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Defining services that are instantiated for every request<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define scoped services.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">26&ndash;29<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Accessing configuration services before the <code class=\"fm-code-in-text1\">Build<\/code> method is called in the <code class=\"fm-code-in-text1\">Program.cs<\/code> file<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the properties defined by the <code class=\"fm-code-in-text1\">WebApplicationBuilder<\/code> class.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">33<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Managing service instantiation<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a service factory.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">34, 35<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Defining multiple implementations for a service<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define multiple services with the same scope and consume them through the <code class=\"fm-code-in-text1\">GetServices<\/code> method.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">36&ndash;38<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using services that support generic type parameters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a service with an unbound type.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">39<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-260\">14.1 Preparing for this chapter<\/h2>\n<p class=\"body\">In this chapter, I continue to use the <code class=\"fm-code-in-text\">Platform<\/code> project from chapter 13. New classes are required to prepare for this chapter. Start by creating the <code class=\"fm-code-in-text\">Platform\/Services<\/code> folder and add to it a class file named <code class=\"fm-code-in-text\">IResponseFormatter.cs<\/code>, with the code shown in listing 14.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.1 The contents of the IResponseFormatter.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n    public interface IResponseFormatter {\n        \n        Task Format(HttpContext context, string content);\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface defines a single method that receives an <code class=\"fm-code-in-text\">HttpContext<\/code> object and a <code class=\"fm-code-in-text\">string<\/code>. To create an implementation of the interface, add a class called <code class=\"fm-code-in-text\">TextResponseFormatter.cs<\/code> to the <code class=\"fm-code-in-text\">Platform\/Services<\/code> folder with the code shown in listing 14.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.2 The contents of the TextResponseFormatter.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n    public class TextResponseFormatter : IResponseFormatter {\n        private int responseCounter = 0;\n                \n        public async Task Format(HttpContext context, string content) {\n            await context.Response.\n                WriteAsync($\"Response {++responseCounter}:\\n{content}\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">TextResponseFormatter<\/code> class implements the interface and writes the content to the response as a simple string with a prefix to make it obvious when the class is used.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-261\">14.1.1 Creating a middleware component and an endpoint<\/h3>\n<p class=\"body\">Some of the examples in this chapter show how features are applied differently when using middleware and endpoints. Add a file called <code class=\"fm-code-in-text\">WeatherMiddleware.cs<\/code> to the <code class=\"fm-code-in-text\">Platform<\/code> folder with the code shown in listing 14.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.3 The contents of the WeatherMiddleware.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n    public class WeatherMiddleware {\n        \n        private RequestDelegate next;\n                \n        public WeatherMiddleware(RequestDelegate nextDelegate) {\n            next = nextDelegate;\n        }\n                \n        public async Task Invoke(HttpContext context) {\n            if (context.Request.Path == \"\/middleware\/class\") {\n                await context.Response\n                 .WriteAsync(\"Middleware Class: It is raining in London\");\n            } else {\n                await next(context);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">To create an endpoint that produces a similar result to the middleware component, add a file called <code class=\"fm-code-in-text\">WeatherEndpoint.cs<\/code> to the <code class=\"fm-code-in-text\">Platform<\/code> folder with the code shown in listing 14.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.4 The contents of the WeatherEndpoint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n    public class WeatherEndpoint {\n        \n        public static async Task Endpoint(HttpContext context) {\n            await context.Response\n                .WriteAsync(\"Endpoint Class: It is cloudy in Milan\");\n        }\n    }\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-262\">14.1.2 Configuring the request pipeline<\/h3>\n<p class=\"body\">Replace the contents of the <code class=\"fm-code-in-text\">Program.cs<\/code> file with those shown in listing 14.5. The classes defined in the previous section are applied alongside a lambda function that produce similar results.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.5 Replacing the contents of the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.UseMiddleware&lt;WeatherMiddleware&gt;();\n\napp.MapGet(\"endpoint\/class\", WeatherEndpoint.Endpoint);\n\nIResponseFormatter formatter = new TextResponseFormatter();\napp.MapGet(\"endpoint\/function\", async context =&gt; {\n    await formatter.Format(context, \n        \"Endpoint Function: It is sunny in LA\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">Start the application by opening a new PowerShell command prompt, navigating to the <code class=\"fm-code-in-text\">Platform<\/code> project folder, and running the command shown in listing 14.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.6 Starting the ASP.NET Core Runtime<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/endpoint\/function, and you will see the response shown in figure 14.1. Each time you reload the browser, the counter shown in the response will be incremented.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre122\" src=\"\/images\/proaspnetcore7\/000119.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-263\">14.2 Understanding service location and tight coupling<\/h2>\n<p class=\"body\">To understand dependency injection, it is important to start with the two problems it solves. In the sections that follow, I describe both problems addressed by dependency injection.<a id=\"calibre_link-1763\"><\/a><a id=\"calibre_link-1764\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Taking a view on dependency injection<\/p>\n<p class=\"fm-sidebar-text\">Dependency injection is one of the topics that readers contact me about most often. About half of the emails complain that I am \u201cforcing\u201d DI upon them. Oddly, the other half are complaints that I did not emphasize the benefits of DI strongly enough and other readers may not have realized how useful it can be.<a id=\"calibre_link-1765\"><\/a><a id=\"calibre_link-923\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">Dependency injection can be a difficult topic to understand, and its value is contentious. DI can be a useful tool, but not everyone likes it&mdash;or needs it.<\/p>\n<p class=\"fm-sidebar-text\">DI offers limited benefit if you are not doing unit testing or if you are working on a small, self-contained, and stable project. It is still helpful to understand how DI works because DI is used to access some important ASP.NET Core features described in earlier chapters, but you don\u2019t always need to embrace DI in the custom classes you write. There are alternative ways of creating shared features&mdash;two of which I describe in the following sections&mdash;and using these is perfectly acceptable if you don\u2019t like DI.<\/p>\n<p class=\"fm-sidebar-text\">I rely on DI in my applications because I find that projects often go in unexpected directions, and being able to easily replace a component with a new implementation can save me a lot of tedious and error-prone changes. I\u2019d rather put in some effort at the start of the project than do a complex set of edits later.<\/p>\n<p class=\"fm-sidebar-text\">But I am not dogmatic about dependency injection and nor should you be. Dependency injection solves a problem that doesn\u2019t arise in every project, and only you can determine whether you need DI for your project.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-264\">14.2.1 Understanding the service location problem<\/h3>\n<p class=\"body\"><a id=\"calibre_link-924\"><\/a>Most projects have features that need to be used in different parts of the application, which are known as <i class=\"fm-italics\">services<\/i>. Common examples include logging tools and configuration settings but can extend to any shared feature, including the <code class=\"fm-code-in-text\">TextResponseFormatter<\/code> class that is defined in listing 14.2 and to handle requests by the middleware component and the lambda function.<\/p>\n<p class=\"body\">Each <code class=\"fm-code-in-text\">TextResponseFormatter<\/code> object maintains a counter that is included in the response sent to the browser, and if I want to incorporate the same counter into the responses generated by other endpoints, I need to have a way to make a single <code class=\"fm-code-in-text\">TextResponseFormatter<\/code> object available in such a way that it can be easily found and consumed at every point where responses are generated.<a id=\"calibre_link-1766\"><\/a><\/p>\n<p class=\"body\">There are many ways to make services locatable, but there are two main approaches, aside from the one that is the main topic of this chapter. The first approach is to create an object and use it as a constructor or method argument to pass it to the part of the application where it is required. The other approach is to add a <code class=\"fm-code-in-text\">static<\/code> property to the service class that provides direct access to the shared instance, as shown in listing 14.7. This is known as the <i class=\"fm-italics\">singleton pattern<\/i>, and it was a common approach before the widespread use of dependency injection.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.7 A singleton in the TextResponseFormatter.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n    public class TextResponseFormatter : IResponseFormatter {\n        private int responseCounter = 0;\n        <b class=\"fm-bold\">private static TextResponseFormatter? shared;<\/b>\n                \n        public async Task Format(HttpContext context, string content) {\n            await context.Response.\n                WriteAsync($\"Response {++responseCounter}:\\n{content}\");\n        }\n                \n        <b class=\"fm-bold\">public static TextResponseFormatter Singleton {<\/b>\n            <b class=\"fm-bold\">get {<\/b>\n                <b class=\"fm-bold\">if (shared == null) {<\/b>\n                    <b class=\"fm-bold\">shared = new TextResponseFormatter();<\/b>\n                <b class=\"fm-bold\">}<\/b>\n                <b class=\"fm-bold\">return shared;<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">This is a basic implementation of the singleton pattern, and there are many variations that pay closer attention to issues such as safe concurrent access. What\u2019s important for this chapter is that the changes in listing 14.7 rely on the consumers of the <code class=\"fm-code-in-text\">TextResponseFormatter<\/code> service obtaining a shared object through the static <code class=\"fm-code-in-text\">Singleton<\/code> property, as shown in listing 14.8.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.8 Using a singleton in the WeatherEndpoint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Platform.Services;<\/b>\n\nnamespace Platform {\n    public class WeatherEndpoint {\n        \n        public static async Task Endpoint(HttpContext context) {\n            <b class=\"fm-bold\">await TextResponseFormatter.Singleton.Format(context,<\/b> \n                <b class=\"fm-bold\">\"Endpoint Class: It is cloudy in Milan\");<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">Listing 14.9 makes the same change to the lambda function in the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.9 Using a service in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.UseMiddleware&lt;WeatherMiddleware&gt;();\n\napp.MapGet(\"endpoint\/class\", WeatherEndpoint.Endpoint);\n\n<b class=\"fm-bold\">IResponseFormatter formatter = TextResponseFormatter.Singleton;<\/b>\napp.MapGet(\"endpoint\/function\", async context =&gt; {\n    await formatter.Format(context, \n        \"Endpoint Function: It is sunny in LA\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The singleton pattern allows me to share a single <code class=\"fm-code-in-text\">TextResponseFormatter<\/code> object so it is used by two endpoints, with the effect that a single counter is incremented by requests for two different URLs<a id=\"calibre_link-1767\"><\/a><a id=\"calibre_link-1768\"><\/a><a id=\"calibre_link-927\"><\/a>.<\/p>\n<p class=\"body\">To see the effect of the singleton pattern, restart ASP.NET Core and request the http:\/\/localhost:5000\/endpoint\/class and http:\/\/localhost:5000\/endpoint\/function URLs. A single counter is updated for both URLs, as shown in figure 14.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre123\" src=\"\/images\/proaspnetcore7\/000120.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.2 Implementing the singleton pattern to create a shared service<\/p>\n<\/div>\n<p class=\"body\">The singleton pattern is simple to understand and easy to use, but the knowledge of how services are located is spread throughout the application, and all service classes and service consumers need to understand how to access the shared object. This can lead to variations in the singleton pattern as new services are created and creates many points in the code that must be updated when there is a change. This pattern can also be rigid and doesn\u2019t allow any flexibility in how services are managed because every consumer always shares a single service object.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-265\">14.2.2 Understanding the tightly coupled components problem<\/h3>\n<p class=\"body\">Although I defined an interface in listing 14.1, the way that I have used the singleton pattern means that consumers are always aware of the implementation class they are using because that\u2019s the class whose static property is used to get the shared object. If I want to switch to a different implementation of the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface, I must locate every use of the service and replace the existing implementation class with the new one. There are patterns to solve this problem, too, such as the <i class=\"fm-italics\">type broker<\/i> pattern, in which a class provides access to singleton objects through their interfaces. Add a class file called <code class=\"fm-code-in-text\">TypeBroker.cs<\/code> to the <code class=\"fm-code-in-text\">Platform\/Services<\/code> folder and use it to define the code shown in listing 14.10.<a id=\"calibre_link-1769\"><\/a><a id=\"calibre_link-1770\"><\/a><a id=\"calibre_link-928\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.10 The contents of the TypeBroker.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n    public static class TypeBroker {\n        private static IResponseFormatter formatter \n            = new TextResponseFormatter();\n                        \n        public static IResponseFormatter Formatter =&gt; formatter;\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Formatter<\/code> property provides access to a shared service object that implements the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface. Consumers of the service need to know that the <code class=\"fm-code-in-text\">TypeBroker<\/code> class is responsible for selecting the implementation that will be used, but this pattern means that service consumers can work through interfaces rather than concrete classes, as shown in listing 14.11.<a id=\"calibre_link-1771\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.11 Using a type broker in the WeatherEndpoint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform.Services;\n\nnamespace Platform {\n    public class WeatherEndpoint {\n        \n        public static async Task Endpoint(HttpContext context) {\n            <b class=\"fm-bold\">await TypeBroker.Formatter.Format(context,<\/b> \n                \"Endpoint Class: It is cloudy in Milan\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">Listing 14.12 makes the same change to the lambda function so that both uses of the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface get their implementation objects from the type broker.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.12 Using a type broker in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.UseMiddleware&lt;WeatherMiddleware&gt;();\n\napp.MapGet(\"endpoint\/class\", WeatherEndpoint.Endpoint);\n\n<b class=\"fm-bold\">IResponseFormatter formatter = TypeBroker.Formatter;<\/b>\napp.MapGet(\"endpoint\/function\", async context =&gt; {\n    await formatter.Format(context, \n        \"Endpoint Function: It is sunny in LA\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">This approach makes it easy to switch to a different implementation class by altering just the <code class=\"fm-code-in-text\">TypeBroker<\/code> class and prevents service consumers from creating dependencies on a specific implementation. It also means that service classes can focus on the features they provide without having to deal with how those features will be located. To demonstrate, add a class file called <code class=\"fm-code-in-text\">HtmlResponseFormatter.cs<\/code> to the <code class=\"fm-code-in-text\">Platform\/Services<\/code> folder with the code shown in listing 14.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.13 The contents of the HtmlResponseFormatter.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n    public class HtmlResponseFormatter : IResponseFormatter {\n        \n        public async Task Format(HttpContext context, string content) {\n            context.Response.ContentType = \"text\/html\";\n            await context.Response.WriteAsync($@\"\n                &lt;!DOCTYPE html&gt;\n                &lt;html lang=\"\"en\"\"&gt;\n                &lt;head&gt;&lt;title&gt;Response&lt;\/title&gt;&lt;\/head&gt;\n                &lt;body&gt;\n                    &lt;h2&gt;Formatted Response&lt;\/h2&gt;\n                    &lt;span&gt;{content}&lt;\/span&gt;\n                &lt;\/body&gt;\n                &lt;\/html&gt;\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">This implementation of the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> sets the <code class=\"fm-code-in-text\">ContentType<\/code> property of the <code class=\"fm-code-in-text\">HttpResponse<\/code> object and inserts the content into an HTML template string. To use the new formatter class, I only need to change the <code class=\"fm-code-in-text\">TypeBroker<\/code>, as shown in listing 14.14.<a id=\"calibre_link-904\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.14 changing the TypeBroker.cs file in the Platform\/Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n    public static class TypeBroker {\n        <b class=\"fm-bold\">private static IResponseFormatter formatter<\/b> \n            <b class=\"fm-bold\">= new HtmlResponseFormatter();<\/b>\n                        \n        public static IResponseFormatter Formatter =&gt; formatter;\n    }\n}<\/pre>\n<p class=\"body\">To confirm the new formatter works, restart ASP.NET Core and request http:\/\/localhost:5000\/endpoint\/function, which will produce the result shown in figure 14.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre124\" src=\"\/images\/proaspnetcore7\/000121.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.3 Using a different service implementation class<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-266\">14.3 Using dependency injection<\/h2>\n<p class=\"body\">Dependency injection provides an alternative approach to providing services that tidy up the rough edges that arise in the singleton and type broker patterns, and is integrated with other ASP.NET Core features. Listing 14.15 shows the use of ASP.NET Core dependency injection to replace the type broker from the previous section.<a id=\"calibre_link-1772\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.15 Using dependency injection in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddSingleton&lt;IResponseFormatter,<\/b> \n    <b class=\"fm-bold\">HtmlResponseFormatter&gt;();<\/b>\n        \nvar app = builder.Build();\n\napp.UseMiddleware&lt;WeatherMiddleware&gt;();\n\napp.MapGet(\"endpoint\/class\", WeatherEndpoint.Endpoint);\n\n<b class=\"fm-bold\">\/\/IResponseFormatter formatter = TypeBroker.Formatter;<\/b>\napp.MapGet(\"endpoint\/function\", \n    <b class=\"fm-bold\">async (HttpContext context, IResponseFormatter formatter) =&gt; {<\/b>\n        await formatter.Format(context, \n            \"Endpoint Function: It is sunny in LA\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\"><a id=\"calibre_link-908\"><\/a>Services are registered using extension methods defined by the <code class=\"fm-code-in-text\">IServiceCollection<\/code> interface, an implementation of which is obtained using the <code class=\"fm-code-in-text\">WebApplicationBuilder.Services<\/code> property. In the listing, I used an extension method to create a service for the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface:<a id=\"calibre_link-1773\"><\/a><\/p>\n<pre class=\"programlisting\">...\nbuilder.<b class=\"fm-bold\">Services.AddSingleton<\/b>&lt;IResponseFormatter, HtmlResponseFormatter&gt;();\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddSingleton<\/code> method is one of the extension methods available for services and tells ASP.NET Core that a single object should be used to satisfy all demands for the service (the other extension methods are described in the \u201cUsing Service Lifecycles\u201d section). The interface and the implementation class are specified as generic type arguments. To consume the service, I added parameters to the functions that handle requests, like this:<\/p>\n<pre class=\"programlisting\">...\nasync (HttpContext context, <b class=\"fm-bold\">IResponseFormatter formatter<\/b>) =&gt; {\n...<\/pre>\n<p class=\"body\">Many of the methods that are used to register middleware or create endpoints will accept any function, which allows parameters to be defined for the services that are required to produce a response. One consequence of this feature is that the C# compiler can\u2019t determine the parameter types, which is why I had to specify them in the listing.<\/p>\n<p class=\"body\">The new parameter declares a dependency on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface, and the function is said to depend on the interface. Before the function is invoked to handle a request, its parameters are inspected, the dependency is detected, and the application\u2019s services are inspected to determine whether it is possible to resolve the dependency.<\/p>\n<p class=\"body\">The call to the <code class=\"fm-code-in-text\">AddSingleton<\/code> method told the dependency injection system that a dependency on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface can be resolved with an <code class=\"fm-code-in-text\">HtmlResponseFormatter<\/code> object. The object is created and used as an argument to invoke the handler function. Because the object that resolves the dependency is provided from outside the function that uses it, it is said to have been <i class=\"fm-italics\">injected<\/i>, which is why the process is known as <i class=\"fm-italics\">dependency injection<\/i>.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-267\">14.3.1 Using a Service with a Constructor Dependency<\/h3>\n<p class=\"body\">Defining a service and consuming it in the same code file may not seem impressive, but once a service is defined, it can be used almost anywhere in an ASP.NET Core application. Listing 14.16 declares a dependency on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface in the middleware class defined at the start of the chapter.<a id=\"calibre_link-1774\"><\/a><a id=\"calibre_link-918\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.16 A dependency in the WeatherMiddleware.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Platform.Services;<\/b>\n\nnamespace Platform {\n    public class WeatherMiddleware {\n        private RequestDelegate next;\n        <b class=\"fm-bold\">private IResponseFormatter formatter;<\/b>\n                \n        <b class=\"fm-bold\">public WeatherMiddleware(RequestDelegate nextDelegate,<\/b>\n                <b class=\"fm-bold\">IResponseFormatter respFormatter) {<\/b>\n            next = nextDelegate;\n            <b class=\"fm-bold\">formatter = respFormatter;<\/b>\n        }\n                \n        public async Task Invoke(HttpContext context) {\n            if (context.Request.Path == \"\/middleware\/class\") {\n                <b class=\"fm-bold\">await formatter.Format(context,<\/b>\n                    <b class=\"fm-bold\">\"Middleware Class: It is raining in London\");<\/b>\n            } else {\n                await next(context);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">To declare the dependency, I added a constructor parameter. To see the result, restart ASP.NET Core and request the http:\/\/localhost:5000\/middleware\/class URL, which will produce the response shown in figure 14.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre124\" src=\"\/images\/proaspnetcore7\/000122.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.4 Declaring a dependency in a middleware class<\/p>\n<\/div>\n<p class=\"body\"><a id=\"calibre_link-919\"><\/a>When the request pipeline is being set up, the ASP.NET Core platform reaches the statement in the <code class=\"fm-code-in-text\">Program.cs<\/code> file that adds the <code class=\"fm-code-in-text\">WeatherMiddleware<\/code> class as a component.<\/p>\n<pre class=\"programlisting\">...\napp.UseMiddleware&lt;<b class=\"fm-bold\">WeatherMiddleware<\/b>&gt;();\n...<\/pre>\n<p class=\"body\">The platform understands it needs to create an instance of the <code class=\"fm-code-in-text\">WeatherMiddleware<\/code> class and inspects the constructor. The dependency on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface is detected, the services are inspected to see if the dependency can be resolved, and the shared service object is used when the constructor is invoked.<\/p>\n<p class=\"body\">There are two important points to understand about this example. The first is that <code class=\"fm-code-in-text\">WeatherMiddleware<\/code> doesn\u2019t know which implementation class will be used to resolve its dependency on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface&mdash;it just knows that it will receive an object that conforms to the interface through its constructor parameter. Second, the <code class=\"fm-code-in-text\">WeatherMiddleware<\/code> class doesn\u2019t know how the dependency is resolved&mdash;it just declares a constructor parameter and relies on ASP.NET Core to figure out the details. This is a more elegant approach than my implementations of the singleton and type broker patterns earlier in the chapter, and I can change the implementation class used to resolve the service by changing the generic type parameters used in the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-268\">14.3.2 Getting services from the HttpContext object<\/h3>\n<p class=\"body\">ASP.NET Core does a good job of supporting dependency injection as widely as possible but there will be times when you are not working directly with the ASP.NET Core API and won\u2019t have a way to declare your service dependencies directly.<\/p>\n<p class=\"body\">Services can be accessed through the <code class=\"fm-code-in-text\">HttpContext<\/code> object, which is used to represent the current request and response, as shown in listing 14.17. You may find that you receive an <code class=\"fm-code-in-text\">HttpContext<\/code> object even if you are working with third-party code that acts as an intermediary to ASP.NET Core and which doesn\u2019t allow you to resolve services<a id=\"calibre_link-1775\"><\/a>.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> This example is a demonstration of a common problem but ASP.NET Core does support dependency injection for endpoint delegates.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.17 Using the HttpContext in the WeatherEndpoint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Platform.Services;<\/b>\n\nnamespace Platform {\n    public class WeatherEndpoint {\n        \n        public static async Task Endpoint(HttpContext context) {\n            <b class=\"fm-bold\">IResponseFormatter formatter = context.RequestServices<\/b>\n                <b class=\"fm-bold\">.GetRequiredService&lt;IResponseFormatter&gt;();<\/b>\n            await formatter.Format(context,\n                \"Endpoint Class: It is cloudy in Milan\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HttpContext.RequestServices<\/code> property returns an object that implements the <code class=\"fm-code-in-text\">IServiceProvider<\/code> interfaces, which provides access to the services that have been configured in the <code class=\"fm-code-in-text\">Program.cs<\/code> file. The <code class=\"fm-code-in-text\">Microsoft.Extensions.DependencyInjection<\/code> namespace contains extension methods for the <code class=\"fm-code-in-text\">IServiceProvider<\/code> interface that allow individual services to be obtained, as described in table 14.3.<a id=\"calibre_link-1776\"><\/a><a id=\"calibre_link-1777\"><\/a><a id=\"calibre_link-1778\"><\/a><a id=\"calibre_link-916\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 14.3 The IServiceProvider extension methods for obtaining services<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1779\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetService&lt;T&gt;()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns a service for the type specified by the generic type parameter or <code class=\"fm-code-in-text1\">null<\/code> if no such service has been defined.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetService(type)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns a service for the type specified or <code class=\"fm-code-in-text1\">null<\/code> if no such service has been defined.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetRequiredService&lt;T&gt;()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns a service specified by the generic type parameter and throws an exception if a service isn\u2019t available.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetRequiredService(type)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns a service for the type specified and throws an exception if a service isn\u2019t available.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">When the <code class=\"fm-code-in-text\">Endpoint<\/code> method is invoked in listing 14.17, the <code class=\"fm-code-in-text\">GetRequiredService&lt;T&gt;<\/code> method is used to obtain an <code class=\"fm-code-in-text\">IResponseFormatter<\/code> object, which is used to format the response. To see the effect, restart ASP.NET Core and use the browser to request http:\/\/localhost:5000\/endpoint\/class, which will produce the formatted response shown in figure 14.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre125\" src=\"\/images\/proaspnetcore7\/000123.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.5 Using a service in an endpoint class<\/p>\n<\/div>\n<p class=\"fm-head2\">Using the activation utility class<\/p>\n<p class=\"body\">I defined static methods for endpoint classes in chapter 13 because it makes them easier to use when creating routes. But for endpoints that require services, it can often be easier to use a class that can be instantiated because it allows for a more generalized approach to handling services. Listing 14.18 revises the endpoint with a constructor and removes the <code class=\"fm-code-in-text\">static<\/code> keyword from the <code class=\"fm-code-in-text\">Endpoint<\/code> method.<a id=\"calibre_link-1780\"><\/a><a id=\"calibre_link-1781\"><\/a><a id=\"calibre_link-736\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.18 Revising the endpoint in the WeatherEndpoint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform.Services;\n\nnamespace Platform {\n    public class WeatherEndpoint {\n        <b class=\"fm-bold\">private IResponseFormatter formatter;<\/b>\n                \n        <b class=\"fm-bold\">public WeatherEndpoint(IResponseFormatter responseFormatter) {<\/b>\n            <b class=\"fm-bold\">formatter = responseFormatter;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">public async Task Endpoint(HttpContext context) {<\/b>\n            await formatter.Format(context, \n                \"Endpoint Class: It is cloudy in Milan\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The most common use of dependency injection in ASP.NET Core applications is in class constructors. Injection through methods, such as performed for middleware classes, is a complex process to re-create, but there are some useful built-in tools that take care of inspecting constructors and resolving dependencies using services. Create a file called <code class=\"fm-code-in-text\">EndpointExtensions.cs<\/code> to the <code class=\"fm-code-in-text\">Services<\/code> folder with the content shown in listing 14.19.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.19 The contents of the EndpointExtensions.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">using System.Reflection;\n\nnamespace Microsoft.AspNetCore.Builder {\n\n    public static class EndpointExtensions {\n        public static void MapEndpoint&lt;T&gt;(this IEndpointRouteBuilder app,\n                string path, string methodName = \"Endpoint\") {\n                                \n            MethodInfo? methodInfo = typeof(T).GetMethod(methodName);\n            if (methodInfo?.ReturnType != typeof(Task)) {\n                throw new System.Exception(\"Method cannot be used\");\n            }\n            T endpointInstance =\n                ActivatorUtilities.CreateInstance&lt;T&gt;(app.ServiceProvider);\n            app.MapGet(path, (RequestDelegate)methodInfo\n                .CreateDelegate(typeof(RequestDelegate), \n                    endpointInstance));\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">MapEndpoint<\/code> extension method accepts a generic type parameter that specifies the endpoint class that will be used. The other arguments are the path that will be used to create the route and the name of the endpoint class method that processes requests.<\/p>\n<p class=\"body\"><a id=\"calibre_link-905\"><\/a>A new instance of the endpoint class is created, and a delegate to the specified method is used to create a route. Like any code that uses .NET reflection, the extension method in listing 14.19 can be difficult to read, but this is the key statement for this chapter:<\/p>\n<pre class=\"programlisting\">...\nT endpointInstance = \n    ActivatorUtilities.CreateInstance&lt;T&gt;(app.ServiceProvider);\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ActivatorUtilities<\/code> class, defined in the <code class=\"fm-code-in-text\">Microsoft.Extensions.DependencyInjection<\/code> namespace, provides methods for instantiating classes that have dependencies declared through their constructor. Table 14.4 shows the most useful <code class=\"fm-code-in-text\">ActivatorUtilities<\/code> methods.<a id=\"calibre_link-1782\"><\/a><a id=\"calibre_link-1783\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 14.4 The ActivatorUtilities methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1784\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">CreateInstance&lt;T&gt;(services, args)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a new instance of the class specified by the type parameter, resolving dependencies using the services and additional (optional) arguments.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">CreateInstance(services, type, args)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a new instance of the class specified by the parameter, resolving dependencies using the services and additional (optional) arguments.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetServiceOrCreateInstance&lt;T&gt;<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">(services, args)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns a service of the specified type, if one is available, or creates a new instance if there is no service.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetServiceOrCreateInstance<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">(services, type, args)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns a service of the specified type, if one is available, or creates a new instance if there is no service.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">These methods make it easy to instantiate classes that declare constructor dependencies. Both methods resolve constructor dependencies using services through an <code class=\"fm-code-in-text\">IServiceProvider<\/code> object and an optional array of arguments that are used for dependencies that are not services. These methods make it easy to apply dependency injection to custom classes, and the use of the <code class=\"fm-code-in-text\">CreateInstance<\/code> method results in an extension method that can create routes with endpoint classes that consume services. Listing 14.20 uses the new extension method to create a route.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.20 Creating a route in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddSingleton&lt;IResponseFormatter, \n    HtmlResponseFormatter&gt;();\n        \nvar app = builder.Build();\n\napp.UseMiddleware&lt;WeatherMiddleware&gt;();\n\n<b class=\"fm-bold\">\/\/app.MapGet(\"endpoint\/class\", WeatherEndpoint.Endpoint);<\/b>\n<b class=\"fm-bold\">app.MapEndpoint&lt;WeatherEndpoint&gt;(\"endpoint\/class\");<\/b>\n\napp.MapGet(\"endpoint\/function\", \n    async (HttpContext context, IResponseFormatter formatter) =&gt; {\n        await formatter.Format(context, \n            \"Endpoint Function: It is sunny in LA\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\"><a id=\"calibre_link-922\"><\/a>To confirm that requests are routed to the endpoint, restart ASP.NET Core and request the http:\/\/localhost:5000\/endpoint\/class URL, which should produce the same response as shown in figure 14.5.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-269\">14.4 Using Service Lifecycles<\/h2>\n<p class=\"body\">When I created the service in the previous section, I used the <code class=\"fm-code-in-text\">AddSingleton<\/code> extension method, like this:<a id=\"calibre_link-1785\"><\/a><\/p>\n<pre class=\"programlisting\">...\nbuilder.Services.<b class=\"fm-bold\">AddSingleton<\/b>&lt;IResponseFormatter, HtmlResponseFormatter&gt;();\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddSingleton<\/code> method produces a service that is instantiated the first time it is used to resolve a dependency and is then reused for each subsequent dependency. This means that any dependency on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> object will be resolved using the same <code class=\"fm-code-in-text\">HtmlResponseFormatter<\/code> object.<a id=\"calibre_link-1786\"><\/a><\/p>\n<p class=\"body\">Singletons are a good way to get started with services, but there are some problems for which they are not suited, so ASP.NET Core supports <i class=\"fm-italics\">scoped<\/i> and <i class=\"fm-italics\">transient<\/i> services, which give different lifecycles for the objects that are created to resolve dependencies. Table 14.5 describes the set of methods used to create services. There are versions of these methods that accept types as conventional arguments, as demonstrated in the \u201cUsing Unbound Types in Services\u201d section, later in this chapter.<a id=\"calibre_link-1787\"><\/a><a id=\"calibre_link-1788\"><\/a><a id=\"calibre_link-1789\"><\/a><a id=\"calibre_link-1790\"><\/a><a id=\"calibre_link-1791\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 14.5 The extension methods for creating services<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1792\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddSingleton&lt;T, U&gt;()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a single object of type <code class=\"fm-code-in-text1\">U<\/code> that is used to resolve all dependencies on type <code class=\"fm-code-in-text1\">T<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddTransient&lt;T, U&gt;()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a new object of type <code class=\"fm-code-in-text1\">U<\/code> to resolve each dependency on type <code class=\"fm-code-in-text1\">T<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddScoped&lt;T, U&gt;()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a new object of type <code class=\"fm-code-in-text1\">U<\/code> that is used to resolve dependencies on <code class=\"fm-code-in-text1\">T<\/code> within a single scope, such as request.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\"><a id=\"calibre_link-906\"><\/a>There are versions of the methods in Table 14.5 that have a single type argument, which allows a service to be created that solves the service location problem without addressing the tightly coupled issue. You can see an example of this type of service in chapter 24, where I share a simple data source that isn\u2019t accessed through an interface.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-270\">14.4.1 Creating transient services<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddTransient<\/code> method does the opposite of the <code class=\"fm-code-in-text\">AddSingleton<\/code> method and creates a new instance of the implementation class for every dependency that is resolved. To create a service that will demonstrate the use of service lifecycles, add a file called <code class=\"fm-code-in-text\">GuidService.cs<\/code> to the <code class=\"fm-code-in-text\">Platform\/Services<\/code> folder with the code shown in listing 14.21.<a id=\"calibre_link-1793\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.21 The contents of the GuidService.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n\n    public class GuidService : IResponseFormatter {\n        private Guid guid = Guid.NewGuid();\n                \n        public async Task Format(HttpContext context, string content) {\n            await context.Response.WriteAsync($\"Guid: {guid}\\n{content}\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Guid<\/code> struct generates a unique identifier, which will make it obvious when a different instance is used to resolve a dependency on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface. In listing 14.22, I have changed the statement that creates the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service to use the <code class=\"fm-code-in-text\">AddTransient<\/code> method and the <code class=\"fm-code-in-text\">GuidService<\/code> implementation class.<a id=\"calibre_link-1794\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.22 Creating a transient service in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddTransient&lt;IResponseFormatter, GuidService&gt;();<\/b>\n\nvar app = builder.Build();\n\napp.UseMiddleware&lt;WeatherMiddleware&gt;();\n\napp.MapEndpoint&lt;WeatherEndpoint&gt;(\"endpoint\/class\");\n\napp.MapGet(\"endpoint\/function\", \n    async (HttpContext context, IResponseFormatter formatter) =&gt; {\n        await formatter.Format(context, \n            \"Endpoint Function: It is sunny in LA\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1795\"><\/a>If you restart ASP.NET Core and request the http:\/\/localhost:5000\/endpoint\/function URL, you will receive the responses similar to the ones shown in figure 14.6. Each response will be shown with a different GUID value, confirming that transient service objects have been used to resolve the dependency on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre126\" src=\"\/images\/proaspnetcore7\/000124.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.6 Using transient services<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-271\">14.4.2 Avoiding the transient service reuse pitfall<\/h3>\n<p class=\"body\">There is a pitfall when using transient services, which you can see by requesting http:\/\/localhost:5000\/middleware\/class and clicking the reload button. Unlike the previous example, the same GUID value is shown in every response, as shown in figure 14.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre127\" src=\"\/images\/proaspnetcore7\/000125.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.7 The same GUID values appearing in responses<\/p>\n<\/div>\n<p class=\"body\">New service objects are created only when dependencies are resolved, not when services are used. The components and endpoints in the example application have their dependencies resolved only when the application starts and the top-level statements in the <code class=\"fm-code-in-text\">Program.cs<\/code> file are executed. Each receives a separate service object, which is then reused for every request that is processed.<\/p>\n<p class=\"body\"><a id=\"calibre_link-1796\"><\/a>To address this issue, I have to ensure that the dependency is resolved every time the <code class=\"fm-code-in-text\">Invoke<\/code> method is called, as shown in listing 14.23.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.23 Moving dependencies in the Platform\/WeatherMiddleware.cs file<\/p>\n<pre class=\"programlisting\">using Platform.Services;\n\nnamespace Platform {\n    public class WeatherMiddleware {\n        private RequestDelegate next;\n        <b class=\"fm-bold\">\/\/private IResponseFormatter formatter;<\/b>\n                \n        <b class=\"fm-bold\">public WeatherMiddleware(RequestDelegate nextDelegate) {<\/b>\n            next = nextDelegate;\n            <b class=\"fm-bold\">\/\/formatter = respFormatter;<\/b>\n        }\n                \n        <b class=\"fm-bold\">public async Task Invoke(HttpContext context,<\/b> \n                <b class=\"fm-bold\">IResponseFormatter formatter) {<\/b>\n            if (context.Request.Path == \"\/middleware\/class\") {\n                await formatter.Format(context,\n                    \"Middleware Class: It is raining in London\");\n            } else {\n                await next(context);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The ASP.NET Core platform will resolve dependencies declared by the <code class=\"fm-code-in-text\">Invoke<\/code> method every time a request is processed, which ensures that a new transient service object is created.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ActivatorUtilities<\/code> class doesn\u2019t deal with resolving dependencies for methods. The simplest way of solving this issue for endpoints is to explicitly request services when each request is handled, which is the approach I used earlier when showing how services are used. It is also possible to enhance the extension method to request services on behalf of an endpoint, as shown in listing 14.24.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.24 Requesting services in the Services\/EndpointExtensions.cs file<\/p>\n<pre class=\"programlisting\">using System.Reflection;\n\nnamespace Microsoft.AspNetCore.Builder {\n\n    public static class EndpointExtensions {\n        \n        public static void MapEndpoint&lt;T&gt;(this IEndpointRouteBuilder app,\n                string path, string methodName = \"Endpoint\") {\n                                \n            MethodInfo? methodInfo = typeof(T).GetMethod(methodName);\n            if (methodInfo?.ReturnType != typeof(Task)) {\n                throw new System.Exception(\"Method cannot be used\");\n            }\n            T endpointInstance =\n                ActivatorUtilities.CreateInstance&lt;T&gt;(app.ServiceProvider);\n                                \n            <b class=\"fm-bold\">ParameterInfo[] methodParams = methodInfo!.GetParameters();<\/b>\n                        \n            <b class=\"fm-bold\">app.MapGet(path, context =&gt;<\/b> \n                <b class=\"fm-bold\">(Task)methodInfo.Invoke(endpointInstance,<\/b>\n                  <b class=\"fm-bold\">methodParams.Select(p =&gt;<\/b> \n                    <b class=\"fm-bold\">p.ParameterType == typeof(HttpContext) ? context<\/b>\n                      <b class=\"fm-bold\">: app.ServiceProvider.GetService(p.ParameterType))<\/b>\n                    <b class=\"fm-bold\">.ToArray())!);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The code in listing 14.24 isn\u2019t as efficient as the approach taken by the ASP.NET Core platform for middleware components. All the parameters defined by the method that handles requests are treated as services to be resolved, except for the <code class=\"fm-code-in-text\">HttpContext<\/code> parameter. A route is created with a delegate that resolves the services for every request and invokes the method that handles the request. Listing 14.25 revises the <code class=\"fm-code-in-text\">WeatherEndpoint<\/code> class to move the dependency on <code class=\"fm-code-in-text\">IResponseFormatter<\/code> to the <code class=\"fm-code-in-text\">Endpoint<\/code> method so that a new service object will be received for every request.<a id=\"calibre_link-1797\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.25 Moving the dependency in the Platform\/WeatherEndpoint.cs file<\/p>\n<pre class=\"programlisting\">using Platform.Services;\n\nnamespace Platform {\n    public class WeatherEndpoint {\n        <b class=\"fm-bold\">\/\/private IResponseFormatter formatter;<\/b>\n                \n        <b class=\"fm-bold\">\/\/public WeatherEndpoint(IResponseFormatter responseFormatter) {<\/b>\n        <b class=\"fm-bold\">\/\/    formatter = responseFormatter;<\/b>\n        <b class=\"fm-bold\">\/\/}<\/b>\n                \n        <b class=\"fm-bold\">public async Task Endpoint(HttpContext context,<\/b> \n                <b class=\"fm-bold\">IResponseFormatter formatter) {<\/b>\n            await formatter.Format(context,\n                \"Endpoint Class: It is cloudy in Milan\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The changes in listing 14.23 to listing 14.25 ensure that the transient service is resolved for every request, which means that a new <code class=\"fm-code-in-text\">GuidService<\/code> object is created and every response contains a unique ID.<\/p>\n<p class=\"body\">Restart ASP.NET Core, navigate to http:\/\/localhost:5000\/endpoint\/class, and click the browser\u2019s reload button. Each time you reload, a new request is sent to ASP.NET Core, and the component or endpoint that handles the request receives a new service object, such that a different GUID is shown in each response, as shown in figure 14.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre128\" src=\"\/images\/proaspnetcore7\/000126.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.8 Using a transient service<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-272\">14.4.3 Using scoped services<\/h3>\n<p class=\"body\">Scoped services strike a balance between singleton and transient services. Within a scope, dependencies are resolved with the same object. A new scope is started for each HTTP request, which means that a service object will be shared by all the components that handle that request. To prepare for a scoped service, listing 14.26 changes the <code class=\"fm-code-in-text\">WeatherMiddleware<\/code> class to declare three dependencies on the same service.<a id=\"calibre_link-1798\"><\/a><a id=\"calibre_link-1799\"><\/a><a id=\"calibre_link-917\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.26 Adding dependencies in the Platform\/WeatherMiddleware.cs file<\/p>\n<pre class=\"programlisting\">using Platform.Services;\n\nnamespace Platform {\n    public class WeatherMiddleware {\n        private RequestDelegate next;\n                \n        public WeatherMiddleware(RequestDelegate nextDelegate) {\n            next = nextDelegate;\n        }\n                \n        <b class=\"fm-bold\">public async Task Invoke(HttpContext context,<\/b> \n                <b class=\"fm-bold\">IResponseFormatter formatter1,<\/b>\n                <b class=\"fm-bold\">IResponseFormatter formatter2,<\/b> \n                <b class=\"fm-bold\">IResponseFormatter formatter3) {<\/b>\n            <b class=\"fm-bold\">if (context.Request.Path == \"\/middleware\/class\") {<\/b>\n                <b class=\"fm-bold\">await formatter1.Format(context, string.Empty);<\/b>\n                <b class=\"fm-bold\">await formatter2.Format(context, string.Empty);<\/b>\n                <b class=\"fm-bold\">await formatter3.Format(context, string.Empty);<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">await next(context);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Declaring several dependencies on the same service isn\u2019t required in real projects, but it is useful for this example because each dependency is resolved independently. Since the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service was created with the <code class=\"fm-code-in-text\">AddTransient<\/code> method, each dependency is resolved with a different object. Restart ASP.NET Core and request http:\/\/localhost:5000\/middleware\/class, and you will see that a different GUID is used for each of the three messages written to the response, as shown in figure 14.9. When you reload the browser, a new set of three GUIDs is displayed.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre129\" src=\"\/images\/proaspnetcore7\/000127.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.9 Resolving dependencies on a transient service<\/p>\n<\/div>\n<p class=\"body\">Listing 14.27 changes the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service to use the scoped lifecycle with the <code class=\"fm-code-in-text\">AddScoped<\/code> method.<a id=\"calibre_link-1800\"><\/a><a id=\"calibre_link-1801\"><\/a><a id=\"calibre_link-907\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can create scopes through the <code class=\"fm-code-in-text1\">CreateScope<\/code> extension method for the <code class=\"fm-code-in-text1\">IServiceProvider<\/code> interface. The result is an <code class=\"fm-code-in-text1\">IServiceProvider<\/code> that is associated with a new scope and that will have its own implementation objects for scoped services.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.27 Using a scoped service in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;IResponseFormatter, GuidService&gt;();<\/b>\n\nvar app = builder.Build();\n\napp.UseMiddleware&lt;WeatherMiddleware&gt;();\n\napp.MapEndpoint&lt;WeatherEndpoint&gt;(\"endpoint\/class\");\n\napp.MapGet(\"endpoint\/function\", \n    async (HttpContext context, IResponseFormatter formatter) =&gt; {\n        await formatter.Format(context, \n            \"Endpoint Function: It is sunny in LA\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/middleware\/class again, and you will see that the same GUID is used to resolve all three dependencies declared by the middleware component, as shown in figure 14.10. When the browser is reloaded, the HTTP request sent to ASP.NET Core creates a new scope and a new service object.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre130\" src=\"\/images\/proaspnetcore7\/000128.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.10 Using a scoped service<\/p>\n<\/div>\n<p class=\"fm-head2\">Avoiding the scoped service validation pitfall<\/p>\n<p class=\"body\">Service consumers are unaware of the lifecycle that has been selected for singleton and transient services: they declare a dependency or request a service and get the object they require.<\/p>\n<p class=\"body\">Scoped services can be used only within a scope. A new scope is created automatically for each request that was received. Requesting a scoped service outside of a scope causes an exception. To see the problem, request http:\/\/localhost:5000\/endpoint\/class, which will generate the exception response shown in figure 14.11.<a id=\"calibre_link-911\"><\/a><a id=\"calibre_link-1802\"><\/a><a id=\"calibre_link-1803\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre131\" src=\"\/images\/proaspnetcore7\/000129.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.11 Requesting a scoped service<\/p>\n<\/div>\n<p class=\"body\">The extension method that configures the endpoint resolves services through an <code class=\"fm-code-in-text\">IServiceProvider<\/code> object obtained from the routing middleware, like this:<\/p>\n<pre class=\"programlisting\">...\napp.ServiceProvider.<b class=\"fm-bold\">GetService<\/b>(p.ParameterType))\n...<\/pre>\n<p class=\"fm-head2\">Accessing scoped services through the context object<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HttpContext<\/code> class defines a <code class=\"fm-code-in-text\">RequestServices<\/code> property that returns an <code class=\"fm-code-in-text\">IServiceProvider<\/code> object that allows access to scoped services, as well as singleton and transient services. This fits well with the most common use of scoped services, which is to use a single service object for each HTTP request. Listing 14.28 revises the endpoint extension method so that dependencies are resolved using the services provided through the <code class=\"fm-code-in-text\">HttpContext<\/code>.<a id=\"calibre_link-1804\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.28 Using a service in the EndpointExtensions.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">using System.Reflection;\n\nnamespace Microsoft.AspNetCore.Builder {\n\n    public static class EndpointExtensions {\n        \n        public static void MapEndpoint&lt;T&gt;(this IEndpointRouteBuilder app,\n                string path, string methodName = \"Endpoint\") {\n                                \n            MethodInfo? methodInfo = typeof(T).GetMethod(methodName);\n            if (methodInfo?.ReturnType != typeof(Task)) {\n                throw new System.Exception(\"Method cannot be used\");\n            }\n            T endpointInstance =\n                ActivatorUtilities.CreateInstance&lt;T&gt;(app.ServiceProvider);\n                                \n            ParameterInfo[] methodParams = methodInfo!.GetParameters();\n                        \n            app.MapGet(path, context =&gt; \n                (Task)methodInfo.Invoke(endpointInstance,\n                  methodParams.Select(p =&gt; \n                    p.ParameterType == typeof(HttpContext) ? context\n                      <b class=\"fm-bold\">: context.RequestServices<\/b>\n                        <b class=\"fm-bold\">.GetService(p.ParameterType))<\/b>\n                    .ToArray())!);\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1805\"><\/a>Using the <code class=\"fm-code-in-text\">HttpContext.RequestServices<\/code> property ensures that services are resolved within the scope of the current HTTP request, which ensures that endpoints don\u2019t use scoped services inappropriately.<\/p>\n<p class=\"fm-head3\">Creating new handlers for each request<\/p>\n<p class=\"body\">Notice that the <code class=\"fm-code-in-text\">ActivatorUtilities.CreateInstance&lt;T&gt;<\/code> method is still used to create an instance of the endpoint class in listing 14.28.<\/p>\n<p class=\"body\">This presents a problem because it requires endpoint classes to know the lifecycles of the services on which they depend. The <code class=\"fm-code-in-text\">WeatherEndpoint<\/code> class depends on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service and must know that a dependency can be declared only through the <code class=\"fm-code-in-text\">Endpoint<\/code> method and not the constructor.<\/p>\n<p class=\"body\">To remove the need for this knowledge, a new instance of the endpoint class can be created to handle each request, as shown in listing 14.29, which allows constructor and method dependencies to be resolved without needing to know which services are scoped.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.29 Instantiating in the EndpointExtensions.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">using System.Reflection;\n\nnamespace Microsoft.AspNetCore.Builder {\n\n    public static class EndpointExtensions {\n        \n        public static void MapEndpoint&lt;T&gt;(this IEndpointRouteBuilder app,\n                string path, string methodName = \"Endpoint\") {\n                                \n            MethodInfo? methodInfo = typeof(T).GetMethod(methodName);\n            if (methodInfo?.ReturnType != typeof(Task)) {\n                throw new System.Exception(\"Method cannot be used\");\n            }\n            <b class=\"fm-bold\">\/\/T endpointInstance =<\/b>\n            <b class=\"fm-bold\">\/\/ActivatorUtilities.CreateInstance&lt;T&gt; (app.ServiceProvider);<\/b>\n                        \n            ParameterInfo[] methodParams = methodInfo!.GetParameters();\n                        \n            <b class=\"fm-bold\">app.MapGet(path, context =&gt; {<\/b>\n                <b class=\"fm-bold\">T endpointInstance =<\/b>\n                    <b class=\"fm-bold\">ActivatorUtilities.CreateInstance&lt;T&gt;<\/b>\n                        <b class=\"fm-bold\">(context.RequestServices);<\/b>\n                <b class=\"fm-bold\">return (Task)methodInfo.Invoke(endpointInstance!,<\/b>\n                    <b class=\"fm-bold\">methodParams.Select(p =&gt;<\/b> \n                    <b class=\"fm-bold\">p.ParameterType == typeof(HttpContext)<\/b>\n                    <b class=\"fm-bold\">? context<\/b>\n                    <b class=\"fm-bold\">: context.RequestServices.GetService(p.ParameterType))<\/b>\n                        <b class=\"fm-bold\">.ToArray())!;<\/b>\n            });\n        }\n    }\n}<\/pre>\n<p class=\"body\">This approach requires a new instance of the endpoint class to handle each request, but it ensures that no knowledge of service lifecycles is required.<a id=\"calibre_link-1806\"><\/a><\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/endpoint\/class. The scoped service will be obtained from the context, producing the responses shown in figure 14.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre132\" src=\"\/images\/proaspnetcore7\/000130.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.12 Using scoped services in lambda functions<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Accessing scoped services when configuring the request pipeline<\/p>\n<p class=\"fm-sidebar-text\">If you require a scoped service to configure the application in the <code class=\"fm-code-in-text1\">Program.cs<\/code> file, then you can create a new scope and then request the service, like this:<\/p>\n<pre class=\"programlisting\">...\napp.Services.<b class=\"fm-bold\">CreateScope<\/b>().ServiceProvider\n    .GetRequiredService&lt;MyScopedService&gt;();\n...<\/pre>\n<p class=\"fm-sidebar-text\">The <code class=\"fm-code-in-text1\">CreateScope<\/code> method creates a scope that allows scoped services to be accessed. If you try to obtain a scoped service without creating a scope, then you will receive an exception.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-273\">14.5 Other dependency injection features<\/h2>\n<p class=\"body\"><a id=\"calibre_link-910\"><\/a>In the sections that follow, I describe some additional features available when using dependency injection. These are not required for all projects, but they are worth understanding because they provide context for how dependency injection works and can be helpful when the standard features are not quite what a project requires.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-274\">14.5.1 Creating dependency chains<\/h3>\n<p class=\"body\">When a class is instantiated to resolve a service dependency, its constructor is inspected, and any dependencies on services are resolved. This allows one service to declare a dependency on another service, creating a chain that is resolved automatically. To demonstrate, add a class file called <code class=\"fm-code-in-text\">TimeStamping.cs<\/code> to the <code class=\"fm-code-in-text\">Platform\/Services<\/code> folder with the code shown in listing 14.30.<a id=\"calibre_link-1807\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.30 The contents of the TimeStamping.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n\n    public interface ITimeStamper {\n        string TimeStamp { get; }\n    }\n        \n    public class DefaultTimeStamper : ITimeStamper {\n        \n        public string TimeStamp {\n            get =&gt; DateTime.Now.ToShortTimeString();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The class file defines an interface named <code class=\"fm-code-in-text\">ITimeStamper<\/code> and an implementation class named <code class=\"fm-code-in-text\">DefaultTimeStamper<\/code>. Next, add a file called <code class=\"fm-code-in-text\">TimeResponseFormatter.cs<\/code> to the <code class=\"fm-code-in-text\">Platform\/Services<\/code> folder with the code shown in listing 14.31.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.31 The contents of the TimeResponseFormatter.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n    public class TimeResponseFormatter : IResponseFormatter {\n        private ITimeStamper stamper;\n                \n        public TimeResponseFormatter(ITimeStamper timeStamper) {\n            stamper = timeStamper;\n        }\n                \n        public async Task Format(HttpContext context, string content) {\n            await context.Response.WriteAsync($\"{stamper.TimeStamp}: \" \n                + content);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">TimeResponseFormatter<\/code> class is an implementation of the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface that declares a dependency on the <code class=\"fm-code-in-text\">ITimeStamper<\/code> interface with a constructor parameter. Listing 14.32 defines services for both interfaces in the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Services don\u2019t need to have the same lifecycle as their dependencies, but you can end up with odd effects if you mix lifecycles. Lifecycles are applied only when a dependency is resolved, which means that if a scoped service depends on a transient service, for example, then the transient object will behave as though it was assigned the scoped lifecycle.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.32 Configuring services in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;IResponseFormatter, TimeResponseFormatter&gt;();<\/b>\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;ITimeStamper, DefaultTimeStamper&gt;();<\/b>\n\nvar app = builder.Build();\n\napp.UseMiddleware&lt;WeatherMiddleware&gt;();\n\napp.MapEndpoint&lt;WeatherEndpoint&gt;(\"endpoint\/class\");\n\napp.MapGet(\"endpoint\/function\", \n    async (HttpContext context, IResponseFormatter formatter) =&gt; {\n        await formatter.Format(context, \n            \"Endpoint Function: It is sunny in LA\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">When a dependency on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service is resolved, the <code class=\"fm-code-in-text\">TimeResponseFormatter<\/code> constructor will be inspected, and its dependency on the <code class=\"fm-code-in-text\">ITimeStamper<\/code> service will be detected. A <code class=\"fm-code-in-text\">DefaultTimeStamper<\/code> object will be created and injected into the <code class=\"fm-code-in-text\">TimeResponseFormatter<\/code> constructor, which allows the original dependency to be resolved. To see the dependency chain in action, restart ASP.NET Core and request http:\/\/localhost:5000\/endpoint\/class, and you will see the timestamp generated by the <code class=\"fm-code-in-text\">DefaultTimeStamper<\/code> class included in the response produced by the <code class=\"fm-code-in-text\">TimeResponseFormatter<\/code> class, as shown in figure 14.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre133\" src=\"\/images\/proaspnetcore7\/000131.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.13 Creating a chain of dependencies<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-275\">14.5.2 Accessing services in the Program.cs file<\/h3>\n<p class=\"body\">A common requirement is to use the application\u2019s configuration settings to alter the set of services that are created in the <code class=\"fm-code-in-text\">Program.cs<\/code> file. This presents a problem because the configuration is presented as a service and services cannot normally be accessed until after the <code class=\"fm-code-in-text\">WebApplicationBuilder.Build<\/code> method is invoked.<\/p>\n<p class=\"body\">To address this issue, the <code class=\"fm-code-in-text\">WebApplication<\/code> and <code class=\"fm-code-in-text\">WebApplicationBuilder<\/code> classes define properties that provide access to the built-in services that provide access to the application configuration, as described in table 14.6.<a id=\"calibre_link-1808\"><\/a><a id=\"calibre_link-925\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 14.6 The WebApplication and WebApplicationBuilder properties for configuration services<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1809\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IConfiguration<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an implementation of the <code class=\"fm-code-in-text1\">IConfiguration<\/code> interface, which provides access to the application\u2019s configuration settings, as described in chapter 15. <code class=\"fm-code-in-text1\">Configuration.<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Environment<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an implementation of the <code class=\"fm-code-in-text1\">IWebHostEnvironment<\/code> interface, which provides information about the environment in which the application is being executed and whose principal use is to determine if the application is configured for development or deployment, as described in chapter 15.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">These services are described in chapter 15, but what\u2019s important for this chapter is that they can be used to customize which services are configured in the <code class=\"fm-code-in-text\">Program.cs<\/code> file, as shown in listing 14.33.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.33 Accessing configuration data in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">IWebHostEnvironment env = builder.Environment;<\/b>\n<b class=\"fm-bold\">if (env.IsDevelopment()) {<\/b>\n    <b class=\"fm-bold\">builder.Services.AddScoped&lt;IResponseFormatter,<\/b> \n        <b class=\"fm-bold\">TimeResponseFormatter&gt;();<\/b>\n    <b class=\"fm-bold\">builder.Services.AddScoped&lt;ITimeStamper, DefaultTimeStamper&gt;();<\/b>\n<b class=\"fm-bold\">} else {<\/b>\n    <b class=\"fm-bold\">builder.Services.AddScoped&lt;IResponseFormatter,<\/b> \n        <b class=\"fm-bold\">HtmlResponseFormatter&gt;();<\/b>\n<b class=\"fm-bold\">}<\/b>\n\nvar app = builder.Build();\n\napp.UseMiddleware&lt;WeatherMiddleware&gt;();\n\napp.MapEndpoint&lt;WeatherEndpoint&gt;(\"endpoint\/class\");\n\napp.MapGet(\"endpoint\/function\", \n    async (HttpContext context, IResponseFormatter formatter) =&gt; {\n        await formatter.Format(context, \n            \"Endpoint Function: It is sunny in LA\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">This example uses the <code class=\"fm-code-in-text\">Environment<\/code> property to get an implementation of the <code class=\"fm-code-in-text\">IWebHostEnvironment<\/code> interface and uses its <code class=\"fm-code-in-text\">IsDevelopment<\/code> extension method to decide which services are set up for the application.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-276\">14.5.3 Using service factory functions<\/h3>\n<p class=\"body\">Factory functions allow you to take control of the way that service implementation objects are created, rather than relying on ASP.NET Core to create instances for you. There are factory versions of the <code class=\"fm-code-in-text\">AddSingleton<\/code>, <code class=\"fm-code-in-text\">AddTransient<\/code>, and <code class=\"fm-code-in-text\">AddScoped<\/code> methods, all of which are used with a function that receives an <code class=\"fm-code-in-text\">IServiceProvider<\/code> object and returns an implementation object for the service.<a id=\"calibre_link-1810\"><\/a><a id=\"calibre_link-912\"><\/a><\/p>\n<p class=\"body\">One use for factory functions is to define the implementation class for a service as a configuration setting, which is read through the <code class=\"fm-code-in-text\">IConfguration<\/code> service. This requires the <code class=\"fm-code-in-text\">WebApplicationBuilder<\/code> properties described in the previous section. Listing 14.34 adds a factory function for the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service that gets the implementation class from the configuration data.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.34 Using a factory function in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">\/\/IWebHostEnvironment env = builder.Environment;<\/b>\n<b class=\"fm-bold\">IConfiguration config = builder.Configuration;<\/b>\n\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;IResponseFormatter&gt;(serviceProvider =&gt; {<\/b>\n    <b class=\"fm-bold\">string? typeName = config[\"services:IResponseFormatter\"];<\/b>\n    <b class=\"fm-bold\">return (IResponseFormatter)ActivatorUtilities<\/b>\n        <b class=\"fm-bold\">.CreateInstance(serviceProvider, typeName == null<\/b>\n            <b class=\"fm-bold\">? typeof(GuidService) : Type.GetType(typeName, true)!);<\/b>\n<b class=\"fm-bold\">});<\/b>\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;ITimeStamper, DefaultTimeStamper&gt;();<\/b>\n\nvar app = builder.Build();\n\napp.UseMiddleware&lt;WeatherMiddleware&gt;();\n\napp.MapEndpoint&lt;WeatherEndpoint&gt;(\"endpoint\/class\");\n\napp.MapGet(\"endpoint\/function\", \n    async (HttpContext context, IResponseFormatter formatter) =&gt; {\n        await formatter.Format(context, \n            \"Endpoint Function: It is sunny in LA\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The factory function reads a value from the configuration data, which is converted into a type and passed to the <code class=\"fm-code-in-text\">ActivatorUtilities.CreateInstance<\/code> method. Listing 14.35 adds a configuration setting to the <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> file that selects the <code class=\"fm-code-in-text\">HtmlResponseFormatter<\/code> class as the implementation for the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service. The JSON configuration file is described in detail in chapter 15.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.35 A setting in the appsettings.Development.json file in the Platform folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  <b class=\"fm-bold\">\"services\": {<\/b>\n    <b class=\"fm-bold\">\"IResponseFormatter\": \"Platform.Services.HtmlResponseFormatter\"<\/b>\n  <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">When a dependency on the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service is resolved, the factory function creates an instance of the type specified in the configuration file. Restart ASP.NET Core and request the http:\/\/localhost:5000\/endpoint\/class URL, which will produce the response shown in figure 14.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre134\" src=\"\/images\/proaspnetcore7\/000132.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.14 Using a service factory<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-277\">14.5.4 Creating services with multiple implementations<\/h3>\n<p class=\"body\">Services can be defined with multiple implementations, which allows a consumer to select an implementation that best suits a specific problem. This is a feature that works best when the service interface provides insight into the capabilities of each implementation class. To provide information about the capabilities of the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> implementation classes, add the default property shown in listing 14.36 to the interface.<a id=\"calibre_link-1811\"><\/a><a id=\"calibre_link-926\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.36 Adding a property in the IResponseFormatter.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n    public interface IResponseFormatter {\n        \n        Task Format(HttpContext context, string content);\n                \n        <b class=\"fm-bold\">public bool RichOutput =&gt; false;<\/b>\n    }\n}<\/pre>\n<p class=\"body\">This <code class=\"fm-code-in-text\">RichOutput<\/code> property will be <code class=\"fm-code-in-text\">false<\/code> for implementation classes that don\u2019t override the default value. To ensure there is one implementation that returns <code class=\"fm-code-in-text\">true<\/code>, add the property shown in listing 14.37 to the <code class=\"fm-code-in-text\">HtmlResponseFormatter<\/code> class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.37 Overriding in the HtmlResponseFormatter.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Services {\n    public class HtmlResponseFormatter : IResponseFormatter {\n        \n        public async Task Format(HttpContext context, string content) {\n            context.Response.ContentType = \"text\/html\";\n            await context.Response.WriteAsync($@\"\n                &lt;!DOCTYPE html&gt;\n\n                &lt;html lang=\"\"en\"\"&gt;\n                &lt;head&gt;&lt;title&gt;Response&lt;\/title&gt;&lt;\/head&gt;\n                &lt;body&gt;\n                    &lt;h2&gt;Formatted Response&lt;\/h2&gt;\n                    &lt;span&gt;{content}&lt;\/span&gt;\n                &lt;\/body&gt;\n                &lt;\/html&gt;\");\n        }\n                \n        <b class=\"fm-bold\">public bool RichOutput =&gt; true;<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Listing 14.38 registers multiple implementations for the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service, which is done by making repeated calls to the <code class=\"fm-code-in-text\">Add&lt;lifecycle&gt;<\/code> method. The listing also replaces the existing request pipeline with two routes that demonstrate how the service can be used.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.38 Defining and using a service in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">\/\/using Platform;<\/b>\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">\/\/IConfiguration config = builder.Configuration;<\/b>\n\n<b class=\"fm-bold\">\/\/builder.Services.AddScoped&lt;IResponseFormatter&gt;(serviceProvider =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/    string? typeName = config[\"services:IResponseFormatter\"];<\/b>\n<b class=\"fm-bold\">\/\/    return (IResponseFormatter)ActivatorUtilities<\/b>\n<b class=\"fm-bold\">\/\/        .CreateInstance(serviceProvider, typeName == null<\/b>\n<b class=\"fm-bold\">\/\/            ? typeof(GuidService) : Type.GetType(typeName, true)!);<\/b>\n<b class=\"fm-bold\">\/\/});<\/b>\n<b class=\"fm-bold\">\/\/builder.Services.AddScoped&lt;ITimeStamper, DefaultTimeStamper&gt;();<\/b>\n\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;IResponseFormatter, TextResponseFormatter&gt;();<\/b>\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;IResponseFormatter, HtmlResponseFormatter&gt;();<\/b>\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;IResponseFormatter, GuidService&gt;();<\/b>\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">\/\/app.UseMiddleware&lt;WeatherMiddleware&gt;();<\/b>\n\n<b class=\"fm-bold\">\/\/app.MapEndpoint&lt;WeatherEndpoint&gt;(\"endpoint\/class\");<\/b>\n\n<b class=\"fm-bold\">\/\/app.MapGet(\"endpoint\/function\",<\/b> \n<b class=\"fm-bold\">\/\/    async (HttpContext context, IResponseFormatter formatter) =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/        await formatter.Format(context,<\/b> \n<b class=\"fm-bold\">\/\/            \"Endpoint Function: It is sunny in LA\");<\/b>\n<b class=\"fm-bold\">\/\/});<\/b>\n\n<b class=\"fm-bold\">app.MapGet(\"single\", async context =&gt; {<\/b>\n    <b class=\"fm-bold\">IResponseFormatter formatter = context.RequestServices<\/b>\n        <b class=\"fm-bold\">.GetRequiredService&lt;IResponseFormatter&gt;();<\/b>\n    <b class=\"fm-bold\">await formatter.Format(context, \"Single service\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\n<b class=\"fm-bold\">app.MapGet(\"\/\", async context =&gt; {<\/b>\n    <b class=\"fm-bold\">IResponseFormatter formatter = context.RequestServices<\/b>\n        <b class=\"fm-bold\">.GetServices&lt;IResponseFormatter&gt;().First(f =&gt; f.RichOutput);<\/b>\n    <b class=\"fm-bold\">await formatter.Format(context, \"Multiple services\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddScoped<\/code> statements register three services for the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> interface, each with a different implementation class. The route for the <code class=\"fm-code-in-text\">\/single<\/code> URL uses the <code class=\"fm-code-in-text\">IServiceProvider.GetRequiredService&lt;T&gt;<\/code> method to request a service, like this:<\/p>\n<pre class=\"programlisting\">...\ncontext.RequestServices.<b class=\"fm-bold\">GetRequiredService<\/b>&lt;IResponseFormatter&gt;();\n...<\/pre>\n<p class=\"body\">This is a service consumer that is unaware that there are multiple implementations available. The service is resolved using the most recently registered implementation, which is the <code class=\"fm-code-in-text\">GuidService<\/code> class. Restart ASP.NET Core and request http:\/\/localhost:5000\/single, and you will see the output on the left side of figure 14.15.<\/p>\n<p class=\"body\">The other endpoint is a service consumer that is aware that multiple implementations may be available and that requests the service using the <code class=\"fm-code-in-text\">IServiceProvider.GetServices&lt;T&gt;<\/code> method.<\/p>\n<pre class=\"programlisting\">...\ncontext.RequestServices.<b class=\"fm-bold\">GetServices<\/b>&lt;IResponseFormatter&gt;()\n    .First(f =&gt; f.RichOutput);\n...<\/pre>\n<p class=\"body\">This method returns an <code class=\"fm-code-in-text\">IEnumerable&lt;IResponseFormatter&gt;<\/code> that enumerates the available implementations. These are filtered using the LINQ <code class=\"fm-code-in-text\">First<\/code> method to select an implementation whose <code class=\"fm-code-in-text\">RichOutput<\/code> property returns <code class=\"fm-code-in-text\">true<\/code>. If you request http:\/\/localhost:5000, you will see the output on the right of figure 14.15, showing that the endpoint has selected the service implementation that best suits its needs.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre122\" src=\"\/images\/proaspnetcore7\/000133.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.15 Using multiple service implementations<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-278\">14.5.5 Using unbound types in services<\/h3>\n<p class=\"body\">Services can be defined with generic type parameters that are bound to specific types when the service is requested, as shown in listing 14.39 <a id=\"calibre_link-1812\"><\/a><a id=\"calibre_link-929\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 14.39 Using an unbound type in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">\/\/using Platform.Services;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">\/\/builder.Services.AddScoped&lt;IResponseFormatter, TextResponseFormatter&gt;();<\/b>\n<b class=\"fm-bold\">\/\/builder.Services.AddScoped&lt;IResponseFormatter, HtmlResponseFormatter&gt;();<\/b>\n<b class=\"fm-bold\">\/\/builder.Services.AddScoped&lt;IResponseFormatter, GuidService&gt;();<\/b>\n\n<b class=\"fm-bold\">builder.Services.AddSingleton(typeof(ICollection&lt;&gt;), typeof(List&lt;&gt;));<\/b>\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">\/\/app.MapGet(\"single\", async context =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/    IResponseFormatter formatter = context.RequestServices<\/b>\n<b class=\"fm-bold\">\/\/        .GetRequiredService&lt;IResponseFormatter&gt;();<\/b>\n<b class=\"fm-bold\">\/\/    await formatter.Format(context, \"Single service\");<\/b>\n<b class=\"fm-bold\">\/\/});<\/b>\n\n<b class=\"fm-bold\">\/\/app.MapGet(\"\/\", async context =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/    IResponseFormatter formatter = context.RequestServices<\/b>\n<b class=\"fm-bold\">\/\/        .GetServices&lt;IResponseFormatter&gt;().First(f =&gt; f.RichOutput);<\/b>\n<b class=\"fm-bold\">\/\/    await formatter.Format(context, \"Multiple services\");<\/b>\n<b class=\"fm-bold\">\/\/});<\/b>\n\n<b class=\"fm-bold\">app.MapGet(\"string\", async context =&gt; {<\/b>\n    <b class=\"fm-bold\">ICollection&lt;string&gt; collection = context.RequestServices<\/b>\n        <b class=\"fm-bold\">.GetRequiredService&lt;ICollection&lt;string&gt;&gt;();<\/b>\n    <b class=\"fm-bold\">collection.Add($\"Request: {DateTime.Now.ToLongTimeString()}\");<\/b>\n    <b class=\"fm-bold\">foreach (string str in collection) {<\/b>\n        <b class=\"fm-bold\">await context.Response.WriteAsync($\"String: {str}\\n\");<\/b>\n    <b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">});<\/b>\n\n<b class=\"fm-bold\">app.MapGet(\"int\", async context =&gt; {<\/b>\n    <b class=\"fm-bold\">ICollection&lt;int&gt; collection<\/b>\n        <b class=\"fm-bold\">= context.RequestServices.GetRequiredService&lt;ICollection&lt;int&gt;&gt;();<\/b>\n    <b class=\"fm-bold\">collection.Add(collection.Count() + 1);<\/b>\n    <b class=\"fm-bold\">foreach (int val in collection) {<\/b>\n        <b class=\"fm-bold\">await context.Response.WriteAsync($\"Int: {val}\\n\");<\/b>\n    <b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1813\"><\/a>This feature relies on the versions of the <code class=\"fm-code-in-text\">AddSingleton<\/code>, <code class=\"fm-code-in-text\">AddScoped<\/code>, and <code class=\"fm-code-in-text\">AddTransient<\/code> methods that accept types as conventional arguments and cannot be performed using generic type arguments. The service in listing 14.39 is created with unbound types, like this:<\/p>\n<pre class=\"programlisting\">...\nservices.AddSingleton(<b class=\"fm-bold\">typeof(ICollection&lt;&gt;), typeof(List&lt;&gt;)<\/b>);\n...<\/pre>\n<p class=\"body\">When a dependency on an <code class=\"fm-code-in-text\">ICollection&lt;T&gt;<\/code> service is resolved, a <code class=\"fm-code-in-text\">List&lt;T&gt;<\/code> object will be created so that a dependency on <code class=\"fm-code-in-text\">ICollection&lt;string&gt;<\/code>, for example, will be resolved using a <code class=\"fm-code-in-text\">List&lt;string&gt;<\/code> object. Rather than require separate services for each type, the unbound service allows mappings for all generic types to be created.<\/p>\n<p class=\"body\">The two endpoints in listing 14.39 request <code class=\"fm-code-in-text\">ICollection&lt;string&gt;<\/code> and <code class=\"fm-code-in-text\">ICollection&lt;int&gt;<\/code> services, each of which will be resolved with a different <code class=\"fm-code-in-text\">List&lt;T&gt;<\/code> object. To target the endpoints, restart ASP.NET Core and request http:\/\/localhost:5000\/string and http:\/\/localhost:5000\/int. The service has been defined as a singleton, which means that the same <code class=\"fm-code-in-text\">List&lt;string&gt;<\/code> and <code class=\"fm-code-in-text\">List&lt;int&gt;<\/code> objects will be used to resolve all requests for <code class=\"fm-code-in-text\">ICollection&lt;string&gt;<\/code> and <code class=\"fm-code-in-text\">ICollection&lt;int&gt;<\/code>. Each request adds a new item to the collection, which you can see by reloading the web browser, as shown in figure 14.16.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre135\" src=\"\/images\/proaspnetcore7\/000134.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 14.16 Using a singleton service with an unbound type<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-279\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><a class=\"calibre58\" id=\"calibre_link-1814\"><\/a>Dependency injection allows application components to declare dependencies on services by defining constructor parameters.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Services can be defined with a type, an object, or a factory function.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The scope of a service determines when services are instantiated and how they are shared between components.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Dependency injection is integrated into the ASP.NET Core request pipeline.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-280\">\n<div class=\"calibre1\" id=\"calibre_link-1815\">\n<h1 class=\"tochead\" id=\"calibre_link-1816\"><a id=\"calibre_link-1817\"><\/a><a id=\"calibre_link-1818\"><\/a>15 Using the platform features, part 1<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Understanding the built-in features provided by ASP.NET Core<\/li>\n<li class=\"co-summary-bullet\">Accessing the application configuration<\/li>\n<li class=\"co-summary-bullet\">Storing secrets outside of the project folder<\/li>\n<li class=\"co-summary-bullet\">Logging messages<\/li>\n<li class=\"co-summary-bullet\">Generating static content and using client-side packages<\/li>\n<\/ul>\n<p class=\"body\">ASP.NET Core includes a set of built-in services and middleware components that provide features that are commonly required by web applications. In this chapter, I describe three of the most important and widely used features: application configuration, logging, and serving static content. In chapter 16, I continue to describe the platform features, focusing on the more advanced built-in services and middleware. Table 15.1 puts the chapter in context.<\/p>\n<p class=\"fm-table-caption\">Table 15.1 Putting platform features in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1819\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The platform features deal with common web application requirements, such as configuration, logging, static files, sessions, authentication, and database access.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using these features means you don\u2019t have to re-create their functionality in your own projects.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The built-in middleware components are added to the request pipeline using extension methods whose name starts with <code class=\"fm-code-in-text1\">Use<\/code>. Services are set up using methods that start with <code class=\"fm-code-in-text1\">Add<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The most common problems relate to the order in which middleware components are added to the request pipeline. Middleware components form a chain along which requests pass, as described in chapter 12.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">You don\u2019t have to use any of the services or middleware components that ASP.NET Core provides.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 15.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 15.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1820\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Accessing the configuration data<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">IConfiguration<\/code> service.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">4&ndash;8<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Setting the application environment<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the launch settings file.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">9&ndash;11<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Determining the application environment<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">IWebHostEnvironment<\/code> service.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">12<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Keeping sensitive data outside of the project<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create user secrets.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">13&ndash;17<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Logging messages<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">ILogger&lt;T&gt;<\/code> service.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">18&ndash;27<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Delivering static content<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Enable the static content middleware.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">28&ndash;31<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Delivering client-side packages<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Install the package with LibMan and deliver it with the static content middleware.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">32&ndash;35<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-281\">15.1 Preparing for this chapter<\/h2>\n<p class=\"body\">In this chapter, I continue to use the Platform project created in chapter 14. To prepare for this chapter, update the <code class=\"fm-code-in-text\">Program.cs<\/code> file to remove middleware and services, as shown in listing 15.1.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.1 The contents of the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">One of the main topics in this chapter is configuration data. Replace the contents of the <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> file with the contents of listing 15.2 to remove the setting added in chapter 14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.2 The contents of the appsettings.Development.json file in the Platform folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Debug\",\n      \"System\": \"Information\",\n      \"Microsoft\": \"Information\"\n    }\n  }\n}<\/pre>\n<p class=\"body\">Start the application by opening a new PowerShell command prompt, navigating to the <code class=\"fm-code-in-text\">Platform<\/code> project folder, and running the command shown in listing 15.3.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.3 Starting the ASP.NET Core runtime<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Open a new browser tab and navigate to http:\/\/localhost:5000; you will see the content shown in figure 15.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre136\" src=\"\/images\/proaspnetcore7\/000135.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-282\">15.2 Using the configuration service<\/h2>\n<p class=\"body\">One of the built-in features provided by ASP.NET Core is access to the application\u2019s configuration settings, which is presented as a service.<a id=\"calibre_link-1821\"><\/a><a id=\"calibre_link-1822\"><\/a><a id=\"calibre_link-739\"><\/a><\/p>\n<p class=\"body\">The main source of configuration data is the <code class=\"fm-code-in-text\">appsettings.json<\/code> file. The <code class=\"fm-code-in-text\">appsettings.json<\/code> file created by the template used in chapter 12 contains the following settings:<a id=\"calibre_link-1823\"><\/a><a id=\"calibre_link-1824\"><\/a><a id=\"calibre_link-1825\"><\/a><\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\"\n}<\/pre>\n<p class=\"body\">The configuration service will process the JSON configuration file and create nested configuration sections that contain individual settings. For the <code class=\"fm-code-in-text\">appsettings.json<\/code> file in the example application, the configuration service will create a <code class=\"fm-code-in-text\">Logging<\/code> configuration section that contains a <code class=\"fm-code-in-text\">LogLevel<\/code> section. The <code class=\"fm-code-in-text\">LogLevel<\/code> section will contain settings for <code class=\"fm-code-in-text\">Default<\/code> and <code class=\"fm-code-in-text\">Microsoft.AspnetCore<\/code>. There will also be an <code class=\"fm-code-in-text\">AllowedHosts<\/code> setting that isn\u2019t part of a configuration section and whose value is an asterisk (the <code class=\"fm-code-in-text\">*<\/code> character).<\/p>\n<p class=\"body\">The configuration service doesn\u2019t understand the meaning of the configuration sections or settings in the <code class=\"fm-code-in-text\">appsettings.json<\/code> file and is just responsible for processing the JSON data file and merging the configuration settings with the values obtained from other sources, such as environment variables or command-line arguments. The result is a hierarchical set of configuration properties, as shown in figure 15.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre137\" src=\"\/images\/proaspnetcore7\/000136.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.2 The hierarchy of configuration properties in the appsettings.json file<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-283\">15.2.1 Understanding the environment configuration file<\/h3>\n<p class=\"body\">Most projects contain more than one JSON configuration file, allowing different settings to be defined for different parts of the development cycle. There are three predefined environments, named <code class=\"fm-code-in-text\">Development<\/code>, <code class=\"fm-code-in-text\">Staging<\/code>, and <code class=\"fm-code-in-text\">Production<\/code>, each of which corresponds to a commonly used phase of development. During startup, the configuration service looks for a JSON file whose name includes the current environment. The default environment is <code class=\"fm-code-in-text\">Development<\/code>, which means the configuration service will load the <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> file and use its contents to supplement the contents of the main <code class=\"fm-code-in-text\">appsettings.json<\/code> file.<a id=\"calibre_link-1826\"><\/a><a id=\"calibre_link-1827\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The Visual Studio Solution Explorer nests the <code class=\"fm-code-in-text1\">appsettings.Development<\/code><code class=\"fm-code-in-text1\">.<\/code><code class=\"fm-code-in-text1\">json<\/code> file in the <code class=\"fm-code-in-text1\">appsettings.json<\/code> item. You can expand the <code class=\"fm-code-in-text1\">appsettings.json<\/code> file to see and edit the nested entries or click the button at the top of the Solution Explorer that disables the nesting feature.<\/p>\n<p class=\"body\">Here are the configuration settings added to the <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> file in listing 15.2:<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Debug\",\n      \"System\": \"Information\",\n      \"Microsoft\": \"Information\"\n    }\n  }\n}<\/pre>\n<p class=\"body\">Where the same setting is defined in both files, the value in the <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> file will replace the one in the <code class=\"fm-code-in-text\">appsettings.json<\/code> file, which means that the contents of the two JSON files will produce the hierarchy of configuration settings shown in figure 15.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre138\" src=\"\/images\/proaspnetcore7\/000137.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.3 Merging JSON configuration settings<\/p>\n<\/div>\n<p class=\"body\">The effect of the additional configuration settings is to increase the detail level of logging messages, which I describe in more detail in the \u201cUsing the Logging Service\u201d section.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-284\">15.2.2 Accessing configuration settings<\/h3>\n<p class=\"body\">The configuration data is accessed through a service. If you only require the configuration data to configure middleware, then the dependency on the configuration service can be declared using a parameter, as shown in listing 15.4.<a id=\"calibre_link-1828\"><\/a><a id=\"calibre_link-1829\"><\/a><a id=\"calibre_link-738\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.4 Accessing configuration data in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapGet(\"config\", async (HttpContext context,<\/b> \n        <b class=\"fm-bold\">IConfiguration config) =&gt; {<\/b>\n    <b class=\"fm-bold\">string? defaultDebug = config[\"Logging:LogLevel:Default\"];<\/b>\n    <b class=\"fm-bold\">await context.Response<\/b>\n        <b class=\"fm-bold\">.WriteAsync($\"The config setting is: {defaultDebug}\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">Configuration data is provided through the <code class=\"fm-code-in-text\">IConfiguration<\/code> interface; this interface is defined in the <code class=\"fm-code-in-text\">Microsoft.Extensions.Configuration<\/code> namespace and provides an API for navigating through the configuration hierarchy and reading configuration settings. Configuration settings can be read by specifying the path through the configuration sections, like this:<\/p>\n<pre class=\"programlisting\">...\nstring? defaultDebug = <b class=\"fm-bold\">config[\"Logging:LogLevel:Default\"]<\/b>;\n...<\/pre>\n<p class=\"body\">This statement reads the value of the <code class=\"fm-code-in-text\">Default<\/code> setting, which is defined in the <code class=\"fm-code-in-text\">LogLevel<\/code> section of the <code class=\"fm-code-in-text\">Logging<\/code> part of the configuration. The names of the configuration sections and the configuration settings are separated by colons (the <code class=\"fm-code-in-text\">:<\/code> character).<\/p>\n<p class=\"body\">The value of the configuration setting read in listing 15.4 is used to provide a result for a middleware component that handles the <code class=\"fm-code-in-text\">\/config<\/code> URL. Restart ASP.NET Core using Control+C at the command prompt and run the command shown in listing 15.5 in the <code class=\"fm-code-in-text\">Platform<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.5 Starting the ASP.NET Core platform<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Once the runtime has restarted, navigate to the http:\/\/localhost:5000\/config URL, and you will see the value of the configuration setting displayed in the browser tab, as shown in figure 15.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre139\" src=\"\/images\/proaspnetcore7\/000138.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.4 Reading configuration data<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-285\">15.2.3 Using the configuration data in the Program.cs file<\/h3>\n<p class=\"body\"><a id=\"calibre_link-921\"><\/a>As noted in chapter 14, the <code class=\"fm-code-in-text\">WebApplication<\/code> and <code class=\"fm-code-in-text\">WebApplicationBuilder<\/code> classes provide a <code class=\"fm-code-in-text\">Configuration<\/code> property that can be used to obtain an implementation of the <code class=\"fm-code-in-text\">IConfiguration<\/code> interface, which is useful when using configuration data to configure an application\u2019s services. Listing 15.6 shows both uses of configuration data.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.6 Configuring services and pipeline in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">var servicesConfig = builder.Configuration;<\/b>\n<b class=\"fm-bold\">\/\/ - use configuration settings to set up services<\/b>\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">var pipelineConfig = app.Configuration;<\/b>\n<b class=\"fm-bold\">\/\/ - use configuration settings to set up pipeline<\/b>\n\napp.MapGet(\"config\", async (HttpContext context, \n        IConfiguration config) =&gt; {\n    string? defaultDebug = config[\"Logging:LogLevel:Default\"];\n    await context.Response\n        .WriteAsync($\"The config setting is: {defaultDebug}\");\n});\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">This may seem like an unnecessary step because there is so little code in the <code class=\"fm-code-in-text\">Program.cs<\/code> file in this example application, which makes it obvious that the configuration service isn\u2019t replaced. It isn\u2019t always as obvious in a real project, where services can be defined in groups by methods defined outside of the <code class=\"fm-code-in-text\">Program.cs<\/code> file, making it difficult to see if these methods alter the <code class=\"fm-code-in-text\">IConfiguration<\/code> service.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-286\">15.2.4 Using configuration data with the options pattern<\/h3>\n<p class=\"body\">In chapter 12, I described the options pattern, which is a useful way to configure middleware components. A helpful feature provided by the <code class=\"fm-code-in-text\">IConfiguration<\/code> service is the ability to create options directly from configuration data.<\/p>\n<p class=\"body\">To prepare, add the configuration settings shown in listing 15.7 to the <code class=\"fm-code-in-text\">appsettings.json<\/code> file.<a id=\"calibre_link-1830\"><\/a><a id=\"calibre_link-1831\"><\/a><a id=\"calibre_link-1832\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.7 Adding configuration data in the appsettings.json file in the Platform folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\",\n  <b class=\"fm-bold\">\"Location\": {<\/b>\n    <b class=\"fm-bold\">\"CityName\": \"Buffalo\"<\/b>\n  <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Location<\/code> section of the configuration file can be used to provide options pattern values, as shown in listing 15.8.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.8 Using configuration data in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Platform;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar servicesConfig = builder.Configuration;\n<b class=\"fm-bold\">builder.Services.Configure&lt;MessageOptions&gt;(<\/b>\n    <b class=\"fm-bold\">servicesConfig.GetSection(\"Location\"));<\/b>\n        \nvar app = builder.Build();\n\nvar pipelineConfig = app.Configuration;\n\/\/ - use configuration settings to set up pipeline\n\n<b class=\"fm-bold\">app.UseMiddleware&lt;LocationMiddleware&gt;();<\/b>\n\napp.MapGet(\"config\", async (HttpContext context, \n        IConfiguration config) =&gt; {\n    string? defaultDebug = config[\"Logging:LogLevel:Default\"];\n    await context.Response\n        .WriteAsync($\"The config setting is: {defaultDebug}\");\n});\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The configuration data is obtained using the <code class=\"fm-code-in-text\">GetSection<\/code> method and passed to the <code class=\"fm-code-in-text\">Configure<\/code> method when the options are created. The configuration values in the selected section are inspected and used to replace the default values with the same names in the options class. To see the effect, restart ASP.NET Core and use the browser to navigate to the http:\/\/localhost:5000\/location URL. You will see the results shown in figure 15.5, where the <code class=\"fm-code-in-text\">CityName<\/code> option is taken from the configuration data and the <code class=\"fm-code-in-text\">CountryName<\/code> option is taken from the default value in the options class.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre140\" src=\"\/images\/proaspnetcore7\/000139.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.5 Using configuration data in the options pattern<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-287\">15.2.5 Understanding the launch settings file<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">launchSettings.json<\/code> file in the <code class=\"fm-code-in-text\">Properties<\/code> folder contains the configuration settings for starting the ASP.NET Core platform, including the TCP ports that are used to listen for HTTP and HTTPS requests and the environment used to select the additional JSON configuration files.<a id=\"calibre_link-887\"><\/a><a id=\"calibre_link-1833\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Visual Studio often hides the <code class=\"fm-code-in-text1\">Properties<\/code> folder. If you can\u2019t see the folder, click the Show All Files button at the top of the Solution Explorer to reveal the folder and the <code class=\"fm-code-in-text1\">launchSettings.json<\/code> file.<\/p>\n<p class=\"body\">Here is the content added to the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file when the project was created and then edited to set the HTTP ports:<a id=\"calibre_link-1834\"><\/a><a id=\"calibre_link-1835\"><\/a><\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http:\/\/localhost:5000\",\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"Platform\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"applicationUrl\": \"http:\/\/localhost:5000\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n} <\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">iisSettings<\/code> section is used to configure the HTTP and HTTPS ports used when the ASP.NET Core platform is started through IIS Express, which is how older versions of ASP.NET Core were deployed.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">profiles<\/code> section describes a series of launch profiles, which define configuration settings for different ways of running the application. The <code class=\"fm-code-in-text\">Platform<\/code> section defines the configuration used by the <code class=\"fm-code-in-text\">dotnet run<\/code> command. The <code class=\"fm-code-in-text\">IIS Express<\/code> section defines the configuration used when the application is used with IIS Express.<\/p>\n<p class=\"body\">Both profiles contain an <code class=\"fm-code-in-text\">environmentVariables<\/code> section, which is used to define environment variables that are added to the application\u2019s configuration data. There is only one environment variable defined by default: <code class=\"fm-code-in-text\">ASPNETCORE_ENVIRONMENT<\/code>.<\/p>\n<p class=\"body\">During startup, the value of the <code class=\"fm-code-in-text\">ASPNETCORE_ENVIRONMENT<\/code> setting is used to select the additional JSON configuration file so that a value of <code class=\"fm-code-in-text\">Development<\/code>, for example, will cause the <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> file to be loaded.<a id=\"calibre_link-1836\"><\/a><a id=\"calibre_link-884\"><\/a><\/p>\n<p class=\"body\">When the application is started within Visual Studio Code, <code class=\"fm-code-in-text\">ASPNETCORE_ENVIRONMENT<\/code> is set in a different file. Select Run &gt; Open Configurations to open the <code class=\"fm-code-in-text\">launch.json<\/code> file in the <code class=\"fm-code-in-text\">.vscode<\/code> folder, which is created when a project is edited with Visual Studio Code. Here is the default configuration for the example project, showing the current <code class=\"fm-code-in-text\">ASPNETCORE_ENVIRONMENT<\/code> value, with the comments removed for brevity:<a id=\"calibre_link-1837\"><\/a><a id=\"calibre_link-1838\"><\/a><\/p>\n<pre class=\"programlisting\">{\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n  \n            \"name\": \".NET Core Launch (web)\",\n            \"type\": \"coreclr\",\n            \"request\": \"launch\",\n            \"preLaunchTask\": \"build\",\n            \"program\": \"${workspaceFolder}\/bin\/Debug\/net7.0\/Platform.dll\",\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\",\n            \"stopAtEntry\": false,\n            \"serverReadyAction\": {\n                \"action\": \"openExternally\",\n                \"pattern\": \"\\\\bNow listening on:\\\\s+(https?:\/\/\\\\S+)\"\n            },\n            \"env\": {\n                <b class=\"fm-bold\">\"ASPNETCORE_ENVIRONMENT\": \"Development\"<\/b>\n            },\n            \"sourceFileMap\": {\n                \"\/Views\": \"${workspaceFolder}\/Views\"\n            }\n        },\n        {\n            \"name\": \".NET Core Attach\",\n            \"type\": \"coreclr\",\n            \"request\": \"attach\"\n        }\n    ]\n}<\/pre>\n<p class=\"body\">To display the value of the <code class=\"fm-code-in-text\">ASPNETCORE_ENVIRONMENT<\/code> setting, add the statements to the middleware component that responds to the <code class=\"fm-code-in-text\">\/config<\/code> URL, as shown in listing 15.9.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.9 Displaying the configuration in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar servicesConfig = builder.Configuration;\nbuilder.Services.Configure&lt;MessageOptions&gt;(\n    servicesConfig.GetSection(\"Location\"));\n        \nvar app = builder.Build();\n\nvar pipelineConfig = app.Configuration;\n\napp.UseMiddleware&lt;LocationMiddleware&gt;();\n\napp.MapGet(\"config\", async (HttpContext context, \n        IConfiguration config) =&gt; {\n    string? defaultDebug = config[\"Logging:LogLevel:Default\"];\n    await context.Response\n        .WriteAsync($\"The config setting is: {defaultDebug}\");\n    <b class=\"fm-bold\">string? environ = config[\"ASPNETCORE_ENVIRONMENT\"];<\/b>\n    <b class=\"fm-bold\">await context.Response.WriteAsync($\"\\nThe env setting is: {environ}\");<\/b>\n});\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">Restart ASP.NET Core and navigate to http:\/\/localhost:5000\/config, and you will see the value of the <code class=\"fm-code-in-text\">ASPNETCORE_ENVIRONMENT<\/code> setting, as shown in figure 15.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre141\" src=\"\/images\/proaspnetcore7\/000140.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.6 Displaying the environment configuration setting<\/p>\n<\/div>\n<p class=\"body\">To see the effect that the <code class=\"fm-code-in-text\">ASPNETCORE_ENVIRONMENT<\/code> setting has on the overall configuration, change the value in the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file, as shown in listing 15.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.10 Changing the launchSettings.json file in the Platform\/Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http:\/\/localhost:5000\",\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"Platform\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"applicationUrl\": \"http:\/\/localhost:5000\",\n      \"environmentVariables\": {\n        <b class=\"fm-bold\">\"ASPNETCORE_ENVIRONMENT\": \"Production\"<\/b>\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<p class=\"body\">If you are using Visual Studio, you can change the environment variables by selecting Debug &gt; Launch Profiles. The settings for each launch profile are displayed, and there is support for changing the value of the <code class=\"fm-code-in-text\">ASPNETCORE_ENVIRONMENT<\/code> variable, as shown in figure 15.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre142\" src=\"\/images\/proaspnetcore7\/000141.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.7 Changing an environment variable using Visual Studio<\/p>\n<\/div>\n<p class=\"body\">If you are using Visual Studio Code, select Run &gt; Open Configurations and change the value in the <code class=\"fm-code-in-text\">env<\/code> section, as shown in listing 15.11.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.11 Changing the launch.json file in the Platform\/.vscode folder<\/p>\n<pre class=\"programlisting\">{\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \".NET Core Launch (web)\",\n            \"type\": \"coreclr\",\n            \"request\": \"launch\",\n            \"preLaunchTask\": \"build\",\n            \"program\": \"${workspaceFolder}\/bin\/Debug\/net7.0\/Platform.dll\",\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}\",\n            \"stopAtEntry\": false,\n            \"serverReadyAction\": {\n                \"action\": \"openExternally\",\n                \"pattern\": \"\\\\bNow listening on:\\\\s+(https?:\/\/\\\\S+)\"\n            },\n            \"env\": {\n                <b class=\"fm-bold\">\"ASPNETCORE_ENVIRONMENT\": \"Production\"<\/b>\n            },\n            \"sourceFileMap\": {\n                \"\/Views\": \"${workspaceFolder}\/Views\"\n            }\n        },\n        {\n            \"name\": \".NET Core Attach\",\n            \"type\": \"coreclr\",\n            \"request\": \"attach\"\n        }\n    ]\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1839\"><\/a>Save the changes to the property page or configuration file and restart ASP.NET Core. Navigate to http:\/\/localhost:5000\/config, and you will see the effect of the environment change, as shown in figure 15.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre141\" src=\"\/images\/proaspnetcore7\/000142.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.8 The effect of changing the environment configuration setting<\/p>\n<\/div>\n<p class=\"body\">Notice that both configuration values displayed in the browser have changed. The <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> file is no longer loaded, and there is no <code class=\"fm-code-in-text\">appsettings.Production.json<\/code> file in the project, so only the configuration settings in the <code class=\"fm-code-in-text\">appsettings.json<\/code> file are used.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-288\">15.2.6 Using the environment service<\/h3>\n<p class=\"body\">The ASP.NET Core platform provides the <code class=\"fm-code-in-text\">IWebHostEnvironment<\/code> service for determining the current environment, which avoids the need to get the configuration setting manually. The <code class=\"fm-code-in-text\">IWebHostEnvironment<\/code> service defines the property and methods shown in table 15.3. The methods are extension methods that are defined in the <code class=\"fm-code-in-text\">Microsoft.Extensions.Hosting<\/code> namespace.<a id=\"calibre_link-1840\"><\/a><a id=\"calibre_link-1841\"><\/a><a id=\"calibre_link-1842\"><\/a><a id=\"calibre_link-1843\"><\/a><a id=\"calibre_link-886\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 15.3 The IWebHostEnvironment extension methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1844\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">EnvironmentName<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the current environment.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsDevelopment()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns <code class=\"fm-code-in-text1\">true<\/code> when the <code class=\"fm-code-in-text1\">Development<\/code> environment has been selected.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsStaging()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns <code class=\"fm-code-in-text1\">true<\/code> when the <code class=\"fm-code-in-text1\">Staging<\/code> environment has been selected.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsProduction()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns <code class=\"fm-code-in-text1\">true<\/code> when the <code class=\"fm-code-in-text1\">Production<\/code> environment has been selected.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsEnvironment(env)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns <code class=\"fm-code-in-text1\">true<\/code> when the environment specified by the argument has been selected.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">If you need to access the environment when setting up services, then you can use the <code class=\"fm-code-in-text\">WebApplicationBuilder.Environment<\/code> property. If you need to access the environment when configuring the pipeline, you can use the <code class=\"fm-code-in-text\">WebApplication.Environment<\/code> property. If you need to access the environment within a middleware component or endpoint, then you can define a <code class=\"fm-code-in-text\">IWebHostEnvironment<\/code> parameter. All three approaches are shown in listing 15.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.12 Accessing the environment in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar servicesConfig = builder.Configuration;\nbuilder.Services.Configure&lt;MessageOptions&gt;(\n    servicesConfig.GetSection(\"Location\"));\n        \n<b class=\"fm-bold\">var servicesEnv = builder.Environment;<\/b>\n<b class=\"fm-bold\">\/\/ - use environment to set up services<\/b>\n\nvar app = builder.Build();\n\nvar pipelineConfig = app.Configuration;\n\/\/ - use configuration settings to set up pipeline\n\n<b class=\"fm-bold\">var pipelineEnv = app.Environment;<\/b>\n<b class=\"fm-bold\">\/\/ - use envirionment to set up pipeline<\/b>\n\napp.UseMiddleware&lt;LocationMiddleware&gt;();\n\n<b class=\"fm-bold\">app.MapGet(\"config\", async (HttpContext context,<\/b>\n        <b class=\"fm-bold\">IConfiguration config, IWebHostEnvironment env) =&gt; {<\/b>\n    string? defaultDebug = config[\"Logging:LogLevel:Default\"];\n    await context.Response\n        .WriteAsync($\"The config setting is: {defaultDebug}\");\n    <b class=\"fm-bold\">await context.Response<\/b>\n        <b class=\"fm-bold\">.WriteAsync($\"\\nThe env setting is: {env.EnvironmentName}\");<\/b>\n});\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/config, which produces the output shown in figure 15.8.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-289\">15.2.7 Storing user secrets<\/h3>\n<p class=\"body\">During development, it is often necessary to use sensitive data to work with the services that an application depends on. This data can include API keys, database connection passwords, or default administration accounts, and it is used both to access services and to reinitialize them to test application changes with a fresh database or user configuration.<\/p>\n<p class=\"body\">If the sensitive data is included in the C# classes or JSON configuration files, it will be checked into the source code version control repository and become visible to all developers and to anyone else who can see the code&mdash;which may mean visible to the world for projects that have open repositories or repositories that are poorly secured.<\/p>\n<p class=\"body\">The user secrets service allows sensitive data to be stored in a file that isn\u2019t part of the project and won\u2019t be checked into version control, allowing each developer to have sensitive data that won\u2019t be accidentally exposed through a version control check-in.<a id=\"calibre_link-1845\"><\/a><a id=\"calibre_link-1846\"><\/a><a id=\"calibre_link-889\"><\/a><\/p>\n<p class=\"fm-head2\">Storing user secrets<\/p>\n<p class=\"body\">The first step is to prepare the file that will be used to store sensitive data. Run the command shown in listing 15.13 in the <code class=\"fm-code-in-text\">Platform<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.13 Initializing user secrets<\/p>\n<pre class=\"programlisting\">dotnet user-secrets init<\/pre>\n<p class=\"body\">This command adds an element to the <code class=\"fm-code-in-text\">Platform.csproj<\/code> project file that contains a unique ID for the project that will be associated with the secrets on each developer machine. Next, run the commands shown in listing 15.14 in the <code class=\"fm-code-in-text\">Platform<\/code> folder.<a id=\"calibre_link-1847\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.14 Storing a user secret<\/p>\n<pre class=\"programlisting\">dotnet user-secrets set \"WebService:Id\" \"MyAccount\"\ndotnet user-secrets set \"WebService:Key\" \"MySecret123$\"<\/pre>\n<p class=\"body\">Each secret has a key and a value, and related secrets can be grouped together by using a common prefix, followed by a colon (the <code class=\"fm-code-in-text\">:<\/code> character), followed by the secret name. The commands in listing 15.14 create related <code class=\"fm-code-in-text\">Id<\/code> and <code class=\"fm-code-in-text\">Key<\/code> secrets that have the <code class=\"fm-code-in-text\">WebService<\/code> prefix.<\/p>\n<p class=\"body\">After each command, you will see a message confirming that a secret has been added to the secret store. To check the secrets for the project, use the command prompt to run the command shown in listing 15.15 in the <code class=\"fm-code-in-text\">Platform<\/code> folder.<a id=\"calibre_link-1848\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.15 Listing the user secrets<\/p>\n<pre class=\"programlisting\">dotnet user-secrets list<\/pre>\n<p class=\"body\">This command produces the following output:<\/p>\n<pre class=\"programlisting\">WebService:Key = MySecret123$\nWebService:Id = MyAccount<\/pre>\n<p class=\"body\">Behind the scenes, a JSON file has been created in the <code class=\"fm-code-in-text\">%APPDATA%\\Microsoft\\UserSecrets<\/code> folder (or the <code class=\"fm-code-in-text\">~\/.microsoft\/usersecrets<\/code> folder for Linux) to store the secrets. Each project has its own folder (whose name corresponds to the unique ID created by the <code class=\"fm-code-in-text\">init<\/code> command in listing 15.13).<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> If you are using Visual Studio, you can create and edit the JSON file directly by right-clicking the project in the Solution Explorer and selecting Manage User Secrets from the pop-up menu.<\/p>\n<p class=\"fm-head2\">Reading user secrets<\/p>\n<p class=\"body\">User secrets are merged with the normal configuration settings and accessed in the same way. In listing 15.16, I have added a statement that displays the secrets to the middleware component that handles the <code class=\"fm-code-in-text\">\/config<\/code> URL.<a id=\"calibre_link-1849\"><\/a><a id=\"calibre_link-888\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.16 Using user secrets in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar servicesConfig = builder.Configuration;\nbuilder.Services.Configure&lt;MessageOptions&gt;(\n    servicesConfig.GetSection(\"Location\"));\n        \nvar servicesEnv = builder.Environment;\n\/\/ - use environment to set up services\n\nvar app = builder.Build();\n\nvar pipelineConfig = app.Configuration;\n\/\/ - use configuration settings to set up pipeline\n\nvar pipelineEnv = app.Environment;\n\/\/ - use envirionment to set up pipeline\n\napp.UseMiddleware&lt;LocationMiddleware&gt;();\n\napp.MapGet(\"config\", async (HttpContext context,\n        IConfiguration config, IWebHostEnvironment env) =&gt; {\n    string? defaultDebug = config[\"Logging:LogLevel:Default\"];\n    await context.Response\n        .WriteAsync($\"The config setting is: {defaultDebug}\");\n    await context.Response\n        .WriteAsync($\"\\nThe env setting is: {env.EnvironmentName}\");\n    <b class=\"fm-bold\">string? wsID = config[\"WebService:Id\"];<\/b>\n    <b class=\"fm-bold\">string? wsKey = config[\"WebService:Key\"];<\/b>\n    <b class=\"fm-bold\">await context.Response.WriteAsync($\"\\nThe secret ID is: {wsID}\");<\/b>\n    <b class=\"fm-bold\">await context.Response.WriteAsync($\"\\nThe secret Key is: {wsKey}\");<\/b>\n});\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">User secrets are loaded only when the application is set to the <code class=\"fm-code-in-text\">Development<\/code> environment. Edit the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file to change the environment to <code class=\"fm-code-in-text\">Development<\/code>, as shown in listing 15.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.17 Changing the launchSettings.json file in the Platform\/Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http:\/\/localhost:5000\",\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"Platform\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"applicationUrl\": \"http:\/\/localhost:5000\",\n      \"environmentVariables\": {\n        <b class=\"fm-bold\">\"ASPNETCORE_ENVIRONMENT\": \"Development\"<\/b>\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<p class=\"body\">Save the changes, restart the ASP.NET Core runtime using the <code class=\"fm-code-in-text\">dotnet run<\/code> command, and request the http:\/\/localhost:5000\/config URL to see the user secrets, as shown in figure 15.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre143\" src=\"\/images\/proaspnetcore7\/000143.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.9 Displaying user secrets<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-290\">15.3 Using the logging service<\/h2>\n<p class=\"body\">ASP.NET Core provides a logging service that can be used to record messages that describe the state of the application to track errors, monitor performance, and help diagnose problems.<a id=\"calibre_link-1850\"><\/a><a id=\"calibre_link-1851\"><\/a><a id=\"calibre_link-1019\"><\/a><\/p>\n<p class=\"body\">Log messages are sent to logging providers, which are responsible for forwarding messages to where they can be seen, stored, and processed. There are built-in providers for basic logging, and there is a range of third-party providers available for feeding messages into logging frameworks that allow messages to be collated and analyzed.<a id=\"calibre_link-1852\"><\/a><\/p>\n<p class=\"body\">Three of the built-in providers are enabled by default: the console provider, the debug provider, and the <code class=\"fm-code-in-text\">EventSource<\/code> provider. The debug provider forwards messages so they can be processed through the <code class=\"fm-code-in-text\">System.Diagnostics.Debug<\/code> class, and the <code class=\"fm-code-in-text\">EventSource<\/code> provider forwards messages for event tracing tools, such as <code class=\"fm-code-in-text\">PerfView<\/code> (<a class=\"url\" href=\"https:\/\/github.com\/Microsoft\/perfview\">https:\/\/github.com\/Microsoft\/perfview<\/a>). I use the console provider in this chapter because it is simple and doesn\u2019t require any additional configuration to display logging messages.<a id=\"calibre_link-1853\"><\/a><a id=\"calibre_link-1854\"><\/a><a id=\"calibre_link-1855\"><\/a><a id=\"calibre_link-1856\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can see the list of providers available and instructions for enabling them at <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/logging\">https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/logging<\/a>.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-291\">15.3.1 Generating logging messages<\/h3>\n<p class=\"body\">To prepare for this section, listing 15.18 reconfigures the application to remove the services, middleware, and endpoints from the previous section.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.18 Configuring the application in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapGet(\"population\/{city?}\", Population.Endpoint);<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">Logging messages are generated using the unbounded <code class=\"fm-code-in-text\">ILogger&lt;&gt;<\/code> service, as shown in listing 15.19.<a id=\"calibre_link-1857\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.19 Generating logging messages in the Population.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n    public class Population {\n        \n        <b class=\"fm-bold\">public static async Task Endpoint(HttpContext context,<\/b>\n                <b class=\"fm-bold\">ILogger&lt;Population&gt; logger) {<\/b>\n            <b class=\"fm-bold\">logger.LogDebug(\"Started processing for {path}\",<\/b>\n                <b class=\"fm-bold\">context.Request.Path);<\/b>\n            string city = context.Request.RouteValues[\"city\"] \n                as string ?? \"london\";\n            int? pop = null;\n            switch (city.ToLower()) {\n                case \"london\":\n                    pop = 8_136_000;\n                    break;\n                case \"paris\":\n                    pop = 2_141_000;\n                    break;\n                case \"monaco\":\n                    pop = 39_000;\n                    break;\n            }\n            if (pop.HasValue) {\n                await context.Response\n                    .WriteAsync($\"City: {city}, Population: {pop}\");\n            } else {\n                context.Response.StatusCode \n                    = StatusCodes.Status404NotFound;\n            }\n            <b class=\"fm-bold\">logger.LogDebug(\"Finished processing for {path}\",<\/b> \n                <b class=\"fm-bold\">context.Request.Path);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The logging service groups log messages together based on the category assigned to messages. Log messages are written using the <code class=\"fm-code-in-text\">ILogger&lt;T&gt;<\/code> interface, where the generic parameter <code class=\"fm-code-in-text\">T<\/code> is used to specify the category. The convention is to use the type of the class that generates the messages as the category type, which is why listing 15.19 declares a dependency on the service using <code class=\"fm-code-in-text\">Population<\/code> for the type argument, like this:<\/p>\n<pre class=\"programlisting\">...\npublic static async Task Endpoint(HttpContext context,\n    <b class=\"fm-bold\">ILogger&lt;Population&gt;<\/b> logger) {\n...<\/pre>\n<p class=\"body\">This ensures that log messages generated by the <code class=\"fm-code-in-text\">Endpoint<\/code> method will be assigned the category <code class=\"fm-code-in-text\">Population<\/code>. Log messages are created using the extension methods shown in table 15.4.<a id=\"calibre_link-1858\"><\/a><a id=\"calibre_link-1020\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 15.4 The ILogger&lt;T&gt; extension methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1859\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">LogTrace<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method generates a <code class=\"fm-code-in-text1\">Trace<\/code>-level message, used for low-level debugging <code class=\"fm-code-in-text1\">during<\/code> development.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">LogDebug<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method generates a <code class=\"fm-code-in-text1\">Debug<\/code>-level message, used for low-level debugging during development or production problem resolution.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">LogInformation<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method generates an <code class=\"fm-code-in-text1\">Information<\/code>-level message, used to provide information about the general state of the application.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">LogWarning<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method generates a <code class=\"fm-code-in-text1\">Warning<\/code>-level message, used to record unexpected, <code class=\"fm-code-in-text1\">but<\/code> minor, problems that are unlikely to disrupt the application.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">LogError<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method generates an <code class=\"fm-code-in-text1\">Error<\/code>-level message, used to record exceptions or errors that are not handled by the application.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">LogCritical<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method generates a <code class=\"fm-code-in-text1\">Critical<\/code>-level message, used to record serious failures.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Log messages are assigned a level that reflects their importance and detail. The levels range from <code class=\"fm-code-in-text\">Trace<\/code>, for detailed diagnostics, to <code class=\"fm-code-in-text\">Critical<\/code>, for the most important information that requires an immediate response. There are overloaded versions of each method that allow log messages to be generated using strings or exceptions. In listing 15.19, I used the <code class=\"fm-code-in-text\">LogDebug<\/code> method to generate logging messages when a request is handled.<a id=\"calibre_link-1860\"><\/a><\/p>\n<pre class=\"programlisting\">...\nlogger.<b class=\"fm-bold\">LogDebug<\/b>(\"Started processing for {path}\", context.Request.Path);\n...<\/pre>\n<p class=\"body\">The result is log messages at the <code class=\"fm-code-in-text\">Debug<\/code> level that are generated when the response is started and completed. To see the log messages, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/population. Look at the console output, and you will see the log messages in the output from ASP.NET Core, like this:<\/p>\n<pre class=\"programlisting\">Building...\ninfo: Microsoft.Hosting.Lifetime[14]\n      Now listening on: http:\/\/localhost:5000\ninfo: Microsoft.Hosting.Lifetime[0]\n      Application started. Press Ctrl+C to shut down.\ninfo: Microsoft.Hosting.Lifetime[0]\n      Hosting environment: Development\ninfo: Microsoft.Hosting.Lifetime[0]\n      Content root path: C:\\Platform\n<b class=\"fm-bold\">dbug: Platform.Population[0]<\/b>\n      <b class=\"fm-bold\">Started processing for \/population<\/b>\n<b class=\"fm-bold\">dbug: Platform.Population[0]<\/b>\n      <b class=\"fm-bold\">Finished processing for \/population<\/b><\/pre>\n<p class=\"fm-head2\">Logging messages in the Program.cs file<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Logger&lt;&gt;<\/code> service is useful for logging in classes but isn\u2019t suited to logging in the <code class=\"fm-code-in-text\">Program.cs<\/code> file, where top-level statements are used to configure the application. The simplest approach is to use the <code class=\"fm-code-in-text\">ILogger<\/code> returned by the <code class=\"fm-code-in-text\">Logger<\/code> property defined by the <code class=\"fm-code-in-text\">WebApplication<\/code> class, as shown in listing 15.20.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.20 Logging in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.Logger.LogDebug(\"Pipeline configuration starting\");<\/b>\n\napp.MapGet(\"population\/{city?}\", Population.Endpoint);\n\n<b class=\"fm-bold\">app.Logger.LogDebug(\"Pipeline configuration complete\");<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ILogger<\/code> interface defines all the methods described in table 15.4. Start ASP.NET Core, and you will see the logging messages in the startup output, like this:<\/p>\n<pre class=\"programlisting\">Building...\n<b class=\"fm-bold\">dbug: Platform[0]<\/b>\n      <b class=\"fm-bold\">Pipeline configuration starting<\/b>\n<b class=\"fm-bold\">dbug: Platform[0]<\/b>\n      <b class=\"fm-bold\">Pipeline configuration complete<\/b>\ninfo: Microsoft.Hosting.Lifetime[14]\n      Now listening on: http:\/\/localhost:5000\ninfo: Microsoft.Hosting.Lifetime[0]\n      Application started. Press Ctrl+C to shut down.\ninfo: Microsoft.Hosting.Lifetime[0]\n      Hosting environment: Development\ninfo: Microsoft.Hosting.Lifetime[0]\n      Content root path: C:\\Platform<\/pre>\n<p class=\"body\">The category for logging messages generated using the <code class=\"fm-code-in-text\">ILogger<\/code> provided by the <code class=\"fm-code-in-text\">WebApplication<\/code> class is the name of the application, which is <code class=\"fm-code-in-text\">Platform<\/code> for this example. If you want to generate log messages with a different category, which can be useful in lambda functions, for example, then you can use the <code class=\"fm-code-in-text\">ILoggerFactory<\/code> interface, which is available as a service, and call the <code class=\"fm-code-in-text\">CreateLogger<\/code> method to obtain an <code class=\"fm-code-in-text\">ILogger<\/code> for a specified category, as shown in listing 15.21.<a id=\"calibre_link-1021\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.21 Creating a logger in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">var logger = app.Services<\/b>\n       <b class=\"fm-bold\">.GetRequiredService&lt;ILoggerFactory&gt;().CreateLogger(\"Pipeline\");<\/b>\n           \n<b class=\"fm-bold\">logger.LogDebug(\"Pipeline configuration starting\");<\/b>\n\napp.MapGet(\"population\/{city?}\", Population.Endpoint);\n\n<b class=\"fm-bold\">logger.LogDebug(\"Pipeline configuration complete\");<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">Restart ASP.NET Core, and you will see the following messages in the output produced as the application starts:<\/p>\n<pre class=\"programlisting\">Building...\n<b class=\"fm-bold\">dbug: Pipeline[0]<\/b>\n      <b class=\"fm-bold\">Pipeline configuration starting<\/b>\n<b class=\"fm-bold\">dbug: Pipeline[0]<\/b>\n      <b class=\"fm-bold\">Pipeline configuration complete<\/b>\ninfo: Microsoft.Hosting.Lifetime[14]\n      Now listening on: http:\/\/localhost:5000\ninfo: Microsoft.Hosting.Lifetime[0]\n      Application started. Press Ctrl+C to shut down.\ninfo: Microsoft.Hosting.Lifetime[0]\n      Hosting environment: Development\ninfo: Microsoft.Hosting.Lifetime[0]\n      Content root path: C:\\Platform<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-292\">15.3.2 Logging messages with attributes<\/h3>\n<p class=\"body\">An alternative approach to generating log messages is to use the <code class=\"fm-code-in-text\">LoggerMessage<\/code> attribute, as shown in listing 15.22.<a id=\"calibre_link-1861\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.22 Using the attribute in the Population.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n    <b class=\"fm-bold\">public partial class Population {<\/b>\n        \n        public static async Task Endpoint(HttpContext context,\n                ILogger&lt;Population&gt; logger) {\n            <b class=\"fm-bold\">\/\/logger.LogDebug(\"Started processing for {path}\",<\/b>\n            <b class=\"fm-bold\">\/\/    context.Request.Path);<\/b>\n            <b class=\"fm-bold\">StartingResponse(logger, context.Request.Path);<\/b>\n            string city = context.Request.RouteValues[\"city\"] \n                as string ?? \"london\";\n            int? pop = null;\n            switch (city.ToLower()) {\n                case \"london\":\n                    pop = 8_136_000;\n                    break;\n                case \"paris\":\n                    pop = 2_141_000;\n                    break;\n                case \"monaco\":\n                    pop = 39_000;\n                    break;\n            }\n            if (pop.HasValue) {\n                await context.Response\n                    .WriteAsync($\"City: {city}, Population: {pop}\");\n            } else {\n                context.Response.StatusCode \n                    = StatusCodes.Status404NotFound;\n            }\n            logger.LogDebug(\"Finished processing for {path}\", \n                context.Request.Path);\n        }\n                \n        <b class=\"fm-bold\">[LoggerMessage(0, LogLevel.Debug, \"Starting response for {path}\")]<\/b>\n        <b class=\"fm-bold\">public static partial void StartingResponse(ILogger logger,<\/b> \n            <b class=\"fm-bold\">string path);<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">LoggerMessage<\/code> attribute is applied to <code class=\"fm-code-in-text\">partial<\/code> methods, which must be defined in <code class=\"fm-code-in-text\">partial<\/code> classes. When the application is compiled, the attribute generates the implementation for the method to which it is applied, resulting in logging, which Microsoft says offers better performance than the other techniques described in this section. Full details of how this feature works can be found at <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/extensions\/logger-message-generator\">https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/extensions\/logger-message-generator<\/a>.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> I do not doubt Microsoft\u2019s assertion that using the <code class=\"fm-code-in-text1\">LoggerMessage<\/code> attribute is faster, but I doubt it matters for most projects. Use the attribute if you find this approach easier to understand and maintain, but don\u2019t rush to adopt it just for the sake of a performance gain unless you have an application that doesn\u2019t meet its performance goals and you are sure that logging performance is contributing to the problem. I am confident that this will never be the case for most projects because of the nature of most web applications, but please get in touch if you find yourself in this position because I am always willing to have my assumptions proven wrong.<\/p>\n<p class=\"body\">Start ASP.NET Core and use a browser to request http:\/\/localhost:5000\/population, and the output will include the following log messages:<\/p>\n<pre class=\"programlisting\">dbug: Platform.Population[0]\n      Starting response for \/population\ndbug: Platform.Population[0]\n      Finished processing for \/population<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-293\">15.3.3 Configuring minimum logging levels<\/h3>\n<p class=\"body\">Earlier in this chapter, I showed you the default contents of the <code class=\"fm-code-in-text\">appsettings.json<\/code> and <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> files and explained how they are merged to create the application\u2019s configuration settings. The settings in the JSON file are used to configure the logging service, which ASP.NET Core provides to record messages about the state of the application.<a id=\"calibre_link-1022\"><\/a><\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Logging:LogLevel<\/code> section of the <code class=\"fm-code-in-text\">appsettings.json<\/code> file is used to set the minimum level for logging messages. Log messages that are below the minimum level are discarded. The <code class=\"fm-code-in-text\">appsettings.json<\/code> file contains the following levels:<a id=\"calibre_link-1862\"><\/a><\/p>\n<pre class=\"programlisting\">...\n\"Default\": \"Information\",\n\"Microsoft.AspNetCore\": \"Warning\"\n...<\/pre>\n<p class=\"body\">The category for the log messages&mdash;which is set using the generic type argument or using a string&mdash;is used to select a minimum filter level.<\/p>\n<p class=\"body\">For the log messages generated by the <code class=\"fm-code-in-text\">Population<\/code> class, for example, the category will be <code class=\"fm-code-in-text\">Platform.Population<\/code>, which means that they can be matched directly by adding a <code class=\"fm-code-in-text\">Platform.Population<\/code> entry to the <code class=\"fm-code-in-text\">appsettings.json<\/code> file or indirectly by specifying just the <code class=\"fm-code-in-text\">Platform<\/code> namespace. Any category for which there is no minimum log level is matched by the <code class=\"fm-code-in-text\">Default<\/code> entry, which is set to <code class=\"fm-code-in-text\">Information<\/code>.<\/p>\n<p class=\"body\">It is common to increase the detail of the log messages displayed during development, which is why the levels in the <code class=\"fm-code-in-text\">appsettings.Development.json<\/code> file specify more detailed logging levels, like this:<\/p>\n<pre class=\"programlisting\">...\n\"Default\": \"Debug\",\n\"System\": \"Information\",\n\"Microsoft\": \"Information\"\n...<\/pre>\n<p class=\"body\">When the application is configured for the <code class=\"fm-code-in-text\">Development<\/code> environment, the default logging level is <code class=\"fm-code-in-text\">Debug<\/code>. The levels for the <code class=\"fm-code-in-text\">System<\/code> and <code class=\"fm-code-in-text\">Microsoft<\/code> categories are set to <code class=\"fm-code-in-text\">Information<\/code>, which affects the logging messages generated by ASP.NET Core and the other packages and frameworks provided by Microsoft.<\/p>\n<p class=\"body\">You can tailor the logging levels to focus the log on those parts of the application that are of interest by setting a level to <code class=\"fm-code-in-text\">Trace<\/code>, <code class=\"fm-code-in-text\">Debug<\/code>, <code class=\"fm-code-in-text\">Information<\/code>, <code class=\"fm-code-in-text\">Warning<\/code>, <code class=\"fm-code-in-text\">Error<\/code>, or <code class=\"fm-code-in-text\">Critical<\/code>. Logging messages can be disabled for a category using the <code class=\"fm-code-in-text\">None<\/code> value.<\/p>\n<p class=\"body\">Listing 15.23 sets the level to <code class=\"fm-code-in-text\">Debug<\/code> for the <code class=\"fm-code-in-text\">Microsoft.AspNetCore<\/code> setting, which will increase the default level of detail and will have the effect of displaying debug-level messages generated by ASP.NET Core.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> If you are using Visual Studio, you may have to expand the <code class=\"fm-code-in-text1\">appsettings.json<\/code> item in the Solution Explorer to see the <code class=\"fm-code-in-text1\">appsettings.Development.json<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.23 Configuring the appsettings.Development.json file in the Platform folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Debug\",\n      \"System\": \"Information\",\n      \"Microsoft\": \"Information\",\n      <b class=\"fm-bold\">\"Microsoft.AspNetCore\": \"Debug\"<\/b>\n    }\n  }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request the http:\/\/localhost:5000\/population URL, and you will see a series of messages from the different ASP.NET Core components. You can reduce the detail by being more specific about the namespace for which messages are required, as shown in listing 15.24.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.24 Configuring the appsettings.Development.json file in the Platform folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Debug\",\n      \"System\": \"Information\",\n      \"Microsoft\": \"Information\",\n      <b class=\"fm-bold\">\"Microsoft.AspNetCore\": \"Warning\",<\/b>\n      <b class=\"fm-bold\">\"Microsoft.AspNetCore.Routing\": \"Debug\"<\/b>\n    }\n  }\n}<\/pre>\n<p class=\"body\">The changes return the <code class=\"fm-code-in-text\">Microsoft.AspNetCore<\/code> category to <code class=\"fm-code-in-text\">Warning<\/code> and set the <code class=\"fm-code-in-text\">Microsoft.AspNetCore.Routing<\/code> category to <code class=\"fm-code-in-text\">Debug<\/code>, which increases the detail level for logging messages by the components responsible for routing. Restart ASP.NET Core and request http:\/\/localhost:5000\/population again, and you will see fewer messages overall, but still see those that report how the request was matched to a route:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]<\/b>\n      <b class=\"fm-bold\">1 candidate(s) found for the request path<\/b> <b class=\"fm-bold\">'\/population'<\/b>\n<b class=\"fm-bold\">dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1005]<\/b>\n      <b class=\"fm-bold\">Endpoint 'HTTP: GET population\/{city?} =&gt;         Endpoint' with route pattern 'population\/{city?}' is valid         for the request path '\/population'<\/b>\n<b class=\"fm-bold\">dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]<\/b>\n      <b class=\"fm-bold\">Request matched endpoint 'HTTP: GET population\/{city?} =&gt; Endpoint'<\/b>\n<b class=\"fm-bold\">info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]<\/b>\n      <b class=\"fm-bold\">Executing endpoint 'HTTP: GET population\/{city?} =&gt; Endpoint'<\/b>\ndbug: Platform.Population[0]\n      Starting response for \/population\ndbug: Platform.Population[0]\n      Finished processing for \/population\n<b class=\"fm-bold\">info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]<\/b>\n      <b class=\"fm-bold\">Executed endpoint 'HTTP: GET population\/{city?} =&gt; Endpoint'<\/b>\n...<\/pre>\n<p class=\"body\">If you are having trouble figuring out a routing scheme, then these messages can be helpful in figuring out what the application is doing with requests.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-294\">15.3.4 Logging HTTP requests and responses<\/h3>\n<p class=\"body\">ASP.NET Core includes built-in middleware for generating log messages that describe the HTTP requests received by an application and the responses it produces. Listing 15.25 adds the HTTP logging middleware to the request pipeline.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.25 Adding logging middleware in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.UseHttpLogging();<\/b>\n\n<b class=\"fm-bold\">\/\/var logger = app.Services<\/b>\n<b class=\"fm-bold\">\/\/  .GetRequiredService&lt;ILoggerFactory&gt;().CreateLogger(\"Pipeline\");<\/b>\n\n<b class=\"fm-bold\">\/\/logger.LogDebug(\"Pipeline configuration starting\");<\/b>\n\napp.MapGet(\"population\/{city?}\", Population.Endpoint);\n\n<b class=\"fm-bold\">\/\/logger.LogDebug(\"Pipeline configuration complete\");<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UseHttpLogging<\/code> method adds a middleware component that generates logging messages that describe the HTTP requests and responses. These log messages are generated with the <code class=\"fm-code-in-text\">Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware<\/code> category and the <code class=\"fm-code-in-text\">Information<\/code> severity, which I have enabled in listing 15.26.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.26 Logging in the appsettings.Development.json file in the Platform folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Debug\",\n      \"System\": \"Information\",\n      \"Microsoft\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\",\n      <b class=\"fm-bold\">\"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware\":<\/b> \n          <b class=\"fm-bold\">\"Information\"<\/b>\n    }\n  }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/population, and you will see logging messages that describe the HTTP request sent by the browser and the response the application produces, similar to the following:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]<\/b>\n      <b class=\"fm-bold\">Request:<\/b>\n      <b class=\"fm-bold\">Protocol: HTTP\/1.1<\/b>\n      <b class=\"fm-bold\">Method: GET<\/b>\n      <b class=\"fm-bold\">Scheme: http<\/b>\n      <b class=\"fm-bold\">PathBase:<\/b>\n      <b class=\"fm-bold\">Path: \/population<\/b>\n      <b class=\"fm-bold\">Accept: text\/html,application\/xhtml+xml,application\/xml;q=0.9,\n          image\/avif,image\/webp,image\/apng,*\/*;q=0.8,<\/b>\n          <b class=\"fm-bold\">application\/signed-exchange;v=b3;q=0.9<\/b>\n      <b class=\"fm-bold\">Connection: keep-alive<\/b>\n      <b class=\"fm-bold\">Host: localhost:5000<\/b>\n      <b class=\"fm-bold\">User-Agent: Mozilla\/5.0 (Windows NT 10.0; Win64; x64)\n          AppleWebKit\/537.36 (KHTML, like Gecko)\n          Chrome\/94.0.4606.71 Safari\/537.36<\/b>\n      <b class=\"fm-bold\">Accept-Encoding: gzip, deflate, br<\/b>\n      <b class=\"fm-bold\">Accept-Language: en-GB,en-US;q=0.9,en;q=0.8<\/b>\n      <b class=\"fm-bold\">Cache-Control: [Redacted]<\/b>\n      <b class=\"fm-bold\">Cookie: [Redacted]<\/b>\n      <b class=\"fm-bold\">Upgrade-Insecure-Requests: [Redacted]<\/b>\n      <b class=\"fm-bold\">sec-ch-ua: [Redacted]<\/b>\n      <b class=\"fm-bold\">sec-ch-ua-mobile: [Redacted]<\/b>\n      <b class=\"fm-bold\">sec-ch-ua-platform: [Redacted]<\/b>\n      <b class=\"fm-bold\">Sec-Fetch-Site: [Redacted]<\/b>\n      <b class=\"fm-bold\">Sec-Fetch-Mode: [Redacted]<\/b>\n      <b class=\"fm-bold\">Sec-Fetch-User: [Redacted]<\/b>\n      <b class=\"fm-bold\">Sec-Fetch-Dest: [Redacted]<\/b>\ndbug: Platform.Population[0]\n      Starting response for \/population\ndbug: Platform.Population[0]\n      Finished processing for \/population\n<b class=\"fm-bold\">info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]<\/b>\n      <b class=\"fm-bold\">Response:<\/b>\n      <b class=\"fm-bold\">StatusCode: 200<\/b>\n      <b class=\"fm-bold\">Date: [Redacted]<\/b>\n      <b class=\"fm-bold\">Server: [Redacted]<\/b>\n      <b class=\"fm-bold\">Transfer-Encoding: chunked<\/b>\n...<\/pre>\n<p class=\"body\">The details of the HTTP request and response logging messages can be configured using the <code class=\"fm-code-in-text\">AddHttpLogging<\/code> method, as shown in listing 15.27.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.27 HTTP logging messages in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.HttpLogging;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddHttpLogging(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.LoggingFields = HttpLoggingFields.RequestMethod<\/b>\n        <b class=\"fm-bold\">| HttpLoggingFields.RequestPath<\/b> \n        <b class=\"fm-bold\">| HttpLoggingFields.ResponseStatusCode;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseHttpLogging();\n\napp.MapGet(\"population\/{city?}\", Population.Endpoint);\n\napp.Run();<\/pre>\n<p class=\"body\">This method selects the fields and headers that are included in the logging message. The configuration in listing 15.27 selects the method and path from the HTTP request and the status code from the response. See <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/http-logging\">https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/http-logging<\/a> for the complete set of configuration options for HTTP logging.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/population, and you will see the selected details in the output:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]<\/b>\n      <b class=\"fm-bold\">Request:<\/b>\n      <b class=\"fm-bold\">Method: GET<\/b>\n      <b class=\"fm-bold\">PathBase:<\/b>\n      <b class=\"fm-bold\">Path: \/population<\/b>\ndbug: Platform.Population[0]\n      Starting response for \/population\ndbug: Platform.Population[0]\n      Finished processing for \/population\n<b class=\"fm-bold\">info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]<\/b>\n      <b class=\"fm-bold\">Response:<\/b>\n      <b class=\"fm-bold\">StatusCode: 200<\/b>\n...<\/pre>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> ASP.NET Core also provides middleware that will generate log messages in the W3C format. See <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/w3c-logger\">https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/w3c-logger<\/a> for details.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-295\">15.4 Using static content and client-side packages<\/h2>\n<p class=\"body\">Most web applications rely on a mix of dynamically generated and static content. The dynamic content is generated by the application based on the user\u2019s identity and actions, such as the contents of a shopping cart or the detail of a specific product and is generated fresh for each request. I describe the different ways that dynamic content can be created using ASP.NET Core in part 3.<a id=\"calibre_link-1863\"><\/a><a id=\"calibre_link-1864\"><\/a><a id=\"calibre_link-1865\"><\/a><a id=\"calibre_link-1866\"><\/a><a id=\"calibre_link-1867\"><\/a><a id=\"calibre_link-867\"><\/a><\/p>\n<p class=\"body\">Static content doesn\u2019t change and is used to provide images, CSS stylesheets, JavaScript files, and anything else on which the application relies but which doesn\u2019t have to be generated for every request. The conventional location for static content in an ASP.NET Core project is the <code class=\"fm-code-in-text\">wwwroot<\/code> folder.<\/p>\n<p class=\"body\">To prepare static content to use in the examples for this section, create the <code class=\"fm-code-in-text\">Platform\/wwwroot<\/code> folder and add to it a file called <code class=\"fm-code-in-text\">static.xhtml<\/code>, with the content shown in listing 15.28. You can create the file with the HTML Page template if you are using Visual Studio.<a id=\"calibre_link-1868\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.28 The contents of the static.xhtml file in the wwwroot folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n    &lt;title&gt;Static Content&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h3&gt;This is static content&lt;\/h3&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The file contains a basic HTML document with just the basic elements required to display a message in the browser.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-296\">15.4.1 Adding the static content middleware<\/h3>\n<p class=\"body\">ASP.NET Core provides a middleware component that handles requests for static content, which is added to the request pipeline in listing 15.29.<a id=\"calibre_link-1869\"><\/a><a id=\"calibre_link-1870\"><\/a><a id=\"calibre_link-1034\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.29 Adding middleware in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Microsoft.AspNetCore.HttpLogging;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddHttpLogging(opts =&gt; {\n    opts.LoggingFields = HttpLoggingFields.RequestMethod\n        | HttpLoggingFields.RequestPath \n        | HttpLoggingFields.ResponseStatusCode;\n});\n\nvar app = builder.Build();\n\napp.UseHttpLogging();\n\n<b class=\"fm-bold\">app.UseStaticFiles();<\/b>\n\napp.MapGet(\"population\/{city?}\", Population.Endpoint);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UseStaticFiles<\/code> extension method adds the static file middleware to the request pipeline. This middleware responds to requests that correspond to the names of disk files and passes on all other requests to the next component in the pipeline. This middleware is usually added close to the start of the request pipeline so that other components don\u2019t handle requests that are for static files.<\/p>\n<p class=\"body\">Restart ASP.NET Core and navigate to http:\/\/localhost:5000\/static.xhtml. The static file middleware will receive the request and respond with the contents of the <code class=\"fm-code-in-text\">static.xhtml<\/code> file in the <code class=\"fm-code-in-text\">wwwroot<\/code> folder, as shown in figure 15.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre144\" src=\"\/images\/proaspnetcore7\/000144.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.10 Serving static content<\/p>\n<\/div>\n<p class=\"body\">The middleware component returns the content of the requested file and sets the response headers, such as <code class=\"fm-code-in-text\">Content-Type<\/code> and <code class=\"fm-code-in-text\">Content-Length<\/code>, that describe the content to the browser.<\/p>\n<p class=\"fm-head2\">Changing the default options for the static content middleware<\/p>\n<p class=\"body\">When the <code class=\"fm-code-in-text\">UseStaticFiles<\/code> method is invoked without arguments, the middleware will use the <code class=\"fm-code-in-text\">wwwroot<\/code> folder to locate files that match the path of the requested URL.<\/p>\n<p class=\"body\">This behavior can be adjusted by passing a <code class=\"fm-code-in-text\">StaticFileOptions<\/code> object to the <code class=\"fm-code-in-text\">UseStaticFiles<\/code> method. Table 15.5 describes the properties defined by the <code class=\"fm-code-in-text\">StaticFileOptions<\/code> class.<a id=\"calibre_link-1871\"><\/a><a id=\"calibre_link-1170\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 15.5 The properties defined by the StaticFileOptions class<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1872\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ContentTypeProvider<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to get or set the <code class=\"fm-code-in-text1\">IContentTypeProvider<\/code> object that is responsible for producing the MIME type for a file. The default implementation of the interface uses the file extension to determine the content type and supports the most common file types.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">DefaultContentType<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to set the default content type if the <code class=\"fm-code-in-text1\">IContentTypeProvider<\/code> cannot determine the type of the file.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">FileProvider<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to locate the content for requests, as shown in the listing below.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnPrepareResponse<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property can be used to register an action that will be invoked before the static content response is generated.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RequestPath<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to specify the URL path that the middleware will respond to, as shown in the following listing.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ServeUnknownFileTypes<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">By default, the static content middleware will not serve files whose content type cannot be determined by the <code class=\"fm-code-in-text1\">IContentTypeProvider<\/code>. This behavior is changed by setting this property to <code class=\"fm-code-in-text1\">true<\/code>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">FileProvider<\/code> and <code class=\"fm-code-in-text\">RequestPath<\/code> properties are the most commonly used. The <code class=\"fm-code-in-text\">FileProvider<\/code> property is used to select a different location for static content, and the <code class=\"fm-code-in-text\">RequestPath<\/code> property is used to specify a URL prefix that denotes requests for static context. Listing 15.30 uses both properties to configure the static file middleware.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> There is also a version of the <code class=\"fm-code-in-text1\">UseStaticFiles<\/code> method that accepts a single string argument, which is used to set the <code class=\"fm-code-in-text1\">RequestPath<\/code> configuration property. This is a convenient way of adding support for URLs without needing to create an options object.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.30 Configuring the static files in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform;\nusing Microsoft.AspNetCore.HttpLogging;\n<b class=\"fm-bold\">using Microsoft.Extensions.FileProviders;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddHttpLogging(opts =&gt; {\n    opts.LoggingFields = HttpLoggingFields.RequestMethod\n        | HttpLoggingFields.RequestPath \n        | HttpLoggingFields.ResponseStatusCode;\n});\n\nvar app = builder.Build();\n\napp.UseHttpLogging();\n\napp.UseStaticFiles();\n\n<b class=\"fm-bold\">var env = app.Environment;<\/b>\n<b class=\"fm-bold\">app.UseStaticFiles(new StaticFileOptions {<\/b>\n    <b class=\"fm-bold\">FileProvider = new<\/b>\n        <b class=\"fm-bold\">PhysicalFileProvider($\"{env.ContentRootPath}\/staticfiles\"),<\/b>\n    <b class=\"fm-bold\">RequestPath = \"\/files\"<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.MapGet(\"population\/{city?}\", Population.Endpoint);\n\napp.Run();<\/pre>\n<p class=\"body\">Multiple instances of the middleware component can be added to the pipeline, each of which handles a separate mapping between URLs and file locations. In the listing, a second instance of the static files middleware is added to the request pipeline so that requests for URLs that start with <code class=\"fm-code-in-text\">\/files<\/code> will be handled using files from a folder named <code class=\"fm-code-in-text\">staticfiles<\/code>. Reading files from the folder is done with an instance of the <code class=\"fm-code-in-text\">PhysicalFileProvider<\/code> class, which is responsible for reading disk files. The <code class=\"fm-code-in-text\">PhysicalFileProvider<\/code> class requires an absolute path to work with, which I based on the value of the <code class=\"fm-code-in-text\">ContentRootPath<\/code> property defined by the <code class=\"fm-code-in-text\">IWebHostEnvironment<\/code> interface, which is the same interface used to determine whether the application is running in the <code class=\"fm-code-in-text\">Development<\/code> or <code class=\"fm-code-in-text\">Production<\/code> environment.<\/p>\n<p class=\"body\">To provide content for the new middleware component to use, create the <code class=\"fm-code-in-text\">Platform\/staticfiles<\/code> folder and add to it an HTML file named <code class=\"fm-code-in-text\">hello.xhtml<\/code> with the content shown in listing 15.31.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.31 The contents of the hello.xhtml file in the Platform\/staticfiles folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n    &lt;title&gt;Static Content&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h3&gt;This is additional static content&lt;\/h3&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use the browser to request the http:\/\/localhost:5000\/files\/hello.xhtml URL. Requests for URLs that begin with <code class=\"fm-code-in-text\">\/files<\/code> and that correspond to files in the <code class=\"fm-code-in-text\">staticfiles<\/code> folder are handled by the new middleware, as shown in figure 15.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre144\" src=\"\/images\/proaspnetcore7\/000145.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.11 Configuring the static files middleware<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-297\">15.4.2 Using client-side packages<\/h3>\n<p class=\"body\">Most web applications rely on client-side packages to support the content they generate, using CSS frameworks to style content or JavaScript packages to create rich functionality in the browser. Microsoft provides the Library Manager tool, known as LibMan, for downloading and managing client-side packages.<a id=\"calibre_link-1873\"><\/a><a id=\"calibre_link-1169\"><\/a><\/p>\n<p class=\"fm-head2\">Preparing the project for client-side packages<\/p>\n<p class=\"body\">Use the command prompt to run the commands shown in listing 15.32, which remove any existing LibMan package and install the version required by this chapter as a global .NET Core tool.<a id=\"calibre_link-1874\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.32 Installing LibMan<\/p>\n<pre class=\"programlisting\">dotnet tool uninstall --global Microsoft.Web.LibraryManager.Cli\ndotnet tool install --global Microsoft.Web.LibraryManager.Cli\n    --version 2.1.175 <\/pre>\n<p class=\"body\">The next step is to create the LibMan configuration file, which specifies the repository that will be used to get client-side packages and the directory into which packages will be downloaded. Open a PowerShell command prompt and run the command shown in listing 15.33 in the <code class=\"fm-code-in-text\">Platform<\/code> folder.<a id=\"calibre_link-1875\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.33 Initializing LibMan<\/p>\n<pre class=\"programlisting\">libman init -p cdnjs<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">-p<\/code> argument specifies the provider that will get packages. I have used <code class=\"fm-code-in-text\">cdnjs<\/code>, which selects <code class=\"fm-code-in-text\">cdnjs.com<\/code>. The other option is <code class=\"fm-code-in-text\">unpkg<\/code>, which selects <code class=\"fm-code-in-text\">unpkg.com<\/code>. If you don\u2019t have existing experience with package repositories, then you should start with the <code class=\"fm-code-in-text\">cdnjs<\/code> option.<\/p>\n<p class=\"body\">The command in listing 15.33 creates a file named <code class=\"fm-code-in-text\">libman.json<\/code> in the <code class=\"fm-code-in-text\">Platform<\/code> folder; the file contains the following settings:<\/p>\n<pre class=\"programlisting\">...\n{\n  \"version\": \"1.0\",\n  \"defaultProvider\": \"cdnjs\",\n  \"libraries\": []\n}\n...<\/pre>\n<p class=\"body\">If you are using Visual Studio, you can create and edit the <code class=\"fm-code-in-text\">libman.json<\/code> file directly by selecting Project &gt; Manage Client-Side Libraries.<\/p>\n<p class=\"fm-head2\">Installing client-side packages<\/p>\n<p class=\"body\">Packages are installed from the command line. Run the command shown in listing 15.34 in the <code class=\"fm-code-in-text\">Platform<\/code> folder to install the Bootstrap package.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.34 Installing the Bootstrap package<\/p>\n<pre class=\"programlisting\">libman install bootstrap@5.2.3 -d wwwroot\/lib\/bootstrap<\/pre>\n<p class=\"body\">The required version is separated from the package name by the <code class=\"fm-code-in-text\">@<\/code> character, and the <code class=\"fm-code-in-text\">-d<\/code> argument is used to specify where the package will be installed. The <code class=\"fm-code-in-text\">wwwroot\/lib<\/code> folder is the conventional location for installing client-side packages in ASP.NET Core projects.<\/p>\n<p class=\"fm-head2\">Using a client-side package<\/p>\n<p class=\"body\">Once a client-side package has been installed, its files can be referenced by <code class=\"fm-code-in-text\">script<\/code> or <code class=\"fm-code-in-text\">link<\/code> HTML elements or by using the features provided by the higher-level ASP.NET Core features described in later chapters.<\/p>\n<p class=\"body\">For simplicity in this chapter, listing 15.35 adds a <code class=\"fm-code-in-text\">link<\/code> element to the static HTML file created earlier in this section.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 15.35 Using a client package in the static.xhtml file in the Platform\/wwwroot folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n    <b class=\"fm-bold\">&lt;link rel=\"stylesheet\" href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" \/&gt;<\/b>\n    &lt;title&gt;Static Content&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    <b class=\"fm-bold\">&lt;h3 class=\"p-2 bg-primary text-white\"&gt;This is static content&lt;\/h3&gt;<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/static.xhtml. When the browser receives and processes the contents of the <code class=\"fm-code-in-text\">static.xhtml<\/code> file, it will encounter the <code class=\"fm-code-in-text\">link<\/code> element and send an HTTP request to the ASP.NET Core runtime for the <code class=\"fm-code-in-text\">\/lib\/bootstrap\/css\/bootstrap.min.css<\/code> URL. The original static file middleware component will receive this request, determine that it corresponds to a file in the <code class=\"fm-code-in-text\">wwwroot<\/code> folder, and return its contents, providing the browser with the Bootstrap CSS stylesheet. The Bootstrap styles are applied through the classes to which the <code class=\"fm-code-in-text\">h3<\/code> element has been assigned, producing the result shown in figure 15.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre145\" src=\"\/images\/proaspnetcore7\/000146.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 15.12 Using a client-side package<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-298\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The ASP.NET Core platform includes features for common tasks, must of which are presented as services.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The configuration service provides access to the application configuration, which includes the contents of the <code class=\"fm-code-in-text\">appsettings.json<\/code> file and environment variables.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The configuration data is typically used with the options service to configure the services available through dependency injection.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The user secrets feature is used to store sensitive data outside of the project folder, so they are not committed into a version control code repository.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The logging service is used to generate log messages, with different severity levels and with options for sending the log messages to different handlers.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core includes middleware for serving static content and a tool for adding packages that will be delivered as static content to the project.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-299\">\n<div class=\"calibre1\" id=\"calibre_link-1876\">\n<h1 class=\"tochead\" id=\"calibre_link-1877\"><a id=\"calibre_link-1878\"><\/a><a id=\"calibre_link-1879\"><\/a>16 Using the platform features, part 2<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-1880\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using cookies to store data that will be presented in subsequent requests<\/li>\n<li class=\"co-summary-bullet\">Using sessions to identify related requests and store associated data<\/li>\n<li class=\"co-summary-bullet\">Working with HTTPS requests<\/li>\n<li class=\"co-summary-bullet\">Limiting the rate of requests processed by endpoints<\/li>\n<li class=\"co-summary-bullet\">Responding to exceptions and errors<\/li>\n<li class=\"co-summary-bullet\">Filtering requests based on the host header<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I continue to describe the basic features provided by the ASP.NET Core platform. I explain how cookies are used and how the user\u2019s consent for tracking cookies is managed. I describe how sessions provide a robust alternative to basic cookies, how to use and enforce HTTPS requests, how to deal with errors, and how to filter requests based on the <code class=\"fm-code-in-text\">Host<\/code> header. Table 16.1 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 16.1 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1881\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using cookies<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the context objects to read and write cookies.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">1&ndash;3<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Managing cookie consent<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the consent middleware.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">4&ndash;6<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Storing data across requests<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use sessions.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">7, 8<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Securing HTTP requests<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the HTTPS middleware.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">9&ndash;13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Restrict the number of requests handled by the application<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the rate limiting middleware<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Handling errors<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the error and status code middleware.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">15&ndash;20<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Restricting a request with the host header<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Set the <code class=\"fm-code-in-text1\">AllowedHosts<\/code> configuration setting.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">21<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-300\">16.1 Preparing for this chapter<\/h2>\n<p class=\"body\">In this chapter, I continue to use the <code class=\"fm-code-in-text\">Platform<\/code> project from chapter 15. To prepare for this chapter, replace the contents of the <code class=\"fm-code-in-text\">Program.cs<\/code> file with the contents of listing 16.1, which removes the middleware and services from the previous chapter.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/proasp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.1 Replacing the contents of the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.MapFallback(async context =&gt; \n    await context.Response.WriteAsync(\"Hello World!\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">Start the application by opening a new PowerShell command prompt, navigating to the folder that contains the <code class=\"fm-code-in-text\">Platform.csproj<\/code> file, and running the command shown in listing 16.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.2 Starting the ASP.NET Core runtime<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Open a new browser window and use it to request http:\/\/localhost:5000, which will produce the response shown in figure 16.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre136\" src=\"\/images\/proaspnetcore7\/000147.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-301\">16.2 Using cookies<\/h2>\n<p class=\"body\">Cookies are small amounts of text added to responses that the browser includes in subsequent requests. Cookies are important for web applications because they allow features to be developed that span a series of HTTP requests, each of which can be identified by the cookies that the browser sends to the server.<\/p>\n<p class=\"body\">ASP.NET Core provides support for working with cookies through the <code class=\"fm-code-in-text\">HttpRequest<\/code> and <code class=\"fm-code-in-text\">HttpResponse<\/code> objects that are provided to middleware components. To demonstrate, listing 16.3 changes the routing configuration in the example application to add endpoints that implement a counter.<a id=\"calibre_link-1882\"><\/a><a id=\"calibre_link-1883\"><\/a><a id=\"calibre_link-1884\"><\/a><a id=\"calibre_link-893\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.3 Using cookies in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapGet(\"\/cookie\", async context =&gt; {<\/b>\n    <b class=\"fm-bold\">int counter1 =<\/b>\n        <b class=\"fm-bold\">int.Parse(context.Request.Cookies[\"counter1\"] ?? \"0\") + 1;<\/b>\n    <b class=\"fm-bold\">context.Response.Cookies.Append(\"counter1\", counter1.ToString(),<\/b>\n        <b class=\"fm-bold\">new CookieOptions {<\/b>\n            <b class=\"fm-bold\">MaxAge = TimeSpan.FromMinutes(30)<\/b>\n        <b class=\"fm-bold\">});<\/b>\n    <b class=\"fm-bold\">int counter2 =<\/b>\n        <b class=\"fm-bold\">int.Parse(context.Request.Cookies[\"counter2\"] ?? \"0\") + 1;<\/b>\n    <b class=\"fm-bold\">context.Response.Cookies.Append(\"counter2\", counter2.ToString(),<\/b>\n        <b class=\"fm-bold\">new CookieOptions {<\/b>\n            <b class=\"fm-bold\">MaxAge = TimeSpan.FromMinutes(30)<\/b>\n        <b class=\"fm-bold\">});<\/b>\n    <b class=\"fm-bold\">await context.Response<\/b>\n        <b class=\"fm-bold\">.WriteAsync($\"Counter1: {counter1}, Counter2: {counter2}\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\n<b class=\"fm-bold\">app.MapGet(\"clear\", context =&gt; {<\/b>\n    <b class=\"fm-bold\">context.Response.Cookies.Delete(\"counter1\");<\/b>\n    <b class=\"fm-bold\">context.Response.Cookies.Delete(\"counter2\");<\/b>\n    <b class=\"fm-bold\">context.Response.Redirect(\"\/\");<\/b>\n    <b class=\"fm-bold\">return Task.CompletedTask;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.MapFallback(async context =&gt; \n    await context.Response.WriteAsync(\"Hello World!\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">The new endpoints rely on cookies called <code class=\"fm-code-in-text\">counter1<\/code> and <code class=\"fm-code-in-text\">counter2<\/code>. When the <code class=\"fm-code-in-text\">\/cookie<\/code> URL is requested, the middleware looks for the cookies and parses the values to an <code class=\"fm-code-in-text\">int<\/code>. If there is no cookie, a fallback zero is used.<\/p>\n<pre class=\"programlisting\">...\nint counter1 = int.Parse(<b class=\"fm-bold\">context.Request.Cookies[\"counter1\"]<\/b> ?? \"0\") + 1;\n...<\/pre>\n<p class=\"body\">Cookies are accessed through the <code class=\"fm-code-in-text\">HttpRequest.Cookies<\/code> property, where the name of the cookie is used as the key. The value retrieved from the cookie is incremented and used to set a cookie in the response, like this:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">context.Response.Cookies.Append<\/b>(\"counter1\", counter1.ToString(),\n    new CookieOptions {\n        MaxAge = TimeSpan.FromMinutes(30)\n});\n...<\/pre>\n<p class=\"body\">Cookies are set through the <code class=\"fm-code-in-text\">HttpResponse.Cookies<\/code> property and the <code class=\"fm-code-in-text\">Append<\/code> method creates or replaces a cookie in the response. The arguments to the <code class=\"fm-code-in-text\">Append<\/code> method are the name of the cookie, its value, and a <code class=\"fm-code-in-text\">CookieOptions<\/code> object, which is used to configure the cookie. The <code class=\"fm-code-in-text\">CookieOptions<\/code> class defines the properties described in table 16.2, each of which corresponds to a cookie field.<a id=\"calibre_link-1885\"><\/a><a id=\"calibre_link-894\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Cookies are sent in the response header, which means that cookies can be set only before the response body is written, after which any changes to the cookies are ignored.<\/p>\n<p class=\"fm-table-caption\">Table 16.2 The CookieOptions properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1886\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Domain<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies the hosts to which the browser will send the cookie. By default, the cookie will be sent only to the host that created the cookie.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Expires<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property sets the expiry for the cookie.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpOnly<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">When <code class=\"fm-code-in-text1\">true<\/code>, this property tells the browser not to include the <code class=\"fm-code-in-text1\">cookie<\/code> in requests made by JavaScript code.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsEssential<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to indicate that a cookie is essential, as described in the \"Managing Cookie Consent\" section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MaxAge<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies the number of seconds until the cookie expires. Older browsers do not support cookies with this setting.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Path<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to set a URL path that must be present in the request before the cookie will be sent by the browser.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SameSite<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to specify whether the cookie should be included in cross-site requests. The values are <code class=\"fm-code-in-text1\">Lax<\/code>, <code class=\"fm-code-in-text1\">Strict<\/code>, and <code class=\"fm-code-in-text1\">None<\/code> (which is the default value).<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Secure<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">When <code class=\"fm-code-in-text1\">true<\/code>, this property tells the browser to send the cookie using HTTPS only.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The only cookie option set in listing 16.3 is <code class=\"fm-code-in-text\">MaxAge<\/code>, which tells the browser that the cookies expire after 30 minutes. The middleware in listing 16.3 deletes the cookies when the <code class=\"fm-code-in-text\">\/clear<\/code> URL is requested, which is done using the <code class=\"fm-code-in-text\">HttpResponse.Cookie.Delete<\/code> method, after which the browser is redirected to the <code class=\"fm-code-in-text\">\/<\/code> URL.<\/p>\n<pre class=\"programlisting\">...\napp.MapGet(\"clear\", context =&gt; {\n    <b class=\"fm-bold\">context.Response.Cookies.Delete(\"counter1\");<\/b>\n    <b class=\"fm-bold\">context.Response.Cookies.Delete(\"counter2\");<\/b>\n    <b class=\"fm-bold\">context.Response.Redirect(\"\/\");<\/b>\n    return Task.CompletedTask;\n});\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core and navigate to http:\/\/localhost:5000\/cookie. The response will contain cookies that are included in subsequent requests, and the counters will be incremented each time the browser is reloaded, as shown in figure 16.2. A request for http:\/\/localhost:5000\/clear will delete the cookies, and the counters will be reset.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre146\" src=\"\/images\/proaspnetcore7\/000148.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.2 Using a cookie<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-302\">16.2.1 Enabling cookie consent checking<\/h3>\n<p class=\"body\">The EU General Data Protection Regulation (GDPR) requires the user\u2019s consent before nonessential cookies can be used. ASP.NET Core provides support for obtaining consent and preventing nonessential cookies from being sent to the browser when consent has not been granted. The options pattern is used to create a policy for cookies, which is applied by a middleware component, as shown in listing 16.4.<a id=\"calibre_link-897\"><\/a><a id=\"calibre_link-1887\"><\/a><a id=\"calibre_link-1888\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Cookie consent is only one part of GDPR. See <a class=\"url\" href=\"https:\/\/en.wikipedia.org\/wiki\/General_Data_Protection_Regulation\">https:\/\/en.wikipedia.org\/wiki\/General_Data_Protection_Regulation<\/a> for a good overview of the regulations.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.4 Enabling cookie consent in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.Configure&lt;CookiePolicyOptions&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.CheckConsentNeeded = context =&gt; true;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.UseCookiePolicy();<\/b>\n\napp.MapGet(\"\/cookie\", async context =&gt; {\n    int counter1 =\n        int.Parse(context.Request.Cookies[\"counter1\"] ?? \"0\") + 1;\n    context.Response.Cookies.Append(\"counter1\", counter1.ToString(),\n        new CookieOptions {\n            MaxAge = TimeSpan.FromMinutes(30),\n            <b class=\"fm-bold\">IsEssential = true<\/b>\n        });\n    int counter2 =\n        int.Parse(context.Request.Cookies[\"counter2\"] ?? \"0\") + 1;\n    context.Response.Cookies.Append(\"counter2\", counter2.ToString(),\n        new CookieOptions {\n            MaxAge = TimeSpan.FromMinutes(30)\n        });\n    await context.Response\n        .WriteAsync($\"Counter1: {counter1}, Counter2: {counter2}\");\n});\n\napp.MapGet(\"clear\", context =&gt; {\n    context.Response.Cookies.Delete(\"counter1\");\n    context.Response.Cookies.Delete(\"counter2\");\n    context.Response.Redirect(\"\/\");\n    return Task.CompletedTask;\n});\n\napp.MapFallback(async context =&gt; \n    await context.Response.WriteAsync(\"Hello World!\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">The options pattern is used to configure a <code class=\"fm-code-in-text\">CookiePolicyOptions<\/code> object, which sets the overall policy for cookies in the application using the properties described in table 16.3.<a id=\"calibre_link-1889\"><\/a><a id=\"calibre_link-898\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 16.3 &nbsp;The CookiePolicyOptions properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1890\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">CheckConsentNeeded<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is assigned a function that receives an <code class=\"fm-code-in-text1\">HttpContext<\/code> object and returns <code class=\"fm-code-in-text1\">true<\/code> if it represents a request for which cookie consent is required. The function is called for every request, and the default function always returns <code class=\"fm-code-in-text1\">false<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ConsentCookie<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an object that is used to configure the cookie sent to the browser to record the user\u2019s cookie consent.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpOnly<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property sets the default value for the <code class=\"fm-code-in-text1\">HttpOnly<\/code> property, as described in table 16.2.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MinimumSameSitePolicy<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property sets the lowest level of security for the <code class=\"fm-code-in-text1\">SameSite<\/code> property, as described in table 16.2.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Secure<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property sets the default value for the <code class=\"fm-code-in-text1\">Secure<\/code> property, as described in table 16.2.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">To enable consent checking, I assigned a new function to the <code class=\"fm-code-in-text\">CheckConsentNeeded<\/code> property that always returns <code class=\"fm-code-in-text\">true<\/code>. The function is called for every request that ASP.NET Core receives, which means that sophisticated rules can be defined to select the requests for which consent is required. For this application, I have taken the most cautious approach and required consent for all requests.<a id=\"calibre_link-1891\"><\/a><a id=\"calibre_link-1892\"><\/a><\/p>\n<p class=\"body\">The middleware that enforces the cookie policy is added to the request pipeline using the <code class=\"fm-code-in-text\">UseCookiePolicy<\/code> method. The result is that only cookies whose <code class=\"fm-code-in-text\">IsEssential<\/code> property is <code class=\"fm-code-in-text\">true<\/code> will be added to responses. Listing 16.4 sets the <code class=\"fm-code-in-text\">IsEssential<\/code> property on <code class=\"fm-code-in-text\">cookie1<\/code> only, and you can see the effect by restarting ASP.NET Core, requesting http:\/\/localhost:5000\/cookie, and reloading the browser. Only the counter whose cookie is marked as essential updates, as shown in figure 16.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre147\" src=\"\/images\/proaspnetcore7\/000149.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.3 Using cookie consent<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-303\">16.2.2 Managing cookie consent<\/h3>\n<p class=\"body\">Unless the user has given consent, only cookies that are essential to the core features of the web application are allowed. Consent is managed through a <i class=\"fm-italics\">request feature<\/i>, which provides middleware components with access to the implementation details of how requests and responses are handled by ASP.NET Core. Features are accessed through the <code class=\"fm-code-in-text\">HttpRequest.Features<\/code> property, and each feature is represented by an interface whose properties and methods deal with one aspect of low-level request handling.<\/p>\n<p class=\"body\">Features deal with aspects of request handling that rarely need to be altered, such as the structure of responses. The exception is the management of cookie consent, which is handled through the <code class=\"fm-code-in-text\">ITrackingConsentFeature<\/code> interface, which defines the methods and properties described in table 16.4.<a id=\"calibre_link-1893\"><\/a><a id=\"calibre_link-1894\"><\/a><a id=\"calibre_link-895\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 16.4 &nbsp;The ITrackingConsentFeature members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1895\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">CanTrack<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns <code class=\"fm-code-in-text1\">true<\/code> if nonessential cookies can be added to the current request, either because the user has given consent or because consent is not required.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">CreateConsentCookie()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns a cookie that can be used by JavaScript clients to indicate consent.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GrantConsent()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Calling this method adds a cookie to the response that grants consent for nonessential cookies.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HasConsent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns <code class=\"fm-code-in-text1\">true<\/code> if the user has given consent for nonessential cookies.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsConsentNeeded<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns <code class=\"fm-code-in-text1\">true<\/code> if consent for nonessential cookies is required for the current request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">WithdrawConsent()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method deletes the consent cookie.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">To deal with consent, add a class file named <code class=\"fm-code-in-text\">ConsentMiddleware.cs<\/code> to the <code class=\"fm-code-in-text\">Platform<\/code> folder and the code shown in listing 16.5. Managing cookie consent can be done using lambda expressions, but I have used a class in this example to keep the <code class=\"fm-code-in-text\">Program.cs<\/code> method uncluttered.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.5 The contents of the ConsentMiddleware.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Http.Features;\n\nnamespace Platform {\n    public class ConsentMiddleware {\n        private RequestDelegate next;\n                \n        public ConsentMiddleware(RequestDelegate nextDelgate) {\n            next = nextDelgate;\n        }\n                \n        public async Task Invoke(HttpContext context) {\n            if (context.Request.Path == \"\/consent\") {\n                ITrackingConsentFeature? consentFeature\n                    = context.Features.Get&lt;ITrackingConsentFeature&gt;();\n                if (consentFeature != null) {\n                    if (!consentFeature.HasConsent) {\n                        consentFeature.GrantConsent();\n                    } else {\n                        consentFeature.WithdrawConsent();\n                    }\n                    await context.Response.WriteAsync(\n                        consentFeature.HasConsent ? \"Consent Granted \\n\" \n                            : \"Consent Withdrawn\\n\");\n                }\n            } else {\n                await next(context);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1896\"><\/a>Request features are obtained using the <code class=\"fm-code-in-text\">Get<\/code> method, where the generic type argument specifies the feature interface that is required, like this:<\/p>\n<pre class=\"programlisting\">...\nITrackingConsentFeature? consentFeature\n    = context.Features.<b class=\"fm-bold\">Get&lt;ITrackingConsentFeature&gt;<\/b>();\n...<\/pre>\n<p class=\"body\">Using the properties and methods described in table 16.4, the new middleware component responds to the <code class=\"fm-code-in-text\">\/consent<\/code> URL to determine and change the cookie consent. Listing 16.6 adds the new middleware to the request pipeline.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.6 Adding middleware in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.Configure&lt;CookiePolicyOptions&gt;(opts =&gt; {\n    opts.CheckConsentNeeded = context =&gt; true;\n});\n\nvar app = builder.Build();\n\napp.UseCookiePolicy();\n<b class=\"fm-bold\">app.UseMiddleware&lt;Platform.ConsentMiddleware&gt;();<\/b>\n\napp.MapGet(\"\/cookie\", async context =&gt; {\n\n\/\/ <i class=\"fm-italics\">...statments omitted for brevity...<\/i><\/pre>\n<p class=\"body\">To see the effect, restart ASP.NET Core and request http:\/\/localhost:5000\/consent and then http:\/\/localhost:5000\/cookie. When consent is granted, nonessential cookies are allowed, and both the counters in the example will work, as shown in figure 16.4. Repeat the process to withdraw consent, and you will find that only the counter whose cookie has been denoted as essential works.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre148\" src=\"\/images\/proaspnetcore7\/000150.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.4 Managing cookie consent<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-304\">16.3 Using sessions<\/h2>\n<p class=\"body\">The example in the previous section used cookies to store the application\u2019s state data, providing the middleware component with the data required. The problem with this approach is that the contents of the cookie are stored at the client, where it can be manipulated and used to alter the behavior of the application.<a id=\"calibre_link-1897\"><\/a><a id=\"calibre_link-1898\"><\/a><a id=\"calibre_link-1899\"><\/a><a id=\"calibre_link-1900\"><\/a><a id=\"calibre_link-896\"><\/a><\/p>\n<p class=\"body\">A better approach is to use the ASP.NET Core session feature. The session middleware adds a cookie to responses, which allows related requests to be identified and which is also associated with data stored at the server.<\/p>\n<p class=\"body\">When a request containing the session cookie is received, the session middleware component retrieves the server-side data associated with the session and makes it available to other middleware components through the <code class=\"fm-code-in-text\">HttpContext<\/code> object. Using sessions means that the application\u2019s data remains at the server and only the identifier for the session is sent to the browser.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-305\">16.3.1 Configuring the session service and middleware<\/h3>\n<p class=\"body\">Setting up sessions requires configuring services and adding a middleware component to the request pipeline. Listing 16.7 adds the statements to the <code class=\"fm-code-in-text\">Program.cs<\/code> file to set up sessions for the example application and removes the endpoints from the previous section.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.7 Configuring sessions in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddDistributedMemoryCache();<\/b>\n\n<b class=\"fm-bold\">builder.Services.AddSession(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.IdleTimeout = TimeSpan.FromMinutes(30);<\/b>\n    <b class=\"fm-bold\">opts.Cookie.IsEssential = true;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.UseSession();<\/b>\n\napp.MapFallback(async context =&gt;\n    await context.Response.WriteAsync(\"Hello World!\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">When you use sessions, you must decide how to store the associated data. ASP.NET Core provides three options for session data storage, each of which has its own method to register a service, as described in table 16.5.<a id=\"calibre_link-1901\"><\/a><a id=\"calibre_link-1902\"><\/a><a id=\"calibre_link-1084\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 16.5 The session storage methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1903\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddDistributedMemoryCache<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sets up an in-memory cache. Despite the name, the cache is not distributed and is responsible only for storing data for the instance of the ASP.NET Core runtime where it is created.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddDistributedSqlServerCache<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sets up a cache that stores data in SQL Server and is available when the <code class=\"fm-code-in-text1\">Microsoft.Extensions.Caching.SqlServer<\/code> package is installed. This cache is used in chapter 17.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddStackExchangeRedisCache<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sets up a Redis cache and is available when the <code class=\"fm-code-in-text1\">Microsoft.Extensions.Caching.Redis<\/code> package is installed.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Caching is described in detail in chapter 17, but for this chapter, I used the in-memory cache:<a id=\"calibre_link-1904\"><\/a><\/p>\n<pre class=\"programlisting\">...\nbuilder.Services.<b class=\"fm-bold\">AddDistributedMemoryCache<\/b>();\n...<\/pre>\n<p class=\"body\">Despite its name, the cache service created by the <code class=\"fm-code-in-text\">AddDistributedMemoryCache<\/code> method isn\u2019t distributed and stores the session data for a single instance of the ASP.NET Core runtime. If you scale an application by deploying multiple instances of the runtime, then you should use one of the other caches, such as the SQL Server cache, which is demonstrated in chapter 17.<\/p>\n<p class=\"body\">The next step is to use the options pattern to configure the session middleware, like this:<a id=\"calibre_link-1905\"><\/a><a id=\"calibre_link-1906\"><\/a><\/p>\n<pre class=\"programlisting\">...\nbuilder.Services.<b class=\"fm-bold\">AddSession<\/b>(opts =&gt; {\n    opts.IdleTimeout = TimeSpan.FromMinutes(30);\n    opts.Cookie.IsEssential = true;\n});\n...<\/pre>\n<p class=\"body\">Table 16.6 shows that the options class for sessions is <code class=\"fm-code-in-text\">SessionOptions<\/code> and describes the key properties it defines.<\/p>\n<p class=\"fm-table-caption\">Table 16.6 Properties defined by the SessionOptions class<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1907\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Cookie<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to configure the session cookie.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IdleTimeout<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to configure the time span after which a session expires.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Cookie<\/code> property returns an object that can be used to configure the session cookie. Table 16.7 describes the most useful cookie configuration properties for session data.<\/p>\n<p class=\"fm-table-caption\">Table 16.7 Cookie configuration properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1908\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpOnly<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies whether the browser will prevent the cookie from being included in HTTP requests sent by JavaScript code. This property should be set to <code class=\"fm-code-in-text1\">true<\/code> for projects that use a JavaScript application whose requests should be included in the session. The default value is <code class=\"fm-code-in-text1\">true<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsEssential<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies whether the cookie is required for the application to function and should be used even when the user has specified that they don\u2019t want the application to use cookies. The default value is <code class=\"fm-code-in-text1\">false<\/code>. See the \u201cManaging Cookie Consent\u201d section for more details.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SecurityPolicy<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property sets the security policy for the cookie, using a value from the <code class=\"fm-code-in-text1\">CookieSecurePolicy<\/code> enum. The values are <code class=\"fm-code-in-text1\">Always<\/code> (which restricts the cookie to HTTPS requests), <code class=\"fm-code-in-text1\">SameAsRequest<\/code> (which restricts the cookie to HTTPS if the original request was made using HTTPS), and <code class=\"fm-code-in-text1\">None<\/code> (which allows the cookie to be used on HTTP and HTTPS requests). The default value is <code class=\"fm-code-in-text1\">None<\/code>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The options set in listing 16.7 allow the session cookie to be included in requests started by JavaScript and flag the cookie as essential so that it will be used even when the user has expressed a preference not to use cookies (see the \u201cManaging Cookie Consent\u201d section for more details about essential cookies). The <code class=\"fm-code-in-text\">IdleTimeout<\/code> option has been set so that sessions expire if no request containing the sessions cookie is received for 30 minutes.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> The session cookie isn\u2019t denoted as essential by default, which can cause problems when cookie consent is used. Listing 16.7 sets the <code class=\"fm-code-in-text1\">IsEssential<\/code> property to <code class=\"fm-code-in-text1\">true<\/code> to ensure that sessions always work. If you find sessions don\u2019t work as expected, then this is the likely cause, and you must either set <code class=\"fm-code-in-text1\">IsEssential<\/code> to <code class=\"fm-code-in-text1\">true<\/code> or adapt your application to deal with users who don\u2019t grant consent and won\u2019t accept session cookies.<\/p>\n<p class=\"body\">The final step is to add the session middleware component to the request pipeline, which is done with the <code class=\"fm-code-in-text\">UseSession<\/code> method. When the middleware processes a request that contains a session cookie, it retrieves the session data from the cache and makes it available through the <code class=\"fm-code-in-text\">HttpContext<\/code> object, before passing the request along the request pipeline and providing it to other middleware components. When a request arrives without a session cookie, a new session is started, and a cookie is added to the response so that subsequent requests can be identified as being part of the session.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-306\">16.3.2 Using session data<\/h3>\n<p class=\"body\">The session middleware provides access to details of the session associated with a request through the <code class=\"fm-code-in-text\">Session<\/code> property of the <code class=\"fm-code-in-text\">HttpContext<\/code> object. The <code class=\"fm-code-in-text\">Session<\/code> property returns an object that implements the <code class=\"fm-code-in-text\">ISession<\/code> interface, which provides the methods shown in table 16.8 for accessing session data.<a id=\"calibre_link-1909\"><\/a><a id=\"calibre_link-1910\"><\/a><a id=\"calibre_link-1148\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 16.8 Useful ISession methods and extension methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1911\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Clear()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method removes all the data in the session.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">CommitAsync()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This asynchronous method commits changed session data to the cache.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetString(key)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method retrieves a string value using the specified key.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetInt32(key)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method retrieves an integer value using the specified key.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Id<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the unique identifier for the session.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsAvailable<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This returns <code class=\"fm-code-in-text1\">true<\/code> when the session data has been loaded.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Keys<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This enumerates the keys for the session data items.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Remove(key)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method removes the value associated with the specified key.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetString(key,val)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method stores a string using the specified key.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetInt32(key,<\/code> <code class=\"fm-code-in-text1\">val)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method stores an integer using the specified key.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Session data is stored in key-value pairs, where the keys are strings and the values are strings or integers. This simple data structure allows session data to be stored easily by each of the caches listed in table 16.5. Applications that need to store more complex data can use serialization, which is the approach I took for the SportsStore. Listing 16.8 uses session data to re-create the counter example.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.8 Using session data in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDistributedMemoryCache();\n\nbuilder.Services.AddSession(opts =&gt; {\n    opts.IdleTimeout = TimeSpan.FromMinutes(30);\n    opts.Cookie.IsEssential = true;\n});\n\nvar app = builder.Build();\n\napp.UseSession();\n\n<b class=\"fm-bold\">app.MapGet(\"\/session\", async context =&gt; {<\/b>\n    <b class=\"fm-bold\">int counter1 = (context.Session.GetInt32(\"counter1\") ?? 0) + 1;<\/b>\n    <b class=\"fm-bold\">int counter2 = (context.Session.GetInt32(\"counter2\") ?? 0) + 1;<\/b>\n    <b class=\"fm-bold\">context.Session.SetInt32(\"counter1\", counter1);<\/b>\n    <b class=\"fm-bold\">context.Session.SetInt32(\"counter2\", counter2);<\/b>\n    <b class=\"fm-bold\">await context.Session.CommitAsync();<\/b>\n    <b class=\"fm-bold\">await context.Response<\/b>\n        <b class=\"fm-bold\">.WriteAsync($\"Counter1: {counter1}, Counter2: {counter2}\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.MapFallback(async context =&gt;\n    await context.Response.WriteAsync(\"Hello World!\"));\n        \napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">GetInt32<\/code> method is used to read the values associated with the keys <code class=\"fm-code-in-text\">counter1<\/code> and <code class=\"fm-code-in-text\">counter2<\/code>. If this is the first request in a session, no value will be available, and the null-coalescing operator is used to provide an initial value. The value is incremented and then stored using the <code class=\"fm-code-in-text\">SetInt32<\/code> method and used to generate a simple result for the client.<\/p>\n<p class=\"body\">The use of the <code class=\"fm-code-in-text\">CommitAsync<\/code> method is optional, but it is good practice to use it because it will throw an exception if the session data can\u2019t be stored in the cache. By default, no error is reported if there are caching problems, which can lead to unpredictable and confusing behavior.<\/p>\n<p class=\"body\">All changes to the session data must be made before the response is sent to the client, which is why I read, update, and store the session data before calling the <code class=\"fm-code-in-text\">Response.WriteAsync<\/code> method in listing 16.8.<a id=\"calibre_link-1912\"><\/a><a id=\"calibre_link-1149\"><\/a><\/p>\n<p class=\"body\">Notice that the statements in listing 16.8 do not have to deal with the session cookie, detect expired sessions, or load the session data from the cache. All this work is done automatically by the session middleware, which presents the results through the <code class=\"fm-code-in-text\">HttpContext.Session<\/code> property. One consequence of this approach is that the <code class=\"fm-code-in-text\">HttpContext.Session<\/code> property is not populated with data until after the session middleware has processed a request, which means that you should attempt to access session data only in middleware or endpoints that are added to the request pipeline after the <code class=\"fm-code-in-text\">UseSession<\/code> method is called.<\/p>\n<p class=\"body\">Restart ASP.NET Core and navigate to the http:\/\/localhost:5000\/session URL, and you will see the value of the counter. Reload the browser, and the counter values will be incremented, as shown in figure 16.5. The sessions and session data will be lost when ASP.NET Core is stopped because I chose the in-memory cache. The other storage options operate outside of the ASP.NET Core runtime and survive application restarts.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre149\" src=\"\/images\/proaspnetcore7\/000151.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.5 Using session data<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-307\">16.4 Working with HTTPS connections<\/h2>\n<p class=\"body\">Users increasingly expect web applications to use HTTPS connections, even for requests that don\u2019t contain or return sensitive data. ASP.NET Core supports both HTTP and HTTPS connections and provides middleware that can force HTTP clients to use HTTPS.<a id=\"calibre_link-1913\"><\/a><a id=\"calibre_link-1914\"><\/a><a id=\"calibre_link-1915\"><\/a><a id=\"calibre_link-1006\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">HTTPS vs. SSL vs. TLS<\/p>\n<p class=\"fm-sidebar-text\">HTTPS is the combination of HTTP and the Transport Layer Security (TLS) or Secure Sockets Layer (SSL). TLS has replaced the obsolete SSL protocol, but the term SSL has become synonymous with secure networking and is often used to refer to TLS.If you are interested in security and cryptography, then the details of HTTPS are worth exploring, and <a class=\"url\" href=\"https:\/\/en.wikipedia.org\/wiki\/HTTPS\">https:\/\/en.wikipedia.org\/wiki\/HTTPS<\/a> is a good place to start.<a id=\"calibre_link-1916\"><\/a><a id=\"calibre_link-1917\"><\/a><\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-308\">16.4.1 Enabling HTTPS connections<\/h3>\n<p class=\"body\">HTTPS is enabled and configured in the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file in the <code class=\"fm-code-in-text\">Properties<\/code> folder, as shown in listing 16.9.<a id=\"calibre_link-1918\"><\/a><a id=\"calibre_link-1919\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.9 Changes in the launchSettings.json file in the Platform\/Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http:\/\/localhost:5000\",\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"Platform\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000;https:\/\/localhost:5500\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<p class=\"body\">The new <code class=\"fm-code-in-text\">applicationUrl<\/code> setting sets the URLs to which the application will respond, and HTTPS is enabled by adding an HTTPS URL to the configuration setting. Note that the URLs are separated by a semicolon and no spaces are allowed.<\/p>\n<p class=\"body\">The .NET Core runtime includes a test certificate that is used for HTTPS requests. Run the commands shown in listing 16.10 in the <code class=\"fm-code-in-text\">Platform<\/code> folder to regenerate and trust the test certificate.<a id=\"calibre_link-1920\"><\/a><a id=\"calibre_link-1008\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.10 Regenerating the Development Certificates<\/p>\n<pre class=\"programlisting\">dotnet dev-certs https --clean\ndotnet dev-certs https --trust<\/pre>\n<p class=\"body\">Select Yes to the prompts to delete the existing certificate that has already been trusted and select Yes to trust the new certificate, as shown in figure 16.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre150\" src=\"\/images\/proaspnetcore7\/000152.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.6 Regenerating the HTTPS certificate<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-309\">16.4.2 Detecting HTTPS requests<\/h3>\n<p class=\"body\">Requests made using HTTPS can be detected through the <code class=\"fm-code-in-text\">HttpRequest.IsHttps<\/code> property. In listing 16.11, I added a message to the fallback response that reports whether a request is made using HTTPS.<a id=\"calibre_link-1921\"><\/a><a id=\"calibre_link-1007\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.11 Detecting HTTPS in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDistributedMemoryCache();\n\nbuilder.Services.AddSession(opts =&gt; {\n    opts.IdleTimeout = TimeSpan.FromMinutes(30);\n    opts.Cookie.IsEssential = true;\n});\n\nvar app = builder.Build();\n\napp.UseSession();\n\napp.MapGet(\"\/session\", async context =&gt; {\n    int counter1 = (context.Session.GetInt32(\"counter1\") ?? 0) + 1;\n    int counter2 = (context.Session.GetInt32(\"counter2\") ?? 0) + 1;\n    context.Session.SetInt32(\"counter1\", counter1);\n    context.Session.SetInt32(\"counter2\", counter2);\n    await context.Session.CommitAsync();\n    await context.Response\n        .WriteAsync($\"Counter1: {counter1}, Counter2: {counter2}\");\n});\n\n<b class=\"fm-bold\">app.MapFallback(async context =&gt; {<\/b>\n    <b class=\"fm-bold\">await context.Response<\/b>\n        <b class=\"fm-bold\">.WriteAsync($\"HTTPS Request: {context.Request.IsHttps} \\n\");<\/b>\n    <b class=\"fm-bold\">await context.Response.WriteAsync(\"Hello World!\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">To test HTTPS, restart ASP.NET Core and navigate to http:\/\/localhost:5000. This is a regular HTTP request and will produce the result shown on the left of figure 16.7. Next, navigate to https:\/\/localhost:5500, paying close attention to the URL scheme, which is <code class=\"fm-code-in-text\">https<\/code> and not <code class=\"fm-code-in-text\">http<\/code>, as it has been in previous examples. The new middleware will detect the HTTPS connection and produce the output on the right of figure 16.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre151\" src=\"\/images\/proaspnetcore7\/000153.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.7 Detecting an HTTPS request<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-310\">16.4.3 Enforcing HTTPS requests<\/h3>\n<p class=\"body\">ASP.NET Core provides a middleware component that enforces the use of HTTPS by sending a redirection response for requests that arrive over HTTP. Listing 16.12 adds this middleware to the request pipeline.<a id=\"calibre_link-1922\"><\/a><a id=\"calibre_link-1011\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.12 Enforcing HTTPS in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDistributedMemoryCache();\n\nbuilder.Services.AddSession(opts =&gt; {\n    opts.IdleTimeout = TimeSpan.FromMinutes(30);\n    opts.Cookie.IsEssential = true;\n});\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.UseHttpsRedirection();<\/b>\napp.UseSession();\n\napp.MapGet(\"\/session\", async context =&gt; {\n    int counter1 = (context.Session.GetInt32(\"counter1\") ?? 0) + 1;\n    int counter2 = (context.Session.GetInt32(\"counter2\") ?? 0) + 1;\n    context.Session.SetInt32(\"counter1\", counter1);\n    context.Session.SetInt32(\"counter2\", counter2);\n    await context.Session.CommitAsync();\n    await context.Response\n        .WriteAsync($\"Counter1: {counter1}, Counter2: {counter2}\");\n});\n\napp.MapFallback(async context =&gt; {\n    await context.Response\n        .WriteAsync($\"HTTPS Request: {context.Request.IsHttps} \\n\");\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UseHttpsRedirection<\/code> method adds the middleware component, which appears at the start of the request pipeline so that the redirection to HTTPS occurs before any other component can short-circuit the pipeline and produce a response using regular HTTP.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Configuring HTTPS redirection<\/p>\n<p class=\"fm-sidebar-text\">The options pattern can be used to configure the HTTPS redirection middleware, by calling the <code class=\"fm-code-in-text1\">AddHttpsRedirection<\/code> method like this:<a id=\"calibre_link-1923\"><\/a><\/p>\n<pre class=\"programlisting\">...\nbuilder.Services.AddHttpsRedirection(opts =&gt; {\n    opts.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;\n    opts.HttpsPort = 443;\n});\n...<\/pre>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-1010\"><\/a>The only two configuration options are shown in this fragment, which sets the status code used in the redirection response, and the port to which the client is redirected, overriding the value that is loaded from the configuration files. Specifying the HTTPS port can be useful when deploying the application, but care should be taken when changing the redirection status code.<\/p>\n<\/div>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000, which is the HTTP URL for the application. The HTTPS redirection middleware will intercept the request and redirect the browser to the HTTPS URL, as shown in figure 16.8.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Modern browsers often hide the URL scheme, which is why you should pay attention to the port number that is displayed. To display the URL scheme in the figure, I had to click the URL bar so the browser would display the full URL.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre152\" src=\"\/images\/proaspnetcore7\/000154.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.8 Forcing HTTPS requests<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-311\">16.4.4 Enabling HTTP strict transport security<\/h3>\n<p class=\"body\">One limitation of HTTPS redirection is that the user can make an initial request using HTTP before being redirected to a secure connection, presenting a security risk.<\/p>\n<p class=\"body\">The HTTP Strict Transport Security (HSTS) protocol is intended to help mitigate this risk and works by including a header in responses that tells browsers to use HTTPS only when sending requests to the web application\u2019s host. After an HSTS header has been received, browsers that support HSTS will send requests to the application using HTTPS even if the user specifies an HTTP URL. Listing 16.13 shows the addition of the HSTS middleware to the request pipeline.<a id=\"calibre_link-1924\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.13 Enabling HSTS in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDistributedMemoryCache();\n\n<b class=\"fm-bold\">builder.Services.AddSession(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.IdleTimeout = TimeSpan.FromMinutes(30);<\/b>\n    <b class=\"fm-bold\">opts.Cookie.IsEssential = true;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nbuilder.Services.AddHsts(opts =&gt; {\n    opts.MaxAge = TimeSpan.FromDays(1);\n    opts.IncludeSubDomains = true;\n});\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">if (app.Environment.IsProduction()) {<\/b>\n    <b class=\"fm-bold\">app.UseHsts();<\/b>\n<b class=\"fm-bold\">}<\/b>\n\napp.UseHttpsRedirection();\napp.UseSession();\n\napp.MapGet(\"\/session\", async context =&gt; {\n    int counter1 = (context.Session.GetInt32(\"counter1\") ?? 0) + 1;\n    int counter2 = (context.Session.GetInt32(\"counter2\") ?? 0) + 1;\n    context.Session.SetInt32(\"counter1\", counter1);\n    context.Session.SetInt32(\"counter2\", counter2);\n    await context.Session.CommitAsync();\n    await context.Response\n        .WriteAsync($\"Counter1: {counter1}, Counter2: {counter2}\");\n});\n\napp.MapFallback(async context =&gt; {\n    await context.Response\n        .WriteAsync($\"HTTPS Request: {context.Request.IsHttps} \\n\");\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The middleware is added to the request pipeline using the <code class=\"fm-code-in-text\">UseHsts<\/code> method. The HSTS middleware can be configured with the <code class=\"fm-code-in-text\">AddHsts<\/code> method, using the properties described in table 16.9.<\/p>\n<p class=\"fm-table-caption\">Table 16.9 The HSTS configuration properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1925\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExcludeHosts<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">List&lt;string&gt;<\/code> that contains the hosts for which the middleware won\u2019t send an HSTS header. The defaults exclude <code class=\"fm-code-in-text1\">localhost<\/code> and the loopback addresses for IP version 4 and version 6.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IncludeSubDomains<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">When <code class=\"fm-code-in-text1\">true<\/code>, the browser will apply the HSTS setting to subdomains. The default value is <code class=\"fm-code-in-text1\">false<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MaxAge<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies the period for which the browser should make only HTTPS requests. The default value is 30 days.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Preload<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is set to <code class=\"fm-code-in-text1\">true<\/code> for domains that are part of the HSTS preload scheme. The domains are hard-coded into the browser, which avoids the initial insecure request and ensures that only HTTPS is used. See <code class=\"fm-code-in-text1\">hstspreload.org<\/code> for more details.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">HSTS is disabled during development and enabled only in production, which is why the <code class=\"fm-code-in-text\">UseHsts<\/code> method is called only for that environment.<\/p>\n<pre class=\"programlisting\">...\nif (app.Environment.<b class=\"fm-bold\">IsProduction<\/b>()) {\n    app.UseHsts();\n}\n... <\/pre>\n<p class=\"body\">HSTS must be used with care because it is easy to create a situation where clients cannot access the application, especially when nonstandard ports are used for HTTP and HTTPS.<\/p>\n<p class=\"body\">If the example application is deployed to a server named <code class=\"fm-code-in-text\">myhost<\/code>, for example, and the user requests http:\/\/myhost:5000, the browser will be redirected to https:\/\/myhost:5500 and sent the HSTS header, and the application will work as expected. But the next time the user requests http:\/\/myhost:5000, they will receive an error stating that a secure connection cannot be established.<\/p>\n<p class=\"body\">This problem arises because some browsers take a simplistic approach to HSTS and assume that HTTP requests are handled on port 80 and HTTPS requests on port 443.<\/p>\n<p class=\"body\">When the user requests http:\/\/myhost:5000, the browser checks its HSTS data and sees that it previously received an HSTS header for <code class=\"fm-code-in-text\">myhost<\/code>. Instead of the HTTP URL that the user entered, the browser sends a request to https:\/\/myhost:5000. ASP.NET Core doesn\u2019t handle HTTPS on the port it uses for HTTP, and the request fails. The browser doesn\u2019t remember or understand the redirection it previously received for port 5001.<\/p>\n<p class=\"body\">This isn\u2019t an issue where port 80 is used for HTTP and 443 is used for HTTPS. The URL http:\/\/myhost is equivalent to http:\/\/myhost:80, and https:\/\/myhost is equivalent to https:\/\/myhost:443, which means that changing the scheme targets the right port.<\/p>\n<p class=\"body\">Once a browser has received an HSTS header, it will continue to honor it for the duration of the header\u2019s <code class=\"fm-code-in-text\">MaxAge<\/code> property. When you first deploy an application, it is a good idea to set the HSTS <code class=\"fm-code-in-text\">MaxAge<\/code> property to a relatively short duration until you are confident that your HTTPS infrastructure is working correctly, which is why I have set <code class=\"fm-code-in-text\">MaxAge<\/code> to one day in listing 16.13. Once you are sure that clients will not need to make HTTP requests, you can increase the <code class=\"fm-code-in-text\">MaxAge<\/code> property. A <code class=\"fm-code-in-text\">MaxAge<\/code> value of one year is commonly used.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> If you are testing HSTS with Google Chrome, you can inspect and edit the list of domains to which HSTS is applied by navigating to <code class=\"fm-code-in-text1\">chrome:\/\/net-internals\/#hsts<\/code>.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-312\">16.5 Using rate limits<\/h2>\n<p class=\"body\">ASP.NET Core includes middleware components that limit the rate at which requests are processed, which can be a good way to ensure that a large number of requests doesn\u2019t overwhelm the application. The .NET framework provides a general API for rate limiting, which is integrated into ASP.NET Core through extension methods used in the <code class=\"fm-code-in-text\">Program.cs<\/code> file. Listing 16.14 defines a rate limit and applies it to an endpoint.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Use this feature with caution. Once a rate limit has been reached, ASP.NET Core rejects HTTP requests with an error until capacity becomes available again, which can confuse clients and users alike.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.14 Defining a rate limit in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Microsoft.AspNetCore.RateLimiting;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDistributedMemoryCache();\n\nbuilder.Services.AddSession(opts =&gt; {\n    opts.IdleTimeout = TimeSpan.FromMinutes(30);\n    opts.Cookie.IsEssential = true;\n});\n\nbuilder.Services.AddHsts(opts =&gt; {\n    opts.MaxAge = TimeSpan.FromDays(1);\n    opts.IncludeSubDomains = true;\n});\n\n<b class=\"fm-bold\">builder.Services.AddRateLimiter(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.AddFixedWindowLimiter(\"fixedWindow\", fixOpts =&gt; {<\/b>\n        <b class=\"fm-bold\">fixOpts.PermitLimit = 1;<\/b>\n        <b class=\"fm-bold\">fixOpts.QueueLimit = 0;<\/b>\n        <b class=\"fm-bold\">fixOpts.Window = TimeSpan.FromSeconds(15);<\/b>\n    <b class=\"fm-bold\">});<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\nif (app.Environment.IsProduction()) {\n    app.UseHsts();\n}\n\napp.UseHttpsRedirection();\n\n<b class=\"fm-bold\">app.UseRateLimiter();<\/b>\n\napp.UseSession();\n\napp.MapGet(\"\/session\", async context =&gt; {\n    int counter1 = (context.Session.GetInt32(\"counter1\") ?? 0) + 1;\n    int counter2 = (context.Session.GetInt32(\"counter2\") ?? 0) + 1;\n    context.Session.SetInt32(\"counter1\", counter1);\n    context.Session.SetInt32(\"counter2\", counter2);\n    await context.Session.CommitAsync();\n    await context.Response\n        .WriteAsync($\"Counter1: {counter1}, Counter2: {counter2}\");\n<b class=\"fm-bold\">}).RequireRateLimiting(\"fixedWindow\");<\/b>\n\napp.MapFallback(async context =&gt; {\n    await context.Response\n        .WriteAsync($\"HTTPS Request: {context.Request.IsHttps} \\n\");\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddRateLimiter<\/code> extension method is used to configure rate limiting, which is done using the options pattern. In this example, I have used the <code class=\"fm-code-in-text\">AddFixedWindowLimiter<\/code> method to create a rate-limiting policy that limits the number of requests that will be handled in a specified duration. The <code class=\"fm-code-in-text\">AddFixedWindowLimiter<\/code> method is one of four extension methods that are available for rate limiting, described in table 16.10. Full details of how each of these rate limits works can be found at <a class=\"url\" href=\"https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/performance\/rate-limit\">https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/performance\/rate-limit<\/a>.<\/p>\n<p class=\"fm-table-caption\">Table 16.10 The rate limiting extension methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1926\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddFixedWindowLimiter<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a rate limiter that allows a specified number of requests in a fixed period.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddSlidingWindowLimiter<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a rate limiter that allows a specified number of requests in a fixed period, with the addition of a sliding window to smooth the rate limits.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddTokenBucketLimiter<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a rate limiter that maintains a pool of tokens that are allocated to requests. Requests can be allocated different amounts of tokens, and requests are only handled when there are sufficient free tokens in the pool.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddConcurrencyLimiter<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method creates a rate limiter that allows a specific number of concurrent requests.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">This is the least flexible of the time-based rate limiters, but it is the easiest to demonstrate and test:<\/p>\n<pre class=\"programlisting\">...\nopts.AddFixedWindowLimiter(\"fixedWindow\", fixOpts =&gt; {\n    fixOpts.PermitLimit = 1;\n    fixOpts.QueueLimit = 0;\n    fixOpts.Window = TimeSpan.FromSeconds(15);\n});\n...<\/pre>\n<p class=\"body\">Each extension method is configured with an instance of its own options class, but they all share the most important properties. The <code class=\"fm-code-in-text\">PermitLimit<\/code> property is used to specify the maximum number of requests, and the <code class=\"fm-code-in-text\">QueueLimit<\/code> property is used to specify the maximum number of requests that will be queued waiting for available capacity. If there is no available capacity and no available slots in the queue, then requests will be rejected. The combination of properties is given a name, which is used to apply the rate limit to endpoints.<\/p>\n<p class=\"body\">These options are supplemented by additional properties which are specific to each rate limiter. In the case of the <code class=\"fm-code-in-text\">AddFixedWindowLimiter<\/code> method, the <code class=\"fm-code-in-text\">Window<\/code> property is used to specify the duration to which the rate is applied.<\/p>\n<p class=\"body\">In listing 16.14, I specified a <code class=\"fm-code-in-text\">PermitLimit<\/code> of <code class=\"fm-code-in-text\">1<\/code>, a <code class=\"fm-code-in-text\">QueueLimit<\/code> of 0, and a <code class=\"fm-code-in-text\">Window<\/code> of <code class=\"fm-code-in-text\">15<\/code> seconds. This means that one request will be accepted every 15 seconds, without any queue, meaning that any additional requests will be rejected. This rate limit is assigned the name <code class=\"fm-code-in-text\">fixedWindow<\/code>.<\/p>\n<p class=\"body\">The rate limiting middleware is added to the pipeline using the <code class=\"fm-code-in-text\">UseRateLimiter<\/code> method and applied to an endpoint with the <code class=\"fm-code-in-text\">RequireRateLimiting<\/code> method. An application can define multiple rate limits and so a name is used to select the rate limit that is required:<\/p>\n<pre class=\"programlisting\">...\n}).<b class=\"fm-bold\">RequireRateLimiting<\/b>(\"fixedWindow\");\n...<\/pre>\n<p class=\"body\">Endpoints can be con configured with different rate limits, or no rate limit, and each rate limit will be managed independently.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Applying rate limits to controllers and pages<\/p>\n<p class=\"fm-sidebar-text\">ASP.NET Core provides extension methods to configure rate limits for controllers, described in chapter 19, and Razor Pages, which are described in chapter 23.<\/p>\n<\/div>\n<p class=\"body\">It can be difficult to test rate limits effectively because browsers will often apply their own restrictions on the requests they send. Microsoft provides recommendations for testing tools at <a class=\"url\" href=\"https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/performance\/rate-limit#testing-endpoints-with-rate-limiting\">https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/performance\/rate-limit#testing-endpoints-with-rate-limiting<\/a>, but a policy as simple as the one defined in listing 16.14 is easy to test.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request https:\/\/localhost:5500\/session. Click the Reload button within 15 seconds, and the new request will exceed the rate limit and ASP.NET Core will respond with a 503 status code, as shown in figure 16.9. Wait until the 15-second period has elapsed and click the Reload button again; the rate limit should reset and the request will be processed normally.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre153\" src=\"\/images\/proaspnetcore7\/000155.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.9 The effect of a rate limit.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-313\">16.6 Handling exceptions and errors<\/h2>\n<p class=\"body\">When the request pipeline is created, the <code class=\"fm-code-in-text\">WebApplicationBuilder<\/code> class uses the development environment to enable middleware that handles exceptions by producing HTTP responses that are helpful to developers. Here is a fragment of code from the <code class=\"fm-code-in-text\">WebApplicationBuilder<\/code> class:<\/p>\n<pre class=\"programlisting\">...\nif (context.HostingEnvironment.IsDevelopment()) {\n    app.UseDeveloperExceptionPage();}\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UseDeveloperExceptionPage<\/code> method adds the middleware component that intercepts exceptions and presents a more useful response. To demonstrate the way that exceptions are handled, listing 16.15 replaces the middleware and endpoints used in earlier examples with a new component that deliberately throws an exception.<a id=\"calibre_link-1927\"><\/a><a id=\"calibre_link-1928\"><\/a><a id=\"calibre_link-1929\"><\/a><a id=\"calibre_link-949\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.15 Adding Middleware in the Program.cs File in the Platform Folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.Run(context =&gt; {<\/b>\n    <b class=\"fm-bold\">throw new Exception(\"Something has gone wrong\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">Restart ASP.NET Core and navigate to http:\/\/localhost:5000 to see the response that the middleware component generates, which is shown in figure 16.10. The page presents a stack trace and details about the request, including details of the headers and cookies it contained.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre154\" src=\"\/images\/proaspnetcore7\/000156.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.10 The developer exception page<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-314\">16.6.1 Returning an HTML error response<\/h3>\n<p class=\"body\">When the developer exception middleware is disabled, as it will be when the application is in production, ASP.NET Core deals with unhandled exceptions by sending a response that contains just an error code. Listing 16.16 changes the environment to production.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.16 Changes in the launchSettings.json file in the Platform\/Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http:\/\/localhost:5000\",\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"Platform\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"applicationUrl\": \"http:\/\/localhost:5000;https:\/\/localhost:5500\",\n      \"environmentVariables\": {\n        <b class=\"fm-bold\">\"ASPNETCORE_ENVIRONMENT\": \"Production\"<\/b>\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<p class=\"body\">Start ASP.NET Core using the <code class=\"fm-code-in-text\">dotnet run<\/code> command and navigate to http:\/\/localhost:5000. The response you see will depend on your browser because ASP.NET Core has only provided it with a response containing status code 500, without any content to display. Figure 16.11 shows how this is handled by Google Chrome.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre155\" src=\"\/images\/proaspnetcore7\/000157.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.11 Returning an error response<\/p>\n<\/div>\n<p class=\"body\">As an alternative to returning just status codes, ASP.NET Core provides middleware that intercepts unhandled exceptions and sends a redirection to the browser instead, which can be used to show a friendlier response than the raw status code. The exception redirection middleware is added with the <code class=\"fm-code-in-text\">UseExceptionHandler<\/code> method, as shown in listing 16.17.<a id=\"calibre_link-1930\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.17 Returning an error response in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">if (!app.Environment.IsDevelopment()) {<\/b>\n    <b class=\"fm-bold\">app.UseExceptionHandler(\"\/error.xhtml\");<\/b>\n    <b class=\"fm-bold\">app.UseStaticFiles();<\/b>\n<b class=\"fm-bold\">}<\/b>\n\napp.Run(context =&gt; {\n    throw new Exception(\"Something has gone wrong\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">When an exception is thrown, the exception handler middleware will intercept the response and redirect the browser to the URL provided as the argument to the <code class=\"fm-code-in-text\">UseExceptionHandler<\/code> method. For this example, the redirection is to a URL that will be handled by a static file, so the <code class=\"fm-code-in-text\">UseStaticFiles<\/code> middleware has also been added to the pipeline.<a id=\"calibre_link-950\"><\/a><\/p>\n<p class=\"body\">To add the file that the browser will receive, create an HTML file named <code class=\"fm-code-in-text\">error.xhtml<\/code> in the <code class=\"fm-code-in-text\">wwwroot<\/code> folder and add the content shown in listing 16.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.18 The contents of the error.xhtml file in the Platform\/wwwroot folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n    &lt;link rel=\"stylesheet\" href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" \/&gt;\n    &lt;title&gt;Error&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body class=\"text-center\"&gt;\n    &lt;h3 class=\"p-2\"&gt;Something went wrong...&lt;\/h3&gt;\n    &lt;h6&gt;You can go back to the &lt;a href=\"\/\"&gt;homepage&lt;\/a&gt; and try again&lt;\/h6&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and navigate to http:\/\/localhost:5000 to see the effect of the new middleware. Instead of the raw status code, the browser will be sent the content of the <code class=\"fm-code-in-text\">\/error.xhtml<\/code> URL, as shown in figure 16.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre156\" src=\"\/images\/proaspnetcore7\/000158.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.12 Displaying an HTML error<\/p>\n<\/div>\n<p class=\"body\">There are versions of the <code class=\"fm-code-in-text\">UseExceptionHandler<\/code> method that allow more complex responses to be composed, but my advice is to keep error handling as simple as possible because you can\u2019t anticipate all of the problems an application may encounter, and you run the risk of encountering another exception when trying to handle the one that triggered the handler, resulting in a confusing response or no response at all.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-315\">16.6.2 Enriching status code responses<\/h3>\n<p class=\"body\">Not all error responses will be the result of uncaught exceptions. Some requests cannot be processed for reasons other than software defects, such as requests for URLs that are not supported or that require authentication. For this type of problem, redirecting the client to a different URL can be problematic because some clients rely on the error code to detect problems. You will see examples of this in later chapters when I show you how to create and consume RESTful web applications.<a id=\"calibre_link-1931\"><\/a><a id=\"calibre_link-1932\"><\/a><a id=\"calibre_link-1933\"><\/a><a id=\"calibre_link-951\"><\/a><\/p>\n<p class=\"body\">ASP.NET Core provides middleware that adds user-friendly content to error responses without requiring redirection. This preserves the error status code while providing a human-readable message that helps users make sense of the problem.<\/p>\n<p class=\"body\">The simplest approach is to define a string that will be used as the body for the response. This is more awkward than simply pointing at a file, but it is a more reliable technique, and as a rule, simple and reliable techniques are preferable when handling errors. To create the string response for the example project, add a class file named <code class=\"fm-code-in-text\">ResponseStrings.cs<\/code> to the <code class=\"fm-code-in-text\">Platform<\/code> folder with the code shown in listing 16.19.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.19 The contents of the ResponseStrings.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n\n    public static class Responses {\n        \n        public static string DefaultResponse = @\"\n        &lt;!DOCTYPE html&gt;\n            &lt;html lang=\"\"en\"\"&gt;\n            &lt;head&gt;\n                &lt;link rel=\"\"stylesheet\"\" \n                   href=\"\"\/lib\/bootstrap\/css\/bootstrap.min.css\"\" \/&gt;\n                &lt;title&gt;Error&lt;\/title&gt;\n            &lt;\/head&gt;\n            &lt;body class=\"\"text-center\"\"&gt;\n                &lt;h3 class=\"\"p-2\"\"&gt;Error {0}&lt;\/h3&gt;\n                &lt;h6&gt;\n                    You can go back to the &lt;a href=\"\"\/\"\"&gt;homepage&lt;\/a&gt; \n                        and try again\n                &lt;\/h6&gt;\n            &lt;\/body&gt;\n        &lt;\/html&gt;\";\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Responses<\/code> class defines a <code class=\"fm-code-in-text\">DefaultResponse<\/code> property to which I have assigned a multiline string containing a simple HTML document. There is a placeholder&mdash;<code class=\"fm-code-in-text\">{0}<\/code>&mdash;into which the response status code will be inserted when the response is sent to the client.<\/p>\n<p class=\"body\">Listing 16.20 adds the status code middleware to the request pipeline and adds a new middleware component that will return a 404 status code, indicating that the requested URL was not found.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.20 Adding middleware in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\nif (!app.Environment.IsDevelopment()) {\n    app.UseExceptionHandler(\"\/error.xhtml\");\n    app.UseStaticFiles();\n}\n\n<b class=\"fm-bold\">app.UseStatusCodePages(\"text\/html\", Platform.Responses.DefaultResponse);<\/b>\n\n<b class=\"fm-bold\">app.Use(async (context, next) =&gt; {<\/b>\n    <b class=\"fm-bold\">if (context.Request.Path == \"\/error\") {<\/b>\n        <b class=\"fm-bold\">context.Response.StatusCode = StatusCodes.Status404NotFound;<\/b>\n        <b class=\"fm-bold\">await Task.CompletedTask;<\/b>\n    <b class=\"fm-bold\">} else {<\/b>\n        <b class=\"fm-bold\">await next();<\/b>\n    <b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.Run(context =&gt; {\n    throw new Exception(\"Something has gone wrong\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UseStatusCodePages<\/code> method adds the response-enriching middleware to the request pipeline. The first argument is the value that will be used for the response\u2019s <code class=\"fm-code-in-text\">Content-Type<\/code> header, which is <code class=\"fm-code-in-text\">text\/html<\/code> in this example. The second argument is the string that will be used as the body of the response, which is the HTML string from listing 16.19.<\/p>\n<p class=\"body\">The custom middleware component sets the <code class=\"fm-code-in-text\">HttpResponse.StatusCode<\/code> property to specify the status code for the response, using a value defined by the <code class=\"fm-code-in-text\">StatusCode<\/code> class. Middleware components are required to return a <code class=\"fm-code-in-text\">Task<\/code>, so I have used the <code class=\"fm-code-in-text\">Task.CompletedTask<\/code> property because there is no work for this middleware component to do.<\/p>\n<p class=\"body\">To see how the 404 status code is handled, restart ASP.NET Core and request http:\/\/localhost:5000\/error. The status code middleware will intercept the result and add the content shown in figure 16.13 to the response. The string used as the second argument to <code class=\"fm-code-in-text\">UseStatusCodePages<\/code> is interpolated using the status code to resolve the placeholder.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre157\" src=\"\/images\/proaspnetcore7\/000159.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.13 Using the status code middleware<\/p>\n<\/div>\n<p class=\"body\">The status code middleware responds only to status codes between 400 and 600 and doesn\u2019t alter responses that already contain content, which means you won\u2019t see the response in the figure if an error occurs after another middleware component has started to generate a response. The status code middleware won\u2019t respond to unhandled exceptions because exceptions disrupt the flow of a request through the pipeline, meaning that the status code middleware isn\u2019t given the opportunity to inspect the response before it is sent to the client. As a result, the <code class=\"fm-code-in-text\">UseStatusCodePages<\/code> method is typically used in conjunction with the <code class=\"fm-code-in-text\">UseExceptionHandler<\/code> or <code class=\"fm-code-in-text\">UseDeveloperExceptionPage<\/code> method.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> There are two related methods, <code class=\"fm-code-in-text1\">UseStatusCodePagesWithRedirects<\/code> and <code class=\"fm-code-in-text1\">UseStatusCodePagesWithReExecute<\/code>, which work by redirecting the client to a different URL or by rerunning the request through the pipeline with a different URL. In both cases, the original status code may be lost.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-316\">16.7 Filtering requests using the host header<\/h2>\n<p class=\"body\">The HTTP specification requires requests to include a <code class=\"fm-code-in-text\">Host<\/code> header that specifies the hostname the request is intended for, which makes it possible to support virtual servers where one HTTP server receives requests on a single port and handles them differently based on the hostname that was requested.<a id=\"calibre_link-1934\"><\/a><a id=\"calibre_link-1935\"><\/a><a id=\"calibre_link-1002\"><\/a><\/p>\n<p class=\"body\">The default set of middleware that is added to the request pipeline by the <code class=\"fm-code-in-text\">Program<\/code> class includes middleware that filters requests based on the <code class=\"fm-code-in-text\">Host<\/code> header so that only requests that target a list of approved hostnames are handled and all other requests are rejected.<\/p>\n<p class=\"body\">The default configuration for the <code class=\"fm-code-in-text\">Hosts<\/code> header middleware is included in the <code class=\"fm-code-in-text\">appsettings.json<\/code> file, as follows:<a id=\"calibre_link-1936\"><\/a><\/p>\n<pre class=\"programlisting\">...\n{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  <b class=\"fm-bold\">\"AllowedHosts\": \"*\",<\/b>\n  \"Location\": {\n    \"CityName\": \"Buffalo\"\n  }\n}\n...<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1026\"><\/a>The <code class=\"fm-code-in-text\">AllowedHosts<\/code> configuration property is added to the JSON file when the project is created, and the default value accepts requests regardless of the <code class=\"fm-code-in-text\">Host<\/code> header value. You can change the configuration by editing the JSON file. The configuration can also be changed using the options pattern, as shown in listing 16.21.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The middleware is added to the pipeline by default, but you can use the <code class=\"fm-code-in-text1\">UseHostFiltering<\/code> method if you need to add the middleware explicitly.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 16.21 Configuring host filtering in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Microsoft.AspNetCore.HostFiltering;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.Configure&lt;HostFilteringOptions&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.AllowedHosts.Clear();<\/b>\n    <b class=\"fm-bold\">opts.AllowedHosts.Add(\"*.example.com\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\nif (!app.Environment.IsDevelopment()) {\n    app.UseExceptionHandler(\"\/error.xhtml\");\n    app.UseStaticFiles();\n}\n\napp.UseStatusCodePages(\"text\/html\", Platform.Responses.DefaultResponse);\n\napp.Use(async (context, next) =&gt; {\n    if (context.Request.Path == \"\/error\") {\n        context.Response.StatusCode = StatusCodes.Status404NotFound;\n        await Task.CompletedTask;\n    } else {\n        await next();\n    }\n});\n\napp.Run(context =&gt; {\n    throw new Exception(\"Something has gone wrong\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HostFilteringOptions<\/code> class is used to configure the host filtering middleware using the properties described in table 16.11.<\/p>\n<p class=\"fm-table-caption\">Table 16.11 The HostFilteringOptions properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1937\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AllowedHosts<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">List&lt;string&gt;<\/code> that contains the domains for which requests are allowed. Wildcards are allowed so that <code class=\"fm-code-in-text1\">*.example.com<\/code> accepts all names in the <code class=\"fm-code-in-text1\">example.com<\/code> domain and <code class=\"fm-code-in-text1\">*<\/code> accepts all header values.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AllowEmptyHosts<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">When <code class=\"fm-code-in-text1\">false<\/code>, this property tells the middleware to reject requests that do not contain a <code class=\"fm-code-in-text1\">Host<\/code> header. The default value is <code class=\"fm-code-in-text1\">true<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IncludeFailureMessage<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">When <code class=\"fm-code-in-text1\">true<\/code>, this property includes a message in the response that indicates the reason for the error. The default value is <code class=\"fm-code-in-text1\">true<\/code>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">In listing 16.21, I called the <code class=\"fm-code-in-text\">Clear<\/code> method to remove the wildcard entry that has been loaded from the <code class=\"fm-code-in-text\">appsettings.json<\/code> file and then called the <code class=\"fm-code-in-text\">Add<\/code> method to accept all hosts in the <code class=\"fm-code-in-text\">example.com<\/code> domain. Requests sent from the browser to localhost will no longer contain an acceptable <code class=\"fm-code-in-text\">Host<\/code> header. You can see what happens by restarting ASP.NET Core and using the browser to request http:\/\/localhost:5000. The <code class=\"fm-code-in-text\">Host<\/code> header middleware checks the <code class=\"fm-code-in-text\">Host<\/code> header in the request, determines that the request hostname doesn\u2019t match the <code class=\"fm-code-in-text\">AllowedHosts<\/code> list, and terminates the request with the 400 status code, which indicates a bad request. Figure 16.14 shows the error message.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre158\" src=\"\/images\/proaspnetcore7\/000160.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 16.14 A request rejected based on the Host header<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-317\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core provides support for adding cookies to responses and reading those cookies when the client includes them in subsequent requests.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Sessions allow related requests to be identified to provide continuity across requests.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core supports HTTPS requests and can be configured to disallow regular HTTP requests.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Limits can be applied to the rate of requests handled by endpoints.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-318\">\n<div class=\"calibre1\" id=\"calibre_link-1938\">\n<h1 class=\"tochead\" id=\"calibre_link-1939\"><a id=\"calibre_link-1940\"><\/a><a id=\"calibre_link-1941\"><\/a>17 Working with data<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-1942\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Caching data values or complete responses using ASP.NET Core<\/li>\n<li class=\"co-summary-bullet\">Working with Entity Framework Core to access data in a relational database<\/li>\n<\/ul>\n<p class=\"body\">All the examples in the earlier chapters in this part of the book have generated fresh responses for each request, which is easy to do when dealing with simple strings or small fragments of HTML. Most real projects deal with data that is expensive to produce and needs to be used as efficiently as possible. In this chapter, I describe the features that ASP.NET Core provides for caching data and caching entire responses. I also show you how to create and configure the services required to access data in a database using Entity Framework Core. Table 17.1 puts the ASP.NET Core features for working with data in context.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The examples in this chapter rely on the SQL Server LocalDB feature that was installed in chapter 2. You will encounter errors if you have not installed LocalDB and the required updates.<\/p>\n<p class=\"fm-table-caption\">Table 17.1 Putting the ASP.NET Core data features in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1943\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The features described in this chapter allow responses to be produced using data that has been previously created, either because it was created for an earlier request or because it has been stored in a database.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Most web applications deal with data that is expensive to re-create for every request. The features in this chapter allow responses to be produced more efficiently and with fewer resources.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Data values are cached using a service. Responses are cached by a middleware component based on the <code class=\"fm-code-in-text1\">Cache-Control<\/code> header. Databases are accessed through a service that translates LINQ queries into SQL statements.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">For caching, it is important to test the effect of your cache policy before deploying the application to ensure you have found the right balance between efficiency and responsiveness. For Entity Framework Core, it is important to pay attention to the queries sent to the database to ensure that they are not retrieving large amounts of data that is processed and then discarded by the application.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">All the features described in this chapter are optional. You can elect not to cache data or responses or to use an external cache. You can choose not to use a database or to access a database using a framework other than Entity Framework Core.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 17.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 17.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1944\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Caching data values<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Set up a cache service and use it in endpoints and middleware components to store data values.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">3&ndash;6<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a persistent cache<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the database-backed cache.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">7&ndash;13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Caching entire responses<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Enable the response or output caching middleware.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14-19<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Storing application data<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use Entity Framework Core.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">20-26, 29-31<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a database schema<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create and apply migrations.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">27, 28<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Accessing data in endpoints<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Consume the database context service.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">32<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Including all request details in logging messages<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Enable the sensitive data logging feature.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">33<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-319\">17.1 Preparing for this chapter<\/h2>\n<p class=\"body\">In this chapter, I continue to use the <code class=\"fm-code-in-text\">Platform<\/code> project from chapter 16. To prepare for this chapter, replace the contents of the <code class=\"fm-code-in-text\">Program.cs<\/code> file with the code shown in listing 17.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.1 Replacing the contents of the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">Start the application by opening a new PowerShell command prompt, navigating to the <code class=\"fm-code-in-text\">Platform<\/code> project folder (which contains the <code class=\"fm-code-in-text\">Platform.csproj<\/code> file), and running the command shown in listing 17.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.2 Starting the ASP.NET Core runtime<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Open a new browser tab, navigate to https:\/\/localhost:5000, and you will see the content shown in figure 17.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre159\" src=\"\/images\/proaspnetcore7\/000161.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 17.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-320\">17.2 Caching data<\/h2>\n<p class=\"body\">In most web applications, there will be some items of data that are relatively expensive to generate but are required repeatedly. The exact nature of the data is specific to each project, but repeatedly performing the same set of calculations can increase the resources required to host the application. To represent an expensive response, add a class file called <code class=\"fm-code-in-text\">SumEndpoint.cs<\/code> to the <code class=\"fm-code-in-text\">Platform<\/code> folder with the code shown in listing 17.3.<a id=\"calibre_link-1945\"><\/a><a id=\"calibre_link-1946\"><\/a><a id=\"calibre_link-865\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.3 The contents of the SumEndpoint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">namespace Platform {\n\n    public class SumEndpoint {\n        \n        public async Task Endpoint(HttpContext context) {\n            int count;\n            int.TryParse((string?)context.Request.RouteValues[\"count\"],\n                out count);\n            long total = 0;\n            for (int i = 1; i &lt;= count; i++) {\n                total += i;\n            }\n            string totalString = $\"({DateTime.Now.ToLongTimeString()}) \"\n                + total;\n            await context.Response.WriteAsync(\n                $\"({DateTime.Now.ToLongTimeString()}) Total for {count}\"\n                + $\" values:\\n{totalString}\\n\");\n        }\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1947\"><\/a>Listing 17.4 creates a route that uses the endpoint, which is applied using the <code class=\"fm-code-in-text\">MapEndpoint<\/code> extension methods created in chapter 14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.4 Adding an endpoint in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapEndpoint&lt;Platform.SumEndpoint&gt;(\"\/sum\/{count:int=1000000000}\");<\/b>\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/sum. The endpoint will sum 1,000,000,000 integer values and produce the result shown in figure 17.2.<\/p>\n<p class=\"body\">Reload the browser window, and the endpoint will repeat the calculation. Both the timestamps change in the response, as shown in the figure, indicating that every part of the response was produced fresh for each request.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You may need to increase or decrease the default value for the route parameter based on the capabilities of your machine. Try to find a value that takes two or three seconds to produce the result&mdash;just long enough that you can tell when the calculation is being performed but not so long that you can step out for coffee while it happens.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre160\" src=\"\/images\/proaspnetcore7\/000162.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 17.2 An expensive response<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-321\">17.2.1 Caching data values<\/h3>\n<p class=\"body\">ASP.NET Core provides a service that can be used to cache data values through the <code class=\"fm-code-in-text\">IDistributedCache<\/code> interface. Listing 17.5 revises the endpoint to declare a dependency on the service and use it to cache calculated values.<a id=\"calibre_link-1948\"><\/a><a id=\"calibre_link-1142\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.5 Using the cache service in the SumEndpoint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Microsoft.Extensions.Caching.Distributed;<\/b>\n\nnamespace Platform {\n\n    public class SumEndpoint {\n        \n        <b class=\"fm-bold\">public async Task Endpoint(HttpContext context,<\/b> \n                <b class=\"fm-bold\">IDistributedCache cache) {<\/b>\n            int count;\n            int.TryParse((string?)context.Request.RouteValues[\"count\"],\n                out count);\n            <b class=\"fm-bold\">string cacheKey = $\"sum_{count}\";<\/b>\n            <b class=\"fm-bold\">string? totalString = await cache.GetStringAsync(cacheKey);<\/b>\n            <b class=\"fm-bold\">if (totalString == null) {<\/b>\n                long total = 0;\n                for (int i = 1; i &lt;= count; i++) {\n                    total += i;\n                }\n                totalString = $\"({DateTime.Now.ToLongTimeString()}) \" \n                    + total;\n                <b class=\"fm-bold\">await cache.SetStringAsync(cacheKey, totalString,<\/b>\n                    <b class=\"fm-bold\">new DistributedCacheEntryOptions {<\/b>\n                        <b class=\"fm-bold\">AbsoluteExpirationRelativeToNow =<\/b> \n                            <b class=\"fm-bold\">TimeSpan.FromMinutes(2)<\/b>\n                    <b class=\"fm-bold\">});<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            await context.Response.WriteAsync(\n                $\"({DateTime.Now.ToLongTimeString()}) Total for {count}\"\n                + $\" values:\\n{totalString}\\n\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The cache service can store only byte arrays, which can be restrictive but allows for a range of <code class=\"fm-code-in-text\">IDistributedCache<\/code> implementations to be used. There are extension methods available that allow strings to be used, which is a more convenient way of caching most data. Table 17.3 describes the most useful methods for using the cache.<a id=\"calibre_link-1949\"><\/a><a id=\"calibre_link-1143\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 17.3 Useful IDistributedCache methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1950\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetString(key)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns the cached string associated with the specified key, or <code class=\"fm-code-in-text1\">null<\/code> if there is no such item.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetStringAsync(key)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns a <code class=\"fm-code-in-text1\">Task&lt;string&gt;<\/code> that produces the cached string associated with the key, or <code class=\"fm-code-in-text1\">null<\/code> if there is no such item.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetString(key, value, options)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method stores a string in the cache using the specified key. The cache entry can be configured with an optional <code class=\"fm-code-in-text1\">DistributedCacheEntryOptions<\/code> object.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetStringAsync(key, value, options)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asynchronously stores a string in the cache using the specified key. The cache entry can be configured with an optional <code class=\"fm-code-in-text1\">DistributedCacheEntryOptions<\/code> object.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Refresh(key)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method resets the expiry interval for the value associated with the key, preventing it from being flushed from the cache.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RefreshAsync(key)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asynchronously resets the expiry interval for the value associated with the key, preventing it from being flushed from the cache.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Remove(key)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method removes the cached item associated with the key.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RemoveAsync(key)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method asynchronously removes the cached item associated with the key.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">By default, entries remain in the cache indefinitely, but the <code class=\"fm-code-in-text\">SetString<\/code> and <code class=\"fm-code-in-text\">SetStringAsync<\/code> methods accept an optional <code class=\"fm-code-in-text\">DistributedCacheEntryOptions<\/code> argument that is used to set an expiry policy, which tells the cache when to eject the item. Table 17.4 shows the properties defined by the <code class=\"fm-code-in-text\">DistributedCacheEntryOptions<\/code> class.<a id=\"calibre_link-1951\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 17.4 The DistributedCacheEntryOptions properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1952\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AbsoluteExpiration<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to specify an absolute expiry date.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AbsoluteExpirationRelativeToNow<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to specify a relative expiry date.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SlidingExpiration<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to specify a period of inactivity, after which the item will be ejected from the cache if it hasn\u2019t been read.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">In listing 17.5, the endpoint uses the <code class=\"fm-code-in-text\">GetStringAsync<\/code> to see whether there is a cached result available from a previous request. If there is no cached value, the endpoint performs the calculation and caches the result using the <code class=\"fm-code-in-text\">SetStringAsync<\/code> method, with the <code class=\"fm-code-in-text\">AbsoluteExpirationRelativeToNow<\/code> property to tell the cache to eject the item after two minutes.<\/p>\n<pre class=\"programlisting\">...\nawait cache.SetStringAsync(cacheKey, totalStr,\n    <b class=\"fm-bold\">new DistributedCacheEntryOptions {<\/b>\n        <b class=\"fm-bold\">AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(2)<\/b>\n    <b class=\"fm-bold\">});<\/b>\n...<\/pre>\n<p class=\"body\">The next step is to set up the cache service, as shown in listing 17.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.6 Adding a service in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddDistributedMemoryCache(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.SizeLimit = 200;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.MapEndpoint&lt;Platform.SumEndpoint&gt;(\"\/sum\/{count:int=1000000000}\");\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\"><code class=\"fm-code-in-text\">AddDistributedMemoryCache<\/code> is the same method I used in chapter 16 to provide the data store for session data. This is one of the three methods used to select an implementation for the <code class=\"fm-code-in-text\">IDistributedCache<\/code> service, as described in table 17.5.<a id=\"calibre_link-1953\"><\/a><a id=\"calibre_link-1954\"><\/a><a id=\"calibre_link-1955\"><\/a><a id=\"calibre_link-1956\"><\/a><a id=\"calibre_link-1957\"><\/a><a id=\"calibre_link-1114\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 17.5 The cache service implementation methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1958\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddDistributedMemoryCache<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sets up an in-memory cache.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddDistributedSqlServerCache<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sets up a cache that stores data in SQL Server and is available when the <code class=\"fm-code-in-text1\">Microsoft.Extensions.Caching.SqlServer<\/code> package is installed. See the \"Caching Responses\" section for details.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddStackExchangeRedisCache<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sets up a Redis cache and is available when the <code class=\"fm-code-in-text1\">Microsoft.Extensions.Caching.Redis<\/code> package is installed.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Listing 17.6 uses the <code class=\"fm-code-in-text\">AddDistributedMemoryCache<\/code> method to create an in-memory cache as the implementation for the <code class=\"fm-code-in-text\">IDistributedCache<\/code> service. This cache is configured using the <code class=\"fm-code-in-text\">MemoryDistributedCacheOptions<\/code> class, whose most useful properties are described in table 17.6.<a id=\"calibre_link-1959\"><\/a><a id=\"calibre_link-1144\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 17.6 Useful MemoryCacheOptions properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1960\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExpirationScanFrequency<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to set a <code class=\"fm-code-in-text1\">TimeSpan<\/code> that determines how often the cache scans for expired items.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SizeLimit<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies the maximum number of items in the cache. When the size is reached, the cache will eject items.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">CompactionPercentage<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies the percentage by which the size of the cache is reduced when <code class=\"fm-code-in-text1\">SizeLimit<\/code> is reached.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The statement in listing 17.6 uses the <code class=\"fm-code-in-text\">SizeLimit<\/code> property to restrict the cache to 200 items. Care must be taken when using an in-memory cache to find the right balance between allocating enough memory for the cache to be effective without exhausting server resources.<\/p>\n<p class=\"body\">To see the effect of the cache, restart ASP.NET Core and request the http:\/\/localhost:5000\/sum URL. Reload the browser, and you will see that only one of the timestamps will change, as shown in figure 17.3. This is because the cache has provided the calculation response, which allows the endpoint to produce the result without having to repeat the calculation.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre161\" src=\"\/images\/proaspnetcore7\/000163.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 17.3 Caching data values<\/p>\n<\/div>\n<p class=\"body\">If you wait for two minutes and then reload the browser, then both timestamps will change because the cached result will have been ejected, and the endpoint will have to perform the calculation to produce the result.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-322\">17.2.2 Using a shared and persistent data cache<\/h3>\n<p class=\"body\">The cache created by the <code class=\"fm-code-in-text\">AddDistributedMemoryCache<\/code> method isn\u2019t distributed, despite the name. The items are stored in memory as part of the ASP.NET Core process, which means that applications that run on multiple servers or containers don\u2019t share cached data. It also means that the contents of the cache are lost when ASP.NET Core is stopped.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddDistributedSqlServerCache<\/code> method stores the cache data in a SQL Server database, which can be shared between multiple ASP.NET Core servers and which stores the data persistently.<a id=\"calibre_link-1961\"><\/a><a id=\"calibre_link-1962\"><\/a><a id=\"calibre_link-880\"><\/a><\/p>\n<p class=\"body\">The first step is to create a database that will be used to store the cached data. You can store the cached data alongside the application\u2019s other data, but for this chapter, I am going to use a separate database, which will be named <code class=\"fm-code-in-text\">CacheDb<\/code>. You can create the database using Azure Data Studio or SQL Server Management Studio, both of which are available for free from Microsoft. Databases can also be created from the command line using <code class=\"fm-code-in-text\">sqlcmd<\/code>. Open a new PowerShell command prompt and run the command shown in listing 17.7 to connect to the LocalDB server.<a id=\"calibre_link-1963\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">sqlcmd<\/code> tool should have been installed as part of the Visual Studio workload or as part of the SQL Server Express installation. If it has not been installed, then you can download an installer from <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/tools\/sqlcmd-utility\">https:\/\/docs.microsoft.com\/en-us\/sql\/tools\/sqlcmd-utility<\/a>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.7 Connecting to the database server<\/p>\n<pre class=\"programlisting\">sqlcmd -S \"(localdb)\\MSSQLLocalDB\"<\/pre>\n<p class=\"body\">Pay close attention to the argument that specifies the database. There is one backslash, which is followed by <code class=\"fm-code-in-text\">MSSQLLocalDB<\/code>. It can be hard to spot the repeated letters: M<b class=\"fm-bold\">S<\/b>-<b class=\"fm-bold\">S<\/b>Q<b class=\"fm-bold\">L<\/b>-<b class=\"fm-bold\">L<\/b>ocalDB (but without the hyphens).<a id=\"calibre_link-1964\"><\/a><\/p>\n<p class=\"body\">When the connection has been established, you will see a <code class=\"fm-code-in-text\">1&gt;<\/code> prompt. Enter the commands shown in listing 17.8 and press the Enter key after each command.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> If you are using Visual Studio, you must apply the updates for SQL Server described in chapter 2. The version of SQL Server that is installed by default when you install Visual Studio cannot create LocalDB databases.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.8 Creating the database<\/p>\n<pre class=\"programlisting\">CREATE DATABASE CacheDb\nGO<\/pre>\n<p class=\"body\">If no errors are reported, then enter <code class=\"fm-code-in-text\">exit<\/code> and press Enter to terminate the connection.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> If you need to reset the cache database, use the command in listing 17.7 to open a connection and use the command <code class=\"fm-code-in-text1\">DROP DATABASE CacheDB<\/code>. You can then re-create the database using the commands in listing 17.8.<\/p>\n<p class=\"body\">Run the commands shown in listing 17.9 to install the package required to create a cache<a id=\"calibre_link-1965\"><\/a>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.9 Installing the SQL cache package<\/p>\n<pre class=\"programlisting\">dotnet tool uninstall --global dotnet-sql-cache\ndotnet tool install --global dotnet-sql-cache --version 7.0.0<\/pre>\n<p class=\"body\">The first command removes any existing version of the <code class=\"fm-code-in-text\">dotnet-sql-cache<\/code> package, and the second command installs the version required for the examples in this book. The next step is to run the command shown in listing 17.10 to create a table in the new database, using the command installed by the <code class=\"fm-code-in-text\">dotnet-sql-cache<\/code> package.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.10 Creating the cache database table<\/p>\n<pre class=\"programlisting\">dotnet sql-cache create \"Server=(localdb)\\MSSQLLocalDB;Database=CacheDb\"\ndbo DataCache<\/pre>\n<p class=\"body\">The arguments for this command are the connection string that specifies the database, the schema, and the name of the table that will be used to store the cached data. Enter the command on a single line and press Enter. It will take a few seconds for the tool to connect to the database. If the process is successful, you will see the following message:<\/p>\n<pre class=\"programlisting\">Table and index were created successfully.<\/pre>\n<p class=\"fm-head2\">Creating the persistent cache service<\/p>\n<p class=\"body\">Now that the database is ready, I can create the service that will use it to store cached data. To add the NuGet package required for SQL Server caching support, open a new PowerShell command prompt, navigate to the <code class=\"fm-code-in-text\">Platform<\/code> project folder, and run the command shown in listing 17.11. (If you are using Visual Studio, you can add the package by selecting Project &gt; Manage NuGet Packages.)<a id=\"calibre_link-1966\"><\/a><a id=\"calibre_link-1145\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.11 Adding a package to the project<\/p>\n<pre class=\"programlisting\">dotnet add package Microsoft.Extensions.Caching.SqlServer --version 7.0.0 <\/pre>\n<p class=\"body\">The next step is to define a connection string, which describes the database connection in the JSON configuration file, as shown in listing 17.12.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The cache created by the <code class=\"fm-code-in-text1\">AddDistributedSqlServerCache<\/code> method is distributed, meaning that multiple applications can use the same database and share cache data. If you are deploying the same application to multiple servers or containers, all instances will be able to share cached data. If you are sharing a cache between different applications, then you should pay close attention to the keys you use to ensure that applications receive the data types they expect.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.12 A connection string in the appsettings.json file in the Platform folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\",\n  \"Location\": {\n    \"CityName\": \"Buffalo\"\n  },\n  <b class=\"fm-bold\">\"ConnectionStrings\": {<\/b>\n    <b class=\"fm-bold\">\"CacheConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=CacheDb\"<\/b>\n  <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">Notice that the connection string uses two backslash characters (<code class=\"fm-code-in-text\">\\\\<\/code>) to escape the character in the JSON file. Listing 17.13 changes the implementation for the cache service to use SQL Server with the connection string from listing 17.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.13 Using a persistent data cache in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">var builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">\/\/builder.Services.AddDistributedMemoryCache(opts =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/    opts.SizeLimit = 200;<\/b>\n<b class=\"fm-bold\">\/\/});<\/b>\n\n<b class=\"fm-bold\">builder.Services.AddDistributedSqlServerCache(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.ConnectionString<\/b>\n        <b class=\"fm-bold\">= builder.Configuration[\"ConnectionStrings:CacheConnection\"];<\/b>\n    <b class=\"fm-bold\">opts.SchemaName = \"dbo\";<\/b>\n    <b class=\"fm-bold\">opts.TableName = \"DataCache\";<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.MapEndpoint&lt;Platform.SumEndpoint&gt;(\"\/sum\/{count:int=1000000000}\");\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">IConfiguration<\/code> service is used to access the connection string from the application\u2019s configuration data. The cache service is created using the <code class=\"fm-code-in-text\">AddDistributedSqlServerCache<\/code> method and is configured using an instance of the <code class=\"fm-code-in-text\">SqlServerCacheOptions<\/code> class, whose most useful properties are described in table 17.7.<a id=\"calibre_link-1967\"><\/a><a id=\"calibre_link-1146\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 17.7 Useful SqlServerCacheOptions properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1968\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ConnectionString<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies the connection string, which is conventionally stored in the JSON configuration file and accessed through the <code class=\"fm-code-in-text1\">IConfguration<\/code> service.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SchemaName<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies the schema name for the cache table.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">TableName<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies the name of the cache table.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExpiredItemsDeletionInterval<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies how often the table is scanned for expired items. The default is 30 minutes.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">DefaultSlidingExpiration<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies how long an item remains unread in the cache before it expires. The default is 20 minutes.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The listing uses the <code class=\"fm-code-in-text\">ConnectionString<\/code>, <code class=\"fm-code-in-text\">SchemaName<\/code>, and <code class=\"fm-code-in-text\">TableName<\/code> properties to configure the cache middleware to use the database table. Restart ASP.NET Core and use a browser to request the http:\/\/localhost:5000\/sum URL. There is no change in the response produced by the application, which is shown in figure 17.4, but you will find that the cached responses are persistent and will be used even when you restart ASP.NET Core.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Caching session-specific data values<\/p>\n<p class=\"fm-sidebar-text\">When you use the <code class=\"fm-code-in-text1\">IDistributedCache<\/code> service, the data values are shared between all requests. If you want to cache different data values for each user, then you can use the session middleware described in chapter 16. The session middleware relies on the <code class=\"fm-code-in-text1\">IDistributedCache<\/code> service to store its data, which means that session data will be stored persistently and available to a distributed application when the <code class=\"fm-code-in-text1\">AddDistributedSqlServerCache<\/code> method is used.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-323\">17.3 Caching responses<\/h2>\n<p class=\"body\">An alternative to caching individual data items is to cache entire responses, which can be a useful approach if a response is expensive to compose and is likely to be repeated. Caching responses requires the addition of a service and a middleware component, as shown in listing 17.14.<a id=\"calibre_link-1969\"><\/a><a id=\"calibre_link-1970\"><\/a><a id=\"calibre_link-1029\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.14 Configuring Caching in the Program.cs File in the Platform Folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Platform.Services;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDistributedSqlServerCache(opts =&gt; {\n    opts.ConnectionString\n        = builder.Configuration[\"ConnectionStrings:CacheConnection\"];\n    opts.SchemaName = \"dbo\";\n    opts.TableName = \"DataCache\";\n});\n\n<b class=\"fm-bold\">builder.Services.AddResponseCaching();<\/b>\n<b class=\"fm-bold\">builder.Services.AddSingleton&lt;IResponseFormatter,<\/b> \n    <b class=\"fm-bold\">HtmlResponseFormatter&gt;();<\/b>\n        \nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.UseResponseCaching();<\/b>\n\napp.MapEndpoint&lt;Platform.SumEndpoint&gt;(\"\/sum\/{count:int=1000000000}\");\n\napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddResponseCaching<\/code> method is used to set up the service used by the cache. The middleware component is added with the <code class=\"fm-code-in-text\">UseResponseCaching<\/code> method, which should be called before any endpoint or middleware that needs its responses cached.<a id=\"calibre_link-1971\"><\/a><a id=\"calibre_link-1972\"><\/a><\/p>\n<p class=\"body\"><a id=\"calibre_link-1147\"><\/a>I have also defined the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service, which I used to explain how dependency injection works in chapter 14. Response caching is used only in certain circumstances, and, as I explain shortly, demonstrating the feature requires an HTML response.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The response caching feature does not use the <code class=\"fm-code-in-text1\">IDistributedCache<\/code> service. Responses are cached in memory and are not distributed.<\/p>\n<p class=\"body\">In listing 17.15, I have updated the <code class=\"fm-code-in-text\">SumEndpoint<\/code> class so that it requests response caching instead of caching just a data value.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.15 Using response caching in the SumEndpoint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">\/\/using Microsoft.Extensions.Caching.Distributed;<\/b>\n<b class=\"fm-bold\">using Platform.Services;<\/b>\n\nnamespace Platform {\n\n    public class SumEndpoint {\n        \n        <b class=\"fm-bold\">public async Task Endpoint(HttpContext context,<\/b> \n                <b class=\"fm-bold\">IResponseFormatter formatter, LinkGenerator generator) {<\/b>\n                                \n            int count;\n            int.TryParse((string?)context.Request.RouteValues[\"count\"],\n                out count);\n            <b class=\"fm-bold\">long total = 0;<\/b>\n            <b class=\"fm-bold\">for (int i = 1; i &lt;= count; i++) {<\/b>\n                <b class=\"fm-bold\">total += i;<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">string totalString = $\"({DateTime.Now.ToLongTimeString()}) \"<\/b> \n                <b class=\"fm-bold\">+ total;<\/b>\n                                \n            <b class=\"fm-bold\">context.Response.Headers[\"Cache-Control\"]<\/b> \n                <b class=\"fm-bold\">= \"public, max-age=120\";<\/b>\n                                \n            <b class=\"fm-bold\">string? url = generator.GetPathByRouteValues(context, null,<\/b>\n                <b class=\"fm-bold\">new { count = count });<\/b>\n                                \n            <b class=\"fm-bold\">await formatter.Format(context,<\/b>\n                <b class=\"fm-bold\">$\"&lt;div&gt;({DateTime.Now.ToLongTimeString()}) Total for \"<\/b>\n                <b class=\"fm-bold\">+ $\"{count} values:&lt;\/div&gt;&lt;div&gt;{totalString}&lt;\/div&gt;\"<\/b>\n                <b class=\"fm-bold\">+ $\"&lt;a href={url}&gt;Reload&lt;\/a&gt;\");<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">Some of the changes to the endpoint enable response caching, but others are just to demonstrate that it is working. For enabling response caching, the important statement is the one that adds a header to the response, like this:<\/p>\n<pre class=\"programlisting\">...\ncontext.Response.Headers[\"Cache-Control\"] = \"public, max-age=120\";\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Cache-Control<\/code> header is used to control response caching. The middleware will only cache responses that have a <code class=\"fm-code-in-text\">Cache-Control<\/code> header that contains the <code class=\"fm-code-in-text\">public<\/code> directive. The <code class=\"fm-code-in-text\">max-age<\/code> directive is used to specify the period that the response can be cached for, expressed in seconds. The <code class=\"fm-code-in-text\">Cache-Control<\/code> header used in listing 17.15 enables caching and specifies that responses can be cached for two minutes.<a id=\"calibre_link-1973\"><\/a><a id=\"calibre_link-1030\"><\/a><\/p>\n<p class=\"body\">Enabling response caching is simple, but checking that it is working requires care. When you reload the browser window or press Return in the URL bar, browsers will include a <code class=\"fm-code-in-text\">Cache-Control<\/code> header in the request that sets the <code class=\"fm-code-in-text\">max-age<\/code> directive to zero, which bypasses the response cache and causes a new response to be generated by the endpoint. The only reliable way to request a URL without the <code class=\"fm-code-in-text\">Cache-Control<\/code> header is to navigate using an HTML anchor element, which is why the endpoint in listing 17.15 uses the <code class=\"fm-code-in-text\">IResponseFormatter<\/code> service to generate an HTML response and uses the <code class=\"fm-code-in-text\">LinkGenerator<\/code> service to create a URL that can be used in the anchor element\u2019s <code class=\"fm-code-in-text\">href<\/code> attribute.<\/p>\n<p class=\"body\">To check the response cache, restart ASP.NET Core and use the browser to request http:\/\/localhost:5000\/sum. Once the response has been generated, click the Reload link to request the same URL. You will see that neither of the timestamps in the response change, indicating that the entire response has been cached, as shown in figure 17.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre162\" src=\"\/images\/proaspnetcore7\/000164.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 17.4 Caching responses<\/p>\n<\/div>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Cache-Control<\/code> header can be combined with the <code class=\"fm-code-in-text\">Vary<\/code> header to provide fine-grained control over which requests are cached. See <a class=\"url\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Cache-Control\">https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Cache-Control<\/a> and <a class=\"url\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Vary\">https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Vary<\/a> for details of the features provided by both headers.<a id=\"calibre_link-1974\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Compressing responses<\/p>\n<p class=\"fm-sidebar-text\">ASP.NET Core includes middleware that will compress responses for browsers that have indicated they can handle compressed data. The middleware is added to the pipeline with the <code class=\"fm-code-in-text1\">UseResponseCompression<\/code> method. Compression is a trade-off between the server resources required for compression and the bandwidth required to deliver content to the client, and it should not be switched on without testing to determine the performance impact.<a id=\"calibre_link-1975\"><\/a><a id=\"calibre_link-1976\"><\/a><\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-324\">17.4 Caching output<\/h2>\n<p class=\"body\"><a id=\"calibre_link-1977\"><\/a>The use of the <code class=\"fm-code-in-text\">Cache-Control<\/code> header follows the intended design of HTTP but limits the benefits of caching responses. ASP.NET Core includes an alternative approach to caching, known as <i class=\"fm-italics\">output caching<\/i>, which overcomes these problems and provides a more configurable&mdash;albeit complex&mdash;set of features. Caching is applied to endpoints and to prepare for this example, I need to update the code written in chapter 14 so that it produces a result on which the caching extension method can be invoked, as shown in listing 17.16.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The output cache feature is more comprehensive than response caching but has its own limitations. There is an API for implementing persistent cache storage, for example, but no default implementation. The distributed cache used in earlier examples can\u2019t be used because output caching requires careful locking of cached data.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.16 A result in the EndpointExtensions.cs file in the Platform\/Services folder<\/p>\n<pre class=\"programlisting\">using System.Reflection;\n\nnamespace Microsoft.AspNetCore.Builder {\n\n    public static class EndpointExtensions {\n        \n        <b class=\"fm-bold\">public static IEndpointConventionBuilder MapEndpoint&lt;T&gt;(<\/b>\n                this IEndpointRouteBuilder app,\n                string path, string methodName = \"Endpoint\") {\n                                \n            MethodInfo? methodInfo = typeof(T).GetMethod(methodName);\n            if (methodInfo?.ReturnType != typeof(Task)) {\n                throw new System.Exception(\"Method cannot be used\");\n            }\n                        \n            ParameterInfo[] methodParams = methodInfo!.GetParameters();\n                        \n            <b class=\"fm-bold\">return app.MapGet(path, context =&gt; {<\/b>\n                T endpointInstance =\n                    ActivatorUtilities.CreateInstance&lt;T&gt;\n                        (context.RequestServices);\n                return (Task)methodInfo.Invoke(endpointInstance!,\n                    methodParams.Select(p =&gt;\n                    p.ParameterType == typeof(HttpContext)\n                    ? context\n                    : context.RequestServices.GetService(p.ParameterType))\n                        .ToArray())!;\n            });\n        }\n    }\n}<\/pre>\n<p class=\"body\">This change alters the <code class=\"fm-code-in-text\">MapEndpoint&lt;T&gt;<\/code> extension method to produce an implementation of the <code class=\"fm-code-in-text\">IEndpointConventionBuilder<\/code> interface, which is the result produced by the built-in <code class=\"fm-code-in-text\">MapGet<\/code> method and the other methods used to create endpoints. Listing 17.17 replaces the response caching from the previous section with output caching.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.17 Using output caching in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">\/\/builder.Services.AddDistributedSqlServerCache(opts =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/    opts.ConnectionString<\/b>\n<b class=\"fm-bold\">\/\/        = builder.Configuration[\"ConnectionStrings:CacheConnection\"];<\/b>\n<b class=\"fm-bold\">\/\/    opts.SchemaName = \"dbo\";<\/b>\n<b class=\"fm-bold\">\/\/    opts.TableName = \"DataCache\";<\/b>\n<b class=\"fm-bold\">\/\/});<\/b>\n\n<b class=\"fm-bold\">builder.Services.AddOutputCache();<\/b>\n\n\/\/builder.Services.AddResponseCaching();\nbuilder.Services.AddSingleton&lt;IResponseFormatter, \n    HtmlResponseFormatter&gt;();\n        \nvar app = builder.Build();\n\n<b class=\"fm-bold\">\/\/app.UseResponseCaching();<\/b>\n<b class=\"fm-bold\">app.UseOutputCache();<\/b>\n\n<b class=\"fm-bold\">app.MapEndpoint&lt;Platform<\/b>\n    <b class=\"fm-bold\">.SumEndpoint&gt;(\"\/sum\/{count:int=1000000000}\")<\/b>\n    <b class=\"fm-bold\">.CacheOutput();<\/b> \n        \napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">This is the simplest output caching configuration, and it is applied in three parts. First, the <code class=\"fm-code-in-text\">AddOutputCache<\/code> method is called to set up the services required for caching. Second, the <code class=\"fm-code-in-text\">UseOutputCache<\/code> method is called to register the caching middleware, which will short-circuit the pipeline where requests are received that can be handled using cached content. The final step enables caching for a specific endpoint, which is done using the <code class=\"fm-code-in-text\">CacheOutput<\/code> extension method:<\/p>\n<pre class=\"programlisting\">...\napp.MapEndpoint&lt;Platform\n    .SumEndpoint&gt;(\"\/sum\/{count:int=1000000000}\")\n    .<b class=\"fm-bold\">CacheOutput<\/b>();\n...<\/pre>\n<p class=\"body\">This extension method enables caching for the sum endpoint. The final step is to update the endpoint so that it doesn\u2019t set the caching header, as shown in listing 17.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.18 Disabling the header in the SumEndpoint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">\/\/using Microsoft.Extensions.Caching.Distributed;<\/b>\nusing Platform.Services;\n\nnamespace Platform {\n\n    public class SumEndpoint {\n        \n        public async Task Endpoint(HttpContext context, \n                IResponseFormatter formatter, LinkGenerator generator) {\n                                \n            int count;\n            int.TryParse((string?)context.Request.RouteValues[\"count\"],\n                out count);\n            long total = 0;\n            for (int i = 1; i &lt;= count; i++) {\n                total += i;\n            }\n            string totalString = $\"({DateTime.Now.ToLongTimeString()}) \" \n                + total;\n                                \n            <b class=\"fm-bold\">\/\/context.Response.Headers[\"Cache-Control\"]<\/b> \n            <b class=\"fm-bold\">\/\/    = \"public, max-age=120\";<\/b>\n                        \n            string? url = generator.GetPathByRouteValues(context, null,\n                new { count = count });\n                                \n            await formatter.Format(context,\n                $\"&lt;div&gt;({DateTime.Now.ToLongTimeString()}) Total for \"\n                + $\"{count} values:&lt;\/div&gt;&lt;div&gt;{totalString}&lt;\/div&gt;\"\n                + $\"&lt;a href={url}&gt;Reload&lt;\/a&gt;\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The endpoint doesn\u2019t need to manage the caching of its content directly, which means that the cache settings can be altered without needing to change the code that generates responses.<\/p>\n<p class=\"body\">The configuration used in listing 17.18 applies the default caching policy, which caches content for one minute and caches only HTTP GET or HEAD requests that produce HTTP 200 responses, and which are not authenticated or set cookies.<\/p>\n<p class=\"body\">To check the output cache, restart ASP.NET Core and use the browser to request http:\/\/localhost:5000\/sum. You will see the same output as in earlier examples, but now the cache is applied when the browser is reloaded and not just when the user clicks on the link, as shown in figure 17.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre163\" src=\"\/images\/proaspnetcore7\/000165.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 17.5 Output caching applies to all requests for a given URL<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-325\">17.4.1 Defining a custom cache policy<\/h3>\n<p class=\"body\">The output caching feature supports custom caching policies and allows different policies to be applied to endpoints. Caching policies are defined using the options pattern, as shown in listing 17.19.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.19 A custom configuration in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddOutputCache(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.AddBasePolicy(policy =&gt; {<\/b>\n        <b class=\"fm-bold\">policy.Cache();<\/b>\n        <b class=\"fm-bold\">policy.Expire(TimeSpan.FromSeconds(10));<\/b>\n    <b class=\"fm-bold\">});<\/b>\n    <b class=\"fm-bold\">opts.AddPolicy(\"30sec\", policy =&gt; {<\/b>\n        <b class=\"fm-bold\">policy.Cache();<\/b> \n        <b class=\"fm-bold\">policy.Expire(TimeSpan.FromSeconds(30));<\/b>\n    <b class=\"fm-bold\">});<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nbuilder.Services.AddSingleton&lt;IResponseFormatter, \n    HtmlResponseFormatter&gt;();\n        \nvar app = builder.Build();\n\napp.UseOutputCache();\n\napp.MapEndpoint&lt;Platform\n    .SumEndpoint&gt;(\"\/sum\/{count:int=1000000000}\")\n    .CacheOutput();\n        \n<b class=\"fm-bold\">app.MapEndpoint&lt;Platform<\/b>\n    <b class=\"fm-bold\">.SumEndpoint&gt;(\"\/sum30\/{count:int=1000000000}\")<\/b>\n    <b class=\"fm-bold\">.CacheOutput(\"30sec\");<\/b>\n        \napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The options pattern is applied to an <code class=\"fm-code-in-text\">OutputCacheOptions<\/code> object, whose most useful methods are described in table 17.8.<\/p>\n<p class=\"fm-table-caption\">Table 17.8 Useful OutputCacheOptions methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1978\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddBasePolicy<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to define the default policy<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddPolicy<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to create a policy that can be applied to specific endpoints.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The methods described in table 17.8 define policies using the methods defined by the <code class=\"fm-code-in-text\">OutputCachePolicyBuilder<\/code> class. The basic methods are described in table 17.9.<\/p>\n<p class=\"fm-table-caption\">Table 17.9 The basic OutputCachePolicyBuilder methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-1979\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Expire<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sets the expiry interval for cached output, expressed as a <code class=\"fm-code-in-text1\">TimeSpan<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetLocking<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to enable or disable locking in the cache. Locking is enabled by default and should only be disabled with caution since it can lead to unexpected behavior.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">NoCache<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method disables caching in the policy.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Cache<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method enables caching in the policy.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The policies in listing 17.19 use the <code class=\"fm-code-in-text\">Cache<\/code> and <code class=\"fm-code-in-text\">Expire<\/code> methods. The <code class=\"fm-code-in-text\">AddBasePolicy<\/code> method is used to change the cache duration for the default policy. The <code class=\"fm-code-in-text\">AddPolicy<\/code> method is used to create a new cache policy named <code class=\"fm-code-in-text\">30sec<\/code>, which caches output for 30 seconds:<\/p>\n<pre class=\"programlisting\">...\nopts.AddPolicy(\"<b class=\"fm-bold\">30sec<\/b>\", policy =&gt; {\n    policy.Cache(); \n    policy.Expire(TimeSpan.FromSeconds(30));\n});\n...<\/pre>\n<p class=\"body\">The name given to a policy created with the <code class=\"fm-code-in-text\">AddPolicy<\/code> method is used to apply the policy to an endpoint, like this:<\/p>\n<pre class=\"programlisting\">...\napp.MapEndpoint&lt;Platform\n    .SumEndpoint&gt;(\"\/sum30\/{count:int=1000000000}\")\n    .<b class=\"fm-bold\">CacheOutput(\"30sec\")<\/b>;\n...<\/pre>\n<p class=\"body\">The result is that requests to the <code class=\"fm-code-in-text\">\/sum<\/code> URL are cached using the default policy, while requests to the <code class=\"fm-code-in-text\">\/sum30<\/code> URL are cached using the <code class=\"fm-code-in-text\">30sec<\/code> policy, causing the output from the endpoints to be cached for a different duration.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-326\">17.5 Using Entity Framework Core<\/h2>\n<p class=\"body\">Not all data values are produced directly by the application, and most projects will need to access data in a database. Entity Framework Core is well-integrated into the ASP.NET Core platform, with good support for creating a database from C# classes and for creating C# classes to represent an existing database. In the sections that follow, I demonstrate the process for creating a simple data model, using it to create a database, and querying that database in an endpoint.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Working with Entity Framework Core<\/p>\n<p class=\"fm-sidebar-text\">The most common complaint about Entity Framework Core is poor performance. When I review projects that have Entity Framework Core performance issues, the problem is almost always because the development team has treated Entity Framework Core as a black box and has not paid attention to the SQL queries that are sent to the database. Not all LINQ features can be translated into SQL, and the most common problem is a query that retrieves large amounts of data from the database, which is then discarded after it has been reduced to produce a single value.<\/p>\n<p class=\"fm-sidebar-text\">Using Entity Framework Core requires a good understanding of SQL and ensuring that the LINQ queries made by the application are translated into efficient SQL queries. There are rare applications that have high-performance data requirements that cannot be met by Entity Framework Core, but that isn\u2019t the case for most typical web applications.<\/p>\n<p class=\"fm-sidebar-text\">That is not to say that Entity Framework Core is perfect. It has its quirks and requires an investment in time to become proficient. If you don\u2019t like the way that Entity Framework Core works, then you may prefer to use an alternative, such as Dapper (<a class=\"url\" href=\"https:\/\/dapperlib.github.io\/Dapper\">https:\/\/dapperlib.github.io\/Dapper<\/a>). But if your issue is that queries are not being performed fast enough, then you should spend some time exploring how those queries are being processed, which you can do using the techniques described in the remainder of the chapter.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-327\">17.5.1 Installing Entity Framework Core<\/h3>\n<p class=\"body\">Entity Framework Core requires a global tool package that is used to manage databases from the command line and to manage packages for the project that provides data access. To install the tools package, open a new PowerShell command prompt and run the commands shown in listing 17.20.<a id=\"calibre_link-1980\"><\/a><a id=\"calibre_link-871\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.20 Installing the Entity Framework Core Global Tool Package<\/p>\n<pre class=\"programlisting\">dotnet tool uninstall --global dotnet-ef\ndotnet tool install --global dotnet-ef --version 7.0.0<\/pre>\n<p class=\"body\">The first command removes any existing version of the <code class=\"fm-code-in-text\">dotnet-ef<\/code> package, and the second command installs the version required for the examples in this book. This package provides the <code class=\"fm-code-in-text\">dotnet ef<\/code> commands that you will see in later examples. To ensure the package is working as expected, run the command shown in listing 17.21.<a id=\"calibre_link-1981\"><\/a><a id=\"calibre_link-941\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.21 Testing the Entity Framework Core Global Tool<\/p>\n<pre class=\"programlisting\">dotnet ef --help<\/pre>\n<p class=\"body\">This command shows the help message for the global tool and produces the following output:<\/p>\n<pre class=\"programlisting\">Entity Framework Core .NET Command-line Tools 7.0.0\nUsage: dotnet ef [options] [command]\nOptions:\n  --version        Show version information\n  -h|--help        Show help information\n  -v|--verbose     Show verbose output.\n  --no-color       Don't colorize output.\n  --prefix-output  Prefix output with level.\nCommands:\n  database    Commands to manage the database.\n  dbcontext   Commands to manage DbContext types.\n  migrations  Commands to manage migrations.\nUse \"dotnet ef [command] --help\" for more information about a command.<\/pre>\n<p class=\"body\">Entity Framework Core also requires packages to be added to the project. If you are using Visual Studio Code or prefer working from the command line, navigate to the <code class=\"fm-code-in-text\">Platform<\/code> project folder (the folder that contains the <code class=\"fm-code-in-text\">Platform.csproj<\/code> file) and run the commands shown in listing 17.22.<a id=\"calibre_link-1982\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.22 Adding Entity Framework Core Packages to the Project<\/p>\n<pre class=\"programlisting\">dotnet add package Microsoft.EntityFrameworkCore.Design --version 7.0.0 \ndotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 7.0.0<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-328\">17.5.2 Creating the data model<\/h3>\n<p class=\"body\">For this chapter, I am going to define the data model using C# classes and use Entity Framework Core to create the database and schema. Create the <code class=\"fm-code-in-text\">Platform\/Models<\/code> folder and add to it a class file called <code class=\"fm-code-in-text\">Calculation.cs<\/code> with the contents shown in listing 17.23.<a id=\"calibre_link-1983\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.23 The contents of the Calculation.cs file in the Platform\/Models folder<\/p>\n<pre class=\"programlisting\">namespace Platform.Models {\n\n    public class Calculation {\n        public long Id { get; set; }\n        public int Count { get; set; }\n        public long Result { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">You can see more complex data models in other chapters, but for this example, I am going to keep with the theme of this chapter and model the calculation performed in earlier examples. The <code class=\"fm-code-in-text\">Id<\/code> property will be used to create a unique key for each object stored in the database, and the <code class=\"fm-code-in-text\">Count<\/code> and <code class=\"fm-code-in-text\">Result<\/code> properties will describe a calculation and its result.<\/p>\n<p class=\"body\">Entity Framework Core uses a context class that provides access to the database. Add a file called <code class=\"fm-code-in-text\">CalculationContext.cs<\/code> to the <code class=\"fm-code-in-text\">Platform\/Models<\/code> folder with the content shown in listing 17.24.<a id=\"calibre_link-1984\"><\/a><a id=\"calibre_link-931\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.24 The CalculationContext.cs file in the Platform\/Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n\nnamespace Platform.Models {\n\n    public class CalculationContext : DbContext {\n        \n        public CalculationContext(\n            DbContextOptions&lt;CalculationContext&gt; opts) : base(opts) { }\n                        \n        public DbSet&lt;Calculation&gt; Calculations =&gt; Set&lt;Calculation&gt;();\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">CalculationContext<\/code> class defines a constructor that is used to receive an options object that is passed on to the base constructor. The <code class=\"fm-code-in-text\">Calculations<\/code> property provides access to the <code class=\"fm-code-in-text\">Calculation<\/code> objects that Entity Framework Core will retrieve from the database.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-329\">17.5.3 Configuring the database service<\/h3>\n<p class=\"body\">Access to the database is provided through a service, as shown in listing 17.25.<a id=\"calibre_link-1985\"><\/a><a id=\"calibre_link-1986\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.25 Configuring the data service in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Platform.Services;\n<b class=\"fm-bold\">using Platform.Models;<\/b>\n<b class=\"fm-bold\">using Microsoft.EntityFrameworkCore;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddOutputCache(opts =&gt; {\n    opts.AddBasePolicy(policy =&gt; {\n        policy.Cache();\n        policy.Expire(TimeSpan.FromSeconds(10));\n    });\n    opts.AddPolicy(\"30sec\", policy =&gt; {\n        policy.Cache();\n        policy.Expire(TimeSpan.FromSeconds(30));\n    });\n});\n\nbuilder.Services.AddSingleton&lt;IResponseFormatter,\n    HtmlResponseFormatter&gt;();\n        \n<b class=\"fm-bold\">builder.Services.AddDbContext&lt;CalculationContext&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.UseSqlServer(<\/b>\n        <b class=\"fm-bold\">builder.Configuration[\"ConnectionStrings:CalcConnection\"]);<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseOutputCache();\n\napp.MapEndpoint&lt;Platform\n    .SumEndpoint&gt;(\"\/sum\/{count:int=1000000000}\")\n    .CacheOutput();\n        \napp.MapEndpoint&lt;Platform\n    .SumEndpoint&gt;(\"\/sum30\/{count:int=1000000000}\")\n    .CacheOutput(\"30sec\");\n        \napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddDbContext<\/code> method creates a service for an Entity Framework Core context class. The method receives an options object that is used to select the database provider, which is done with the <code class=\"fm-code-in-text\">UseSqlServer<\/code> method. The <code class=\"fm-code-in-text\">IConfiguration<\/code> service is used to get the connection string for the database, which is defined in listing 17.26.<a id=\"calibre_link-1987\"><\/a><a id=\"calibre_link-938\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.26 A connection string in the appsettings.json file in the Platform folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\",\n      <b class=\"fm-bold\">\"Microsoft.EntityFrameworkCore\": \"Information\"<\/b>\n    }\n  },\n  \"AllowedHosts\": \"*\",\n  \"Location\": {\n    \"CityName\": \"Buffalo\"\n  },\n  \"ConnectionStrings\": {\n    \"CacheConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=CacheDb\",\n    <b class=\"fm-bold\">\"CalcConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=CalcDb\"<\/b>\n  }\n}<\/pre>\n<p class=\"body\">The listing also sets the logging level for the <code class=\"fm-code-in-text\">Microsoft.EntityFrameworkCore<\/code> category, which will show the SQL statements that are used by Entity Framework Core to query the database.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Set the <code class=\"fm-code-in-text1\">MultipleActiveResultSets<\/code> option to <code class=\"fm-code-in-text1\">True<\/code> for connection strings that will be used to make queries with multiple result sets. You can see an example of this option set in the connection strings for the SportsStore project in chapter 7.<a id=\"calibre_link-1988\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-330\">17.5.4 Creating and applying the database migration<\/h3>\n<p class=\"body\">Entity Framework Core manages the relationship between data model classes and the database using a feature called <i class=\"fm-italics\">migrations<\/i>. When changes are made to the model classes, a new migration is created that modifies the database to match those changes. To create the initial migration, which will create a new database and prepare it to store <code class=\"fm-code-in-text\">Calculation<\/code> objects, open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">Platform.csproj<\/code> file, and run the command shown in listing 17.27.<a id=\"calibre_link-1989\"><\/a><a id=\"calibre_link-1990\"><\/a><a id=\"calibre_link-875\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.27 Creating a migration<\/p>\n<pre class=\"programlisting\">dotnet ef migrations add Initial<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">dotnet ef<\/code> commands relate to Entity Framework Core. The command in listing 17.27 creates a new migration named <code class=\"fm-code-in-text\">Initial<\/code>, which is the name conventionally given to the first migration for a project. You will see that a <code class=\"fm-code-in-text\">Migrations<\/code> folder has been added to the project and that it contains class files whose statements prepare the database so that it can store the objects in the data model. To apply the migration, run the command shown in listing 17.28 in the <code class=\"fm-code-in-text\">Platform<\/code> project folder.<a id=\"calibre_link-1991\"><\/a><a id=\"calibre_link-1992\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.28 Applying a migration<\/p>\n<pre class=\"programlisting\">dotnet ef database update<\/pre>\n<p class=\"body\">This command executes the commands in the migration and uses them to prepare the database, which you can see in the SQL statements written to the command prompt.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-331\">17.5.5 Seeding the database<\/h3>\n<p class=\"body\">Most applications require some seed data, especially during development. Entity Framework Core does provide a database seeding feature, but it is of limited use for most projects because it doesn\u2019t allow data to be seeded where the database allocates unique keys to the objects it stores. This is an important feature in most data models because it means the application doesn\u2019t have to worry about allocating unique key values.<a id=\"calibre_link-1993\"><\/a><\/p>\n<p class=\"body\">A more flexible approach is to use the regular Entity Framework Core features to add seed data to the database. Create a file called <code class=\"fm-code-in-text\">SeedData.cs<\/code> in the <code class=\"fm-code-in-text\">Platform\/Models<\/code> folder with the code shown in listing 17.29.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.29 The contents of the SeedData.cs file in the Platform\/Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n\nnamespace Platform.Models {\n    public class SeedData {\n        private CalculationContext context;\n        private ILogger&lt;SeedData&gt; logger;\n                \n        private static Dictionary&lt;int, long&gt; data\n            = new Dictionary&lt;int, long&gt;() {\n                {1, 1}, {2, 3}, {3, 6}, {4, 10}, {5, 15},\n                {6, 21}, {7, 28}, {8, 36}, {9, 45}, {10, 55}\n            };\n                        \n        public SeedData(CalculationContext dataContext, \n                ILogger&lt;SeedData&gt; log) {\n            context = dataContext;\n            logger = log;\n        }\n                \n        public void SeedDatabase() {\n            context.Database.Migrate();\n            if (context.Calculations?.Count() == 0) {\n                logger.LogInformation(\"Preparing to seed database\");\n                context.Calculations.AddRange(\n                        data.Select(kvp =&gt; new Calculation() {\n                            Count = kvp.Key, Result = kvp.Value\n                        }));\n                context.SaveChanges();\n                logger.LogInformation(\"Database seeded\");\n            } else {\n                logger.LogInformation(\"Database not seeded\");\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">SeedData<\/code> class declares constructor dependencies on the <code class=\"fm-code-in-text\">CalculationContext<\/code> and <code class=\"fm-code-in-text\">ILogger&lt;T&gt;<\/code> types, which are used in the <code class=\"fm-code-in-text\">SeedDatabase<\/code> method to prepare the database. The context\u2019s <code class=\"fm-code-in-text\">Database.Migrate<\/code> method is used to apply any pending migrations to the database, and the <code class=\"fm-code-in-text\">Calculations<\/code> property is used to store new data using the <code class=\"fm-code-in-text\">AddRange<\/code> method, which accepts a sequence of <code class=\"fm-code-in-text\">Calculation<\/code> objects.<a id=\"calibre_link-1994\"><\/a><\/p>\n<p class=\"body\">The new objects are stored in the database using the <code class=\"fm-code-in-text\">SaveChanges<\/code> method. To use the <code class=\"fm-code-in-text\">SeedData<\/code> class, make the changes shown in listing 17.30 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<a id=\"calibre_link-1995\"><\/a><a id=\"calibre_link-934\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.30 Enabling database seeding in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Platform.Models;\nusing Platform.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddOutputCache(opts =&gt; {\n    opts.AddBasePolicy(policy =&gt; {\n        policy.Cache();\n        policy.Expire(TimeSpan.FromSeconds(10));\n    });\n    opts.AddPolicy(\"30sec\", policy =&gt; {\n        policy.Cache();\n        policy.Expire(TimeSpan.FromSeconds(30));\n    });\n});\n\nbuilder.Services.AddSingleton&lt;IResponseFormatter,\n    HtmlResponseFormatter&gt;();\n        \nbuilder.Services.AddDbContext&lt;CalculationContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:CalcConnection\"]);\n});\n\n<b class=\"fm-bold\">builder.Services.AddTransient&lt;SeedData&gt;();<\/b>\n\nvar app = builder.Build();\n\napp.UseOutputCache();\n\napp.MapEndpoint&lt;Platform\n    .SumEndpoint&gt;(\"\/sum\/{count:int=1000000000}\")\n    .CacheOutput();\n        \napp.MapEndpoint&lt;Platform\n    .SumEndpoint&gt;(\"\/sum30\/{count:int=1000000000}\")\n    .CacheOutput(\"30sec\");\n        \napp.MapGet(\"\/\", async context =&gt; {\n    await context.Response.WriteAsync(\"Hello World!\");\n});\n\n<b class=\"fm-bold\">bool cmdLineInit = (app.Configuration[\"INITDB\"] ?? \"false\") == \"true\";<\/b>\n<b class=\"fm-bold\">if (app.Environment.IsDevelopment() || cmdLineInit) {<\/b>\n    <b class=\"fm-bold\">var seedData = app.Services.GetRequiredService&lt;SeedData&gt;();<\/b>\n    <b class=\"fm-bold\">seedData.SeedDatabase();<\/b>\n<b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">if (!cmdLineInit) {<\/b>\n    <b class=\"fm-bold\">app.Run();<\/b>\n<b class=\"fm-bold\">}<\/b><\/pre>\n<p class=\"body\">I create a service for the <code class=\"fm-code-in-text\">SeedData<\/code> class, which means that it will be instantiated, and its dependencies will be resolved, which is more convenient than working directly with the class constructor.<\/p>\n<p class=\"body\">If the hosting environment is <code class=\"fm-code-in-text\">Development<\/code>, the database will be seeded automatically as the application starts. It can also be useful to seed the database explicitly, especially when setting up the application for staging or production testing. This statement checks for a configuration setting named <code class=\"fm-code-in-text\">INITDB<\/code>:<a id=\"calibre_link-1996\"><\/a><a id=\"calibre_link-885\"><\/a><\/p>\n<pre class=\"programlisting\">...\nbool cmdLineInit = (<code class=\"fm-code-in-text1\">app.<\/code><b class=\"fm-bold\">Configuration[\"INITDB\"]<\/b> ?? \"false\") == \"true\";\n...<\/pre>\n<p class=\"body\">This setting can be supplied on the command line to seed the database, after which the application will terminate because the <code class=\"fm-code-in-text\">Run<\/code> method, which starts listening for HTTP requests, is never called.<\/p>\n<p class=\"body\">To seed the database, open a new PowerShell command prompt, navigate to the project folder, and run the command shown in listing 17.31.<a id=\"calibre_link-1997\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.31 Seeding the database<\/p>\n<pre class=\"programlisting\">dotnet run INITDB=true<\/pre>\n<p class=\"body\">The application will start, and the database will be seeded with the results for the ten calculations defined by the <code class=\"fm-code-in-text\">SeedData<\/code> class, after which the application will terminate. During the seeding process, you will see the SQL statements that are sent to the database, which check to see whether there are any pending migrations, count the number of rows in the table used to store <code class=\"fm-code-in-text\">Calculation<\/code> data, and, if the table is empty, add the seed data.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> If you need to reset the database, you can use the <code class=\"fm-code-in-text1\">dotnet ef database drop --force<\/code> command. You can then use <code class=\"fm-code-in-text1\">dotnet run INITDB=true<\/code> to re-create and seed the database again.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-332\">17.5.6 Using data in an endpoint<\/h3>\n<p class=\"body\">Endpoints and middleware components access Entity Framework Core data by declaring a dependency on the context class and using its <code class=\"fm-code-in-text\">DbSet&lt;T&gt;<\/code> properties to perform LINQ queries. The LINQ queries are translated into SQL and sent to the database. The row data received from the database is used to create data model objects that are used to produce responses. Listing 17.32 updates the <code class=\"fm-code-in-text\">SumEndpoint<\/code> class to use Entity Framework Core.<a id=\"calibre_link-1998\"><\/a><a id=\"calibre_link-1999\"><\/a><a id=\"calibre_link-935\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.32 Using a database in the SumEndpoint.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">\/\/using Platform.Services;<\/b>\n<b class=\"fm-bold\">using Platform.Models;<\/b>\n\nnamespace Platform {\n\n    public class SumEndpoint {\n        \n        <b class=\"fm-bold\">public async Task Endpoint(HttpContext context,<\/b> \n                <b class=\"fm-bold\">CalculationContext dataContext) {<\/b>\n                                \n            int count;\n            int.TryParse((string?)context.Request.RouteValues[\"count\"],\n                out count);\n                                \n            <b class=\"fm-bold\">long total = dataContext.Calculations?<\/b>\n                <b class=\"fm-bold\">.FirstOrDefault(c =&gt; c.Count == count)?.Result ?? 0;<\/b>\n            <b class=\"fm-bold\">if (total == 0) {<\/b>\n                for (int i = 1; i &lt;= count; i++) {\n                    total += i;\n                }\n                <b class=\"fm-bold\">dataContext.Calculations?.Add(new() {<\/b>\n                    <b class=\"fm-bold\">Count = count, Result = total<\/b>\n                <b class=\"fm-bold\">});<\/b>\n                <b class=\"fm-bold\">await dataContext.SaveChangesAsync();<\/b>\n            }\n            string totalString = $\"({DateTime.Now.ToLongTimeString()}) \" \n                + total;\n                                \n            <b class=\"fm-bold\">await context.Response.WriteAsync(<\/b>\n                <b class=\"fm-bold\">$\"({DateTime.Now.ToLongTimeString()}) Total for {count}\"<\/b>\n                <b class=\"fm-bold\">+ $\" values:\\n{totalString}\\n\");<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The endpoint uses the LINQ <code class=\"fm-code-in-text\">FirstOrDefault<\/code> to search for a stored <code class=\"fm-code-in-text\">Calculation<\/code> object for the calculation that has been requested like this:<\/p>\n<pre class=\"programlisting\">...\ndataContext.Calculations?\n    .<b class=\"fm-bold\">FirstOrDefault<\/b>(c =&gt; c.Count == count)?.Result ?? 0;            \n...<\/pre>\n<p class=\"body\">If an object has been stored, it is used to prepare the response. If not, then the calculation is performed, and a new <code class=\"fm-code-in-text\">Calculation<\/code> object is stored by these statements:<\/p>\n<pre class=\"programlisting\">...\ndataContext.Calculations?.Add(new () { Count = count, Result = total});\nawait dataContext.SaveChangesAsync();\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Add<\/code> method is used to tell Entity Framework Core that the object should be stored, but the update isn\u2019t performed until the <code class=\"fm-code-in-text\">SaveChangesAsync<\/code> method is called. To see the effect of the changes, restart ASP.NET Core MVC (without the <code class=\"fm-code-in-text\">INITDB<\/code> argument if you are using the command line) and request the http:\/\/localhost:5000\/sum\/10 URL. This is one of the calculations with which the database has been seeded, and you will be able to see the query sent to the database in the logging messages produced by the application.<a id=\"calibre_link-2000\"><\/a><a id=\"calibre_link-2001\"><\/a><a id=\"calibre_link-942\"><\/a><\/p>\n<pre class=\"programlisting\">...\nExecuting DbCommand [Parameters=[@__count_0='?' (DbType = Int32)], \n   CommandType='Text', CommandTimeout='30']\nSELECT TOP(1) .[Id], .[Count], .[Result]\n      FROM [Calculations] AS \n      WHERE .[Count] = @__count_0\n...<\/pre>\n<p class=\"body\">If you request http:\/\/localhost:5000\/sum\/100, the database will be queried, but no result will be found. The endpoint performs the calculation and stores the result in the database before producing the result shown in figure 17.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre164\" src=\"\/images\/proaspnetcore7\/000166.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 17.6 Performing a calculation<\/p>\n<\/div>\n<p class=\"body\">Once a result has been stored in the database, subsequent requests for the same URL will be satisfied using the stored data. You can see the SQL statement used to store the data in the logging output produced by Entity Framework Core.<\/p>\n<pre class=\"programlisting\">...\nExecuting DbCommand [Parameters=[@p0='?' (DbType = Int32),\n     @p1='?' (DbType = Int64)], \n        CommandType='Text', CommandTimeout='30']\nSET NOCOUNT ON;\nINSERT INTO [Calculations] ([Count], [Result])\nVALUES (@p0, @p1);\nSELECT [Id]\nFROM [Calculations]\nWHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();\n...<\/pre>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Notice that the data retrieved from the database is not cached and that each request leads to a new SQL query. Depending on the frequency and complexity of the queries you require, you may want to cache data values or responses using the techniques described earlier in the chapter.<\/p>\n<p class=\"fm-head2\">Enabling sensitive data logging<\/p>\n<p class=\"body\">Entity Framework Core doesn\u2019t include parameter values in the logging messages it produces, which is why the logging output contains question marks, like this:<a id=\"calibre_link-2002\"><\/a><a id=\"calibre_link-943\"><\/a><\/p>\n<pre class=\"programlisting\">...\nExecuting DbCommand [Parameters=[<b class=\"fm-bold\">@__count_0='?'<\/b> (DbType = Int32)],\n     CommandType='Text', CommandTimeout='30']\n...<\/pre>\n<p class=\"body\">The data is omitted as a precaution to prevent sensitive data from being stored in logs. If you are having problems with queries and need to see the values sent to the database, then you can use the <code class=\"fm-code-in-text\">EnableSensitiveDataLogging<\/code> method when configuring the database context, as shown in listing 17.33.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 17.33 Sensitive data logging in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">...\nbuilder.Services.AddDbContext&lt;CalculationContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:CalcConnection\"]);\n    <b class=\"fm-bold\">opts.EnableSensitiveDataLogging(true);<\/b>\n});\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core MVC and request the http:\/\/localhost:5000\/sum\/100 URL again. When the request is handled, Entity Framework Core will include parameter values in the logging message it creates to show the SQL query, like this:<\/p>\n<pre class=\"programlisting\">...\nExecuted DbCommand (40ms) [Parameters=[<b class=\"fm-bold\">@__count_0='100'<\/b>],\n     CommandType='Text', CommandTimeout='30']\nSELECT TOP(1) .[Id], .[Count], .[Result]\nFROM [Calculations] AS \nWHERE .[Count] = @__count_0\n...<\/pre>\n<p class=\"body\">This is a feature that should be used with caution because logs are often accessible by people who would not usually have access to the sensitive data that applications handle, such as credit card numbers and account details.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-333\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core provides support for caching individual data values, which can be accessed by endpoints.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Entire responses can be cached, which means that requests are serviced from the cache without using the endpoint.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core provides two different middleware components for caching responses. The response cache requires a header to be set and only uses cached responses for some requests. The output cache is more complex, but more comprehensive, and uses cached responses more widely.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Entity Framework Core provides access to relational data. Entity Framework Core is configured as a service and consumed by endpoints via dependency injection. Entity Framework Core can be used to create a database from a set of model classes and is queried using LINQ.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-334\">\n<div class=\"calibre1\" id=\"calibre_link-2003\">\n<h1 class=\"tochead\" id=\"calibre_link-2004\">Part 3.<\/h1>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-335\">\n<div class=\"calibre1\" id=\"calibre_link-2005\">\n<h1 class=\"tochead\" id=\"calibre_link-2006\">18 Creating the example project<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2007\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Creating an ASP.NET Core project<\/li>\n<li class=\"co-summary-bullet\">Creating a simple data model<\/li>\n<li class=\"co-summary-bullet\">Adding Entity Framework Core to the ASP.NET Core project<\/li>\n<li class=\"co-summary-bullet\">Creating and applying an Entity Framework Core migration<\/li>\n<li class=\"co-summary-bullet\">Adding the Bootstrap CSS package to the project<\/li>\n<li class=\"co-summary-bullet\">Defining a simple request pipeline configuration<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, you will create the example project used throughout this part of the book. The project contains a simple data model, a client-side package for formatting HTML content, and a simple request pipeline.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-336\">18.1 Creating the project<\/h2>\n<p class=\"body\">Open a new PowerShell command prompt and run the commands shown in listing 18.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.1 Creating the project<\/p>\n<pre class=\"programlisting\">dotnet new globaljson --sdk-version 7.0.100 --output WebApp\ndotnet new web --no-https --output WebApp --framework net7.0\ndotnet new sln -o WebApp\ndotnet sln WebApp add WebApp<\/pre>\n<p class=\"body\">If you are using Visual Studio, open the <code class=\"fm-code-in-text\">WebApp.sln<\/code> file in the <code class=\"fm-code-in-text\">WebApp<\/code> folder. If you are using Visual Studio Code, open the <code class=\"fm-code-in-text\">WebApp<\/code> folder. Click the Yes button when prompted to add the assets required for building and debugging the project, as shown in figure 18.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre165\" src=\"\/images\/proaspnetcore7\/000167.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 18.1 Adding project assets<\/p>\n<\/div>\n<p class=\"body\">Open the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file in the <code class=\"fm-code-in-text\">WebApp\/Properties<\/code> folder, change the HTTP port, and disable automatic browser launching, as shown in listing 18.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.2 Setting the port in the launchSettings.json file in the Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"WebApp\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      <b class=\"fm-bold\">\"launchBrowser\": false,<\/b>\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-337\">18.2 Adding a data model<\/h2>\n<p class=\"body\">A data model helps demonstrate the different ways that web applications can be built using ASP.NET Core, showing how complex responses can be composed and how data can be submitted by the user. In the sections that follow, I create a simple data model and use it to create the database schema that will be used to store the application\u2019s data.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-338\">18.2.1 Adding NuGet packages to the project<\/h3>\n<p class=\"body\">The data model will use Entity Framework Core to store and query data in a SQL Server LocalDB database. To add the NuGet packages for Entity Framework Core, use a PowerShell command prompt to run the commands shown in listing 18.3 in the <code class=\"fm-code-in-text\">WebApp<\/code> project folder.<a id=\"calibre_link-2008\"><\/a><a id=\"calibre_link-872\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.3 Adding packages to the project<\/p>\n<pre class=\"programlisting\">dotnet add package Microsoft.EntityFrameworkCore.Design --version 7.0.0 \ndotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 7.0.0<\/pre>\n<p class=\"body\">If you are using Visual Studio, you can add the packages by selecting Project &gt; Manage NuGet Packages. Take care to choose the correct version of the packages to add to the project.<\/p>\n<p class=\"body\">If you have not followed the examples in earlier chapters, you will need to install the global tool package that is used to create and manage Entity Framework Core migrations. Run the commands shown in listing 18.4 to remove any existing version of the package and install the version required for this book. (You can skip these commands if you installed this version of the tools package in earlier chapters.)<a id=\"calibre_link-2009\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.4 Installing a global tool package<\/p>\n<pre class=\"programlisting\">dotnet tool uninstall --global dotnet-ef\ndotnet tool install --global dotnet-ef --version 7.0.0<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-339\">18.2.2 Creating the data model<\/h3>\n<p class=\"body\">The data model for this part of the book will consist of three related classes: <code class=\"fm-code-in-text\">Product<\/code>, <code class=\"fm-code-in-text\">Supplier<\/code>, and <code class=\"fm-code-in-text\">Category<\/code>. Create a new folder named <code class=\"fm-code-in-text\">Models<\/code> and add to it a class file named <code class=\"fm-code-in-text\">Category.cs<\/code>, with the contents shown in listing 18.5.<a id=\"calibre_link-2010\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.5 The contents of the Category.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n    public class Category {\n        \n        public long CategoryId { get; set; }\n        public required string Name { get; set; }\n                \n        public IEnumerable&lt;Product&gt;? Products { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Add a class called <code class=\"fm-code-in-text\">Supplier.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the class shown in listing 18.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.6 The contents of the Supplier.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n    public class Supplier {\n        \n        public long SupplierId { get; set; }\n        public required string Name { get; set; }\n        public required string City { get; set; }\n                \n        public IEnumerable&lt;Product&gt;? Products { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Next, add a class named <code class=\"fm-code-in-text\">Product.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the class shown in listing 18.7.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.7 The contents of the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations.Schema;\n\nnamespace WebApp.Models {\n    public class Product {\n        \n        public long ProductId { get; set; }\n                \n        public required string Name { get; set; }\n        [Column(TypeName = \"decimal(8, 2)\")]\n        public decimal Price { get; set; }\n                \n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        public long SupplierId { get; set; }\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Each of the three data model classes defines a key property whose value will be allocated by the database when new objects are stored. There are also navigation properties that will be used to query for related data so that it will be possible, for example, to query for all the products in a specific category.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Price<\/code> property has been decorated with the <code class=\"fm-code-in-text\">Column<\/code> attribute, which specifies the precision of the values that will be stored in the database. There isn\u2019t a one-to-one mapping between C# and SQL numeric types, and the <code class=\"fm-code-in-text\">Column<\/code> attribute tells Entity Framework Core which SQL type should be used in the database to store <code class=\"fm-code-in-text\">Price<\/code> values. In this case, the <code class=\"fm-code-in-text\">decimal(8, 2)<\/code> type will allow a total of eight digits, including two following the decimal point.<a id=\"calibre_link-2011\"><\/a><a id=\"calibre_link-2012\"><\/a><a id=\"calibre_link-2013\"><\/a><a id=\"calibre_link-937\"><\/a><\/p>\n<p class=\"body\">To create the Entity Framework Core context class that will provide access to the database, add a file called <code class=\"fm-code-in-text\">DataContext.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and add the code shown in listing 18.8.<a id=\"calibre_link-2014\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.8 The contents of the DataContext.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n\nnamespace WebApp.Models {\n    public class DataContext : DbContext {\n        \n        public DataContext(DbContextOptions&lt;DataContext&gt; opts)\n            : base(opts) { }\n                        \n        public DbSet&lt;Product&gt; Products =&gt; Set&lt;Product&gt;();\n        public DbSet&lt;Category&gt; Categories =&gt; Set&lt;Category&gt;();\n        public DbSet&lt;Supplier&gt; Suppliers =&gt; Set&lt;Supplier&gt;();\n    }\n}<\/pre>\n<p class=\"body\">The context class defines properties that will be used to query the database for <code class=\"fm-code-in-text\">Product<\/code>, <code class=\"fm-code-in-text\">Category<\/code>, and <code class=\"fm-code-in-text\">Supplier<\/code> data.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-340\">18.2.3 Preparing the seed data<\/h3>\n<p class=\"body\">Add a class called <code class=\"fm-code-in-text\">SeedData.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and add the code shown in listing 18.9 to define the seed data that will be used to populate the database.<a id=\"calibre_link-2015\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.9 The contents of the SeedData.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n\nnamespace WebApp.Models {\n    public static class SeedData {\n        \n        public static void SeedDatabase(DataContext context) {\n            context.Database.Migrate();\n            if (context.Products.Count() == 0 \n                    &amp;&amp; context.Suppliers.Count() == 0\n                    &amp;&amp; context.Categories.Count() == 0) {\n                                        \n                Supplier s1 = new Supplier \n                    { Name = \"Splash Dudes\", City = \"San Jose\"};\n                Supplier s2 = new Supplier \n                    { Name = \"Soccer Town\", City = \"Chicago\"};\n                Supplier s3 = new Supplier \n                    { Name = \"Chess Co\", City = \"New York\"};\n                                        \n                Category c1 = new Category { Name = \"Watersports\" };\n                Category c2 = new Category { Name = \"Soccer\" };\n                Category c3 = new Category { Name = \"Chess\" };\n                                \n                context.Products.AddRange(\n                    new Product {  Name = \"Kayak\", Price = 275, \n                        Category = c1, Supplier = s1},\n                    new Product {  Name = \"Lifejacket\", Price = 48.95m, \n                        Category = c1, Supplier = s1},\n                    new Product {  Name = \"Soccer Ball\", Price = 19.50m, \n                        Category = c2, Supplier = s2},\n                    new Product {  Name = \"Corner Flags\", Price = 34.95m, \n                        Category = c2, Supplier = s2},\n                    new Product {  Name = \"Stadium\", Price = 79500, \n                        Category = c2, Supplier = s2},\n                    new Product {  Name = \"Thinking Cap\", Price = 16, \n                        Category = c3, Supplier = s3},\n                    new Product {  Name = \"Unsteady Chair\", Price = 29.95m, \n                        Category = c3, Supplier = s3},\n                    new Product {  Name = \"Human Chess Board\", Price = 75, \n                        Category = c3, Supplier = s3},\n                    new Product {  Name = \"Bling-Bling King\", Price = 1200, \n                        Category = c3, Supplier = s3}\n                );\n                context.SaveChanges();\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The static <code class=\"fm-code-in-text\">SeedDatabase<\/code> method ensures that all pending migrations have been applied to the database. If the database is empty, it is seeded with categories, suppliers, and products. Entity Framework Core will take care of mapping the objects into the tables in the database, and the key properties will be assigned automatically when the data is stored.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-341\">18.2.4 Configuring EF Core services and middleware<\/h3>\n<p class=\"body\">Make the changes to the <code class=\"fm-code-in-text\">Program.cs<\/code> file shown in listing 18.10, which configure Entity Framework Core and set up the <code class=\"fm-code-in-text\">DataContext<\/code> services that will be used throughout this part of the book to access the database.<a id=\"calibre_link-2016\"><\/a><a id=\"calibre_link-2017\"><\/a><a id=\"calibre_link-932\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.10 Services and middleware in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using Microsoft.EntityFrameworkCore;<\/b>\n<b class=\"fm-bold\">using WebApp.Models;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.UseSqlServer(builder.Configuration[<\/b>\n        <b class=\"fm-bold\">\"ConnectionStrings:ProductConnection\"]);<\/b>\n    <b class=\"fm-bold\">opts.EnableSensitiveDataLogging(true);<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\n<b class=\"fm-bold\">var context = app.Services.CreateScope().ServiceProvider<\/b>\n    <b class=\"fm-bold\">.GetRequiredService&lt;DataContext&gt;();<\/b>\n<b class=\"fm-bold\">SeedData.SeedDatabase(context);<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">DataContext<\/code> service is scoped, which means that I have to create a scope to get the service required by the <code class=\"fm-code-in-text\">SeedDatabase<\/code> method.<\/p>\n<p class=\"body\">To define the connection string that will be used for the application\u2019s data, add the configuration settings shown in listing 18.11 in the <code class=\"fm-code-in-text\">appsettings.json<\/code> file. The connection string should be entered on a single line.<a id=\"calibre_link-2018\"><\/a><a id=\"calibre_link-876\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.11 A connection string in the appsettings.json file in the WebApp folder<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\",\n      <b class=\"fm-bold\">\"Microsoft.EntityFrameworkCore\": \"Information\"<\/b>\n    }\n  },\n  \"AllowedHosts\": \"*\",\n  <b class=\"fm-bold\">\"ConnectionStrings\": {<\/b>\n    <b class=\"fm-bold\">\"ProductConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=Products;<\/b>\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span><b class=\"fm-bold\">MultipleActiveResultSets=True\"<\/b>\n  <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">In addition to the connection string, listing 18.11 sets the logging detail for Entity Framework Core so that the SQL queries sent to the database are logged.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-342\">18.2.5 Creating and applying the migration<\/h3>\n<p class=\"body\">To create the migration that will set up the database schema, use a PowerShell command prompt to run the command shown in listing 18.12 in the <code class=\"fm-code-in-text\">WebApp<\/code> project folder.<a id=\"calibre_link-2019\"><\/a><a id=\"calibre_link-2020\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.12 Creating an Entity Framework Core migration<\/p>\n<pre class=\"programlisting\">dotnet ef migrations add Initial<\/pre>\n<p class=\"body\">Once the migration has been created, apply it to the database using the command shown in listing 18.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.13 Applying the migration to the database<\/p>\n<pre class=\"programlisting\">dotnet ef database update<\/pre>\n<p class=\"body\">The logging messages displayed by the application will show the SQL commands that are sent to the database.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> If you need to reset the database, then run the <code class=\"fm-code-in-text1\">dotnet ef database drop --force<\/code> command and then the command in listing 18.13.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-343\">18.3 Adding the CSS framework<\/h2>\n<p class=\"body\">Later chapters will demonstrate the different ways that HTML responses can be generated. Run the commands shown in listing 18.14 to remove any existing version of the LibMan package and install the version used in this book. (You can skip these commands if you installed this version of LibMan in earlier chapters.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.14 Installing the LibMan tool package<\/p>\n<pre class=\"programlisting\">dotnet tool uninstall --global Microsoft.Web.LibraryManager.Cli\ndotnet tool install --global Microsoft.Web.LibraryManager.Cli\n    --version 2.1.175 <\/pre>\n<p class=\"body\">To add the Bootstrap CSS framework so that the HTML responses can be styled, run the commands shown in listing 18.15 in the <code class=\"fm-code-in-text\">WebApp<\/code> project folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.15 Installing the Bootstrap CSS framework<\/p>\n<pre class=\"programlisting\">libman init -p cdnjs\nlibman install bootstrap@5.2.3 -d wwwroot\/lib\/bootstrap<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-344\">18.4 Configuring the request pipeline<\/h2>\n<p class=\"body\">To define a simple middleware component that will be used to make sure the example project has been set up correctly, add a class file called <code class=\"fm-code-in-text\">TestMiddleware.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp<\/code> folder and add the code shown in listing 18.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.16 The contents of the TestMiddleware.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using WebApp.Models;\n\nnamespace WebApp {\n    public class TestMiddleware {\n        private RequestDelegate nextDelegate;\n                \n        public TestMiddleware(RequestDelegate next) {\n            nextDelegate = next;\n        }\n                \n        public async Task Invoke(HttpContext context, \n                DataContext dataContext) {\n            if (context.Request.Path == \"\/test\") {\n                await context.Response.WriteAsync($\"There are \"\n                    + dataContext.Products.Count() + \" products\\n\");\n                await context.Response.WriteAsync(\"There are \"  \n                    + dataContext.Categories.Count() + \" categories\\n\");\n                await context.Response.WriteAsync($\"There are \" \n                    + dataContext.Suppliers.Count() + \" suppliers\\n\");\n            } else {\n                await nextDelegate(context);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">Add the middleware component to the request pipeline, as shown in listing 18.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.17 A middleware component in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.UseMiddleware&lt;WebApp.TestMiddleware&gt;();<\/b>\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-345\">18.5 Running the example application<\/h2>\n<p class=\"body\">Start the application by running the command shown in listing 18.18 in the <code class=\"fm-code-in-text\">WebApp<\/code> project folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 18.18 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a new browser tab and request http:\/\/localhost:5000\/test, and you will see the response shown in figure 18.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre166\" src=\"\/images\/proaspnetcore7\/000168.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 18.2 Running the example application<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-346\">\n<div class=\"calibre1\" id=\"calibre_link-2021\">\n<h1 class=\"tochead\" id=\"calibre_link-2022\"><a id=\"calibre_link-2023\"><\/a>19 Creating RESTful web services<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using ASP.NET Core to create RESTful web services<\/li>\n<li class=\"co-summary-bullet\">Creating web services with the minimal API<\/li>\n<li class=\"co-summary-bullet\">Creating web services with controllers<\/li>\n<li class=\"co-summary-bullet\">Using model binding data from web service requests<\/li>\n<li class=\"co-summary-bullet\">Managing the content produced by web services<\/li>\n<\/ul>\n<p class=\"body\">Web services accept HTTP requests and generate responses that contain data. In this chapter, I explain how the features provided by the MVC Framework, which is an integral part of ASP.NET Core, can be used to build on the capabilities described in part 2 to create web services.<\/p>\n<p class=\"body\">The nature of web services means that some of the examples in this chapter are tested using command-line tools provided by PowerShell, and it is important to enter the commands exactly as shown. Chapter 20 introduces more sophisticated tools for working with web services, but the command-line approach is better suited to following examples in a book chapter, even if they can feel a little awkward as you type them in. Table 19.1 puts RESTful web services in context.<\/p>\n<p class=\"fm-table-caption\">Table 19.1 Putting RESTful web services in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2024\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Web services provide access to an application's data, typically expressed in the JSON format.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Web services are most often used to provide rich client-side applications with data.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The combination of the URL and an HTTP method describes an operation that is handled by an endpoint, or an action method defined by a controller.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">There is no widespread agreement about how web services should be implemented, and care must be taken to produce just the data the client expects.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">There are several different approaches to providing clients with data, although RESTful web services are the most common.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 19.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 19.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2025\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Defining a web service<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define endpoints in the <code class=\"fm-code-in-text1\">Program.cs<\/code> file or create a controller with action methods that correspond to the operations that you require.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">3-13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Generating data sequences over time<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">IAsyncEnumerable&lt;T&gt;<\/code> response, which will prevent the request thread from blocking while results are generated.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Preventing request values from being used for sensitive data properties<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a binding target to restrict the model binding process to only safe properties.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">15-17<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Expressing nondata outcomes<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use action results to describe the response that ASP.NET Core should send.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">18-23<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Validating data<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the ASP.NET Core model binding and model validation features.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">24-26<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Automatically validating requests<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">ApiController<\/code> attribute.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">27<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Omitting null values from data responses<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Map the data objects to filter out properties or configure the JSON serializer to ignore <code class=\"fm-code-in-text1\">null<\/code> properties.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">28-32<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Apply a rate limit<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">EnableRateLimiting<\/code> and <code class=\"fm-code-in-text1\">DisableRateLimiting<\/code>.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">33, 34<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-347\">19.1 Preparing for this chapter<\/h2>\n<p class=\"body\">In this chapter, I continue to use the WebApp project created in chapter 18. To prepare for this chapter, drop the database by opening a new PowerShell command prompt, navigating to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and running the command shown in listing 19.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter-and for all the other chapters in this book-from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.1 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<p class=\"body\">Start the application by running the command shown in listing 19.2 in the project folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.2 Starting the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Request the URL http:\/\/localhost:5000\/test once ASP.NET Core has started, and you will see the response shown in figure 19.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre167\" src=\"\/images\/proaspnetcore7\/000169.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-348\">19.2 Understanding RESTful web services<\/h2>\n<p class=\"body\">Web services respond to HTTP requests with data that can be consumed by clients, such as JavaScript applications. There are no hard-and-fast rules for how web services should work, but the most common approach is to adopt the Representational State Transfer (REST) pattern. There is no authoritative specification for REST, and there is no consensus about what constitutes a RESTful web service, but there are some common themes that are widely used for web services.The lack of a detailed specification leads to endless disagreement about what REST means and how RESTful web services should be created, all of which can be safely ignored if the web services you create work for your projects.<a id=\"calibre_link-2026\"><\/a><a id=\"calibre_link-2027\"><\/a><a id=\"calibre_link-2028\"><\/a><a id=\"calibre_link-1116\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-349\">19.2.1 Understanding request URLs and methods<\/h3>\n<p class=\"body\">The core premise of REST-and the only aspect for which there is broad agreement-is that a web service defines an API through a combination of URLs and HTTP methods such as GET and POST, which are also known as the HTTP <i class=\"fm-italics\">verbs<\/i>. The method specifies the type of operation, while the URL specifies the data object or objects that the operation applies to.<\/p>\n<p class=\"body\">As an example, here is a URL that might identify a <code class=\"fm-code-in-text\">Product<\/code> object in the example application:<a id=\"calibre_link-2029\"><\/a><\/p>\n<pre class=\"programlisting\">\/api\/products\/1<\/pre>\n<p class=\"body\">This URL may identify the <code class=\"fm-code-in-text\">Product<\/code> object that has a value of <code class=\"fm-code-in-text\">1<\/code> for its <code class=\"fm-code-in-text\">ProductId<\/code> property. The URL identifies the <code class=\"fm-code-in-text\">Product<\/code>, but it is the HTTP method that specifies what should be done with it. Table 19.3 lists the HTTP methods that are commonly used in web services and the operations they conventionally represent.<\/p>\n<p class=\"fm-table-caption\">Table 19.3 HTTP methods and operations<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2030\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">HTTP Method<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">GET<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to retrieve one or more data objects.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">POST<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to create a new object.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">PUT<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to update an existing object.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">PATCH<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to update part of an existing object.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">DELETE<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to delete an object.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-350\">19.2.2 Understanding JSON<\/h3>\n<p class=\"body\">Most RESTful web services format the response data using the JavaScript Object Notation (JSON) format. JSON has become popular because it is simple and easily consumed by JavaScript clients. JSON is described in detail at <a class=\"url\" href=\"https:\/\/www.json.org\">www.json.org<\/a>, but you don't need to understand every aspect of JSON to create web services because ASP.NET Core provides all the features required to create JSON responses.<a id=\"calibre_link-2031\"><\/a><a id=\"calibre_link-2032\"><\/a><a id=\"calibre_link-1001\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding the alternatives to RESTful web services<\/p>\n<p class=\"fm-sidebar-text\">REST isn't the only way to design web services, and there are some popular alternatives. <i class=\"fm-italics\">GraphQL<\/i> is most closely associated with the React JavaScript framework, but it can be used more widely. Unlike REST web services, which provide specific queries through individual combinations of a URL and an HTTP method, GraphQL provides access to all an application's data and lets clients query for just the data they require in the format they require. GraphQL can be complex to set up-and can require more sophisticated clients-but the result is a more flexible web service that puts the developers of the client in control of the data they consume. GraphQL isn't supported directly by ASP.NET Core, but there are .NET implementations available. See <a class=\"url\" href=\"https:\/\/graphql.org\">https:\/\/graphql.org<\/a> for more detail.<a id=\"calibre_link-2033\"><\/a><a id=\"calibre_link-2034\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">A new alternative is gRPC, a full remote procedure call framework that focuses on speed and efficiency. At the time of writing, gRPC cannot be used in web browsers, such as by the Angular or React framework, because browsers don't provide the fine-grained access that gRPC requires to formulate its HTTP requests.<a id=\"calibre_link-2035\"><\/a><a id=\"calibre_link-2036\"><\/a><\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-351\">19.3 Creating a web service using the minimal API<\/h2>\n<p class=\"body\"><a id=\"calibre_link-2037\"><\/a>As you learn about the facilities that ASP.NET Core provides for web services, it can be easy to forget they are built on the features described in part 2. To create a simple web service, add the statements shown in listing 19.3 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.3 Creating a web service in the Program.cs file in the Platform folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n<b class=\"fm-bold\">using System.Text.Json;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">const string BASEURL = \"api\/products\";<\/b>\n\n<b class=\"fm-bold\">app.MapGet($\"{BASEURL}\/{{id}}\", async (HttpContext context,<\/b>\n        <b class=\"fm-bold\">DataContext data) =&gt; {<\/b>\n    <b class=\"fm-bold\">string? id = context.Request.RouteValues[\"id\"] as string;<\/b>\n    <b class=\"fm-bold\">if (id != null) {<\/b>\n        <b class=\"fm-bold\">Product? p = data.Products.Find(long.Parse(id));<\/b>\n        <b class=\"fm-bold\">if (p == null) {<\/b>\n            <b class=\"fm-bold\">context.Response.StatusCode = StatusCodes.Status404NotFound;<\/b>\n        <b class=\"fm-bold\">} else {<\/b>\n            <b class=\"fm-bold\">context.Response.ContentType = \"application\/json\";<\/b>\n            <b class=\"fm-bold\">await context.Response<\/b>\n                <b class=\"fm-bold\">.WriteAsync(JsonSerializer.Serialize&lt;Product&gt;(p));<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">});<\/b>\n\n<b class=\"fm-bold\">app.MapGet(BASEURL, async (HttpContext context, DataContext data) =&gt; {<\/b>\n    <b class=\"fm-bold\">context.Response.ContentType = \"application\/json\";<\/b>\n    <b class=\"fm-bold\">await context.Response.WriteAsync(JsonSerializer<\/b>\n        <b class=\"fm-bold\">.Serialize&lt;IEnumerable&lt;Product&gt;&gt;(data.Products));<\/b>\n<b class=\"fm-bold\">});<\/b>\n\n<b class=\"fm-bold\">app.MapPost(BASEURL, async (HttpContext context, DataContext data) =&gt; {<\/b>\n    <b class=\"fm-bold\">Product? p = await<\/b>\n        <b class=\"fm-bold\">JsonSerializer.DeserializeAsync&lt;Product&gt;(context.Request.Body);<\/b>\n    <b class=\"fm-bold\">if (p != null) {<\/b> \n        <b class=\"fm-bold\">await data.AddAsync(p);<\/b>\n       <b class=\"fm-bold\">await data.SaveChangesAsync();<\/b>\n        <b class=\"fm-bold\">context.Response.StatusCode = StatusCodes.Status200OK;<\/b>\n    <b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The same API that I used to register endpoints in earlier chapters can be used to create a web service, using only features that you have seen before. The <code class=\"fm-code-in-text\">MapGet<\/code> and <code class=\"fm-code-in-text\">MapPost<\/code> methods are used to create three routes, all of which match URLs that start with <code class=\"fm-code-in-text\">\/api<\/code>, which is the conventional prefix for web services.<\/p>\n<p class=\"body\">The endpoint for the first route receives a value from a segment variable that is used to locate a single <code class=\"fm-code-in-text\">Product<\/code> object in the database. The endpoint for the second route retrieves all the <code class=\"fm-code-in-text\">Product<\/code> objects in the database. The third endpoint handles POST requests and reads the request body to get a JSON representation of a new object to add to the database.<\/p>\n<p class=\"body\">There are better ASP.NET Core features for creating web services, which you will see shortly, but the code in listing 19.3 shows how the HTTP method and the URL can be combined to describe an operation, which is the key concept in creating web services.<\/p>\n<p class=\"body\">To test the web service, restart ASP.NET Core and request http:\/\/localhost:5000\/api\/products\/1. The request will be matched by the first route defined in listing 19.3 and will produce the response shown on the left of figure 19.2. Next, request http:\/\/localhost:5000\/api\/products, which will be matched by the second route and produce the response shown on the right of figure 19.2.<a id=\"calibre_link-2038\"><\/a><a id=\"calibre_link-1038\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The responses shown in the figure contain <code class=\"fm-code-in-text1\">null<\/code> values for the <code class=\"fm-code-in-text1\">Supplier<\/code> and <code class=\"fm-code-in-text1\">Category<\/code> properties because the LINQ queries do not include related data. See chapter 20 for details.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre168\" src=\"\/images\/proaspnetcore7\/000170.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.2 Web service response<\/p>\n<\/div>\n<p class=\"body\">Testing the third route requires a different approach because it isn't possible to send HTTP POST requests using the browser. Open a new PowerShell command prompt and run the command shown in listing 19.4. It is important to enter the command exactly as shown because the <code class=\"fm-code-in-text\">Invoke-RestMethod<\/code> command is fussy about the syntax of its arguments.<a id=\"calibre_link-2039\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You may receive an error when you use the <code class=\"fm-code-in-text1\">Invoke-RestMethod<\/code> or <code class=\"fm-code-in-text1\">Invoke-WebRequest<\/code> command to test the examples in this chapter if you have not performed the initial setup for Microsoft Edge. The problem can be fixed by running the browser and selecting the initial configurations you require.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.4 Sending a POST request<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/api\/products -Method POST -Body  \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>(@{ Name=\"Swimming Goggles\"; Price=12.75; CategoryId=1; SupplierId=1} |\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span> ConvertTo-Json) -ContentType \"application\/json\"<\/pre>\n<p class=\"body\">The command sends an HTTP POST command that is matched by the third route defined in listing 19.3. The body of the request is a JSON-formatted object that is parsed to create a <code class=\"fm-code-in-text\">Product<\/code>, which is then stored in the database. The JSON object included in the request contains values for the <code class=\"fm-code-in-text\">Name<\/code>, <code class=\"fm-code-in-text\">Price<\/code>, <code class=\"fm-code-in-text\">CategoryId<\/code>, and <code class=\"fm-code-in-text\">SupplierId<\/code> properties. The unique key for the object, which is associated with the <code class=\"fm-code-in-text\">ProductId<\/code> property, is assigned by the database when the object is stored. Use the browser to request the http:\/\/localhost:5000\/api\/products URL again, and you will see that the JSON response contains the new object, as shown in figure 19.3.<a id=\"calibre_link-882\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre169\" src=\"\/images\/proaspnetcore7\/000171.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.3 Storing new data using the web service<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-352\">19.4 Creating a web service using a controller<\/h2>\n<p class=\"body\">The drawback of using individual endpoints to create a web service is that each endpoint has to duplicate a similar set of steps to produce a response: get the Entity Framework Core service so that it can query the database, set the <code class=\"fm-code-in-text\">Content-Type<\/code> header for the response, serialize the objects into JSON, and so on. As a result, web services created with endpoints are difficult to understand and awkward to maintain, and the <code class=\"fm-code-in-text\">Program.cs<\/code> file quickly becomes unwieldy.<\/p>\n<p class=\"body\">A more elegant and robust approach is to use a <i class=\"fm-italics\">controller<\/i>, which allows a web service to be defined in a single class. Controllers are part of the MVC Framework, which builds on the ASP.NET Core platform and takes care of handling data in the same way that endpoints take care of processing URLs.<a id=\"calibre_link-2040\"><\/a><a id=\"calibre_link-2041\"><\/a><a id=\"calibre_link-2042\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">The rise and fall of the MVC pattern in ASP.NET Core<\/p>\n<p class=\"fm-sidebar-text\">The MVC Framework is an implementation of the Model-View-Controller pattern, which describes one way to structure an application. The examples in this chapter use two of the three pillars of the pattern: a data model (the <i class=\"fm-italics\">M<\/i> in MVC) and controllers (the <i class=\"fm-italics\">C<\/i> in MVC). chapter 21 provides the missing piece and explains how views can be used to create HTML responses using Razor.<a id=\"calibre_link-2043\"><\/a><a id=\"calibre_link-2044\"><\/a><a id=\"calibre_link-1078\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">The MVC pattern was an important step in the evolution of ASP.NET and allowed the platform to break away from the Web Forms model that predated it. Web Forms applications were easy to start but quickly became difficult to manage and hid details of HTTP requests and responses from the developer. By contrast, the adherence to the MVC pattern provided a strong and scalable structure for applications written with the MVC Framework and hid nothing from the developer. The MVC Framework revitalized ASP.NET and provided the foundation for what became ASP.NET Core, which dropped support for Web Forms and focused solely on using the MVC pattern.<\/p>\n<p class=\"fm-sidebar-text\">As ASP.NET Core evolved, other styles of web application have been embraced, and the MVC Framework is only one of the ways that applications can be created. That doesn't undermine the utility of the MVC pattern, but it doesn't have the central role that it used to in ASP.NET Core development, and the features that used to be unique to the MVC Framework can now be accessed through other approaches, such as Razor Pages and Blazor.<\/p>\n<p class=\"fm-sidebar-text\">A consequence of this evolution is that understanding the MVC pattern is no longer a prerequisite for effective ASP.NET Core development. If you are interested in understanding the MVC pattern, then <a class=\"url\" href=\"https:\/\/en.wikipedia.org\/wiki\/Model-view-controller\">https:\/\/en.wikipedia.org\/wiki\/Model-view-controller<\/a> is a good place to start. But for this book, understanding how the features provided by the MVC Framework build on the ASP.NET Core platform is all the context that is required.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-353\">19.4.1 Enabling the MVC Framework<\/h3>\n<p class=\"body\">The first step to creating a web service using a controller is to configure the MVC framework, which requires a service and an endpoint, as shown in listing 19.5. This listing also removes the endpoints defined in the previous section.<a id=\"calibre_link-2045\"><\/a><a id=\"calibre_link-2046\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.5 Enabling the MVC Framework in the Program.cs File in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n<b class=\"fm-bold\">using WebApp.Models;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\n<b class=\"fm-bold\">builder.Services.AddControllers();<\/b>\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.MapControllers();<\/b>\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddControllers<\/code> method defines the services that are required by the MVC framework, and the <code class=\"fm-code-in-text\">MapControllers<\/code> method defines routes that will allow controllers to handle requests. You will see other methods used to configure the MVC framework used in later chapters, which provide access to different features, but the methods used in listing 19.5 are the ones that configure the MVC framework for web services.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-354\">19.4.2 Creating a controller<\/h3>\n<p class=\"body\"><i class=\"fm-italics\">Controllers<\/i> are classes whose methods, known as <i class=\"fm-italics\">actions<\/i>, can process HTTP requests. Controllers are discovered automatically when the application is started. The basic discovery process is simple: any public class whose name ends with <code class=\"fm-code-in-text\">Controller<\/code> is a controller, and any <code class=\"fm-code-in-text\">public<\/code> method a controller defines is an action. To demonstrate how simple a controller can be, create the <code class=\"fm-code-in-text\">WebApp\/Controllers<\/code> folder and add to it a file named <code class=\"fm-code-in-text\">ProductsController.cs<\/code> with the code shown in listing 19.6.<a id=\"calibre_link-2047\"><\/a><a id=\"calibre_link-2048\"><\/a><a id=\"calibre_link-2049\"><\/a><a id=\"calibre_link-734\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Controllers are conventionally defined in the <code class=\"fm-code-in-text1\">Controllers<\/code> folder, but they can be defined anywhere in the project and will still be discovered.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.6 The contents of the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [Route(\"api\/[controller]\")]\n    public class ProductsController : ControllerBase {\n        \n        [HttpGet]\n        public IEnumerable&lt;Product&gt; GetProducts() {\n            return new Product[] {\n                new Product() { Name = \"Product #1\" },\n                new Product() { Name = \"Product #2\" },\n            };\n        }\n                \n        [HttpGet(\"{id}\")]\n        public Product GetProduct() {\n            return new Product() {\n                ProductId = 1, Name = \"Test Product\"\n            };\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ProductsController<\/code> class meets the criteria that the MVC framework looks for in a controller. It defines public methods named <code class=\"fm-code-in-text\">GetProducts<\/code> and <code class=\"fm-code-in-text\">GetProduct<\/code>, which will be treated as actions.<\/p>\n<p class=\"fm-head2\">Understanding the base class<\/p>\n<p class=\"body\">Controllers are derived from the <code class=\"fm-code-in-text\">ControllerBase<\/code> class, which provides access to features provided by the MVC Framework and the underlying ASP.NET Core platform. Table 19.4 describes the most useful properties provided by the <code class=\"fm-code-in-text\">ControllerBase<\/code> class.<a id=\"calibre_link-2050\"><\/a><a id=\"calibre_link-2051\"><\/a><a id=\"calibre_link-2052\"><\/a><a id=\"calibre_link-2053\"><\/a><a id=\"calibre_link-1079\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Although controllers are typically derived from the <code class=\"fm-code-in-text1\">ControllerBase<\/code> or <code class=\"fm-code-in-text1\">Controller<\/code> classes (described in chapter 21), this is just convention, and the MVC Framework will accept any class whose name ends with <code class=\"fm-code-in-text1\">Controller<\/code>, that is derived from a class whose name ends with <code class=\"fm-code-in-text1\">Controller<\/code>, or that has been decorated with the <code class=\"fm-code-in-text1\">Controller<\/code> attribute. Apply the <code class=\"fm-code-in-text1\">NonController<\/code> attribute to classes that meet these criteria but that should not receive HTTP requests.<\/p>\n<p class=\"fm-table-caption\">Table 19.4 Useful ControllerBase properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2054\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpContext<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the <code class=\"fm-code-in-text1\">HttpContext<\/code> object for the current request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ModelState<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns details of the data validation process, as demonstrated in the \"Validating Data\" section later in the chapter and described in detail in chapter 29.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Request<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the <code class=\"fm-code-in-text1\">HttpRequest<\/code> object for the current request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Response<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the <code class=\"fm-code-in-text1\">HttpResponse<\/code> object for the current response.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RouteData<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the data extracted from the request URL by the routing middleware, as described in chapter 13.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">User<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an object that describes the user associated with the current request, as described in chapter 38.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">A new instance of the controller class is created each time one of its actions is used to handle a request, which means the properties in table 19.4 describe only the current request.<\/p>\n<p class=\"fm-head2\">Understanding the controller attributes<\/p>\n<p class=\"body\">The HTTP methods and URLs supported by the action methods are determined by the combination of attributes that are applied to the controller. The URL for the controller is specified by the <code class=\"fm-code-in-text\">Route<\/code> attribute, which is applied to the class, like this:<a id=\"calibre_link-2055\"><\/a><a id=\"calibre_link-2056\"><\/a><\/p>\n<pre class=\"programlisting\">...\n[Route(\"<b class=\"fm-bold\">api\/[controller]<\/b>\")]\npublic class ProductsController: ControllerBase {\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">[controller]<\/code> part of the attribute argument is used to derive the URL from the name of the controller class. The <code class=\"fm-code-in-text\">Controller<\/code> part of the class name is dropped, which means that the attribute in listing 19.6 sets the URL for the controller to <code class=\"fm-code-in-text\">\/api\/products<\/code>.<\/p>\n<p class=\"body\">Each action is decorated with an attribute that specifies the HTTP method that it supports, like this:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">[HttpGet]<\/b>\npublic Product[] GetProducts() {\n...<\/pre>\n<p class=\"body\">The name given to action methods doesn't matter in controllers used for web services. There are other uses for controllers, described in chapter 21, where the name does matter, but for web services, it is the HTTP method attributes and the route patterns that are important.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HttpGet<\/code> attribute tells the MVC framework that the <code class=\"fm-code-in-text\">GetProducts<\/code> action method will handle HTTP GET requests. Table 19.5 describes the full set of attributes that can be applied to actions to specify HTTP methods.<a id=\"calibre_link-2057\"><\/a><a id=\"calibre_link-1250\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 19.5 The HTTP method attributes<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2058\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpGet<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies that the action can be invoked only by HTTP requests that use the GET verb.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpPost<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies that the action can be invoked only by HTTP requests that use the POST verb.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpDelete<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies that the action can be invoked only by HTTP requests that use the DELETE verb.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpPut<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies that the action can be invoked only by HTTP requests that use the PUT verb.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpPatch<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies that the action can be invoked only by HTTP requests that use the PATCH verb.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpHead<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies that the action can be invoked only by HTTP requests that use the HEAD verb.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AcceptVerbs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify multiple HTTP verbs.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The attributes applied to actions to specify HTTP methods can also be used to build on the controller's base URL.<\/p>\n<pre class=\"programlisting\">...\n[HttpGet<b class=\"fm-bold\">(\"{id}\")<\/b>]\npublic Product GetProduct() {\n...<\/pre>\n<p class=\"body\">This attribute tells the MVC framework that the <code class=\"fm-code-in-text\">GetProduct<\/code> action method handles GET requests for the URL pattern <code class=\"fm-code-in-text\">api\/products\/{id}<\/code>. During the discovery process, the attributes applied to the controller are used to build the set of URL patterns that the controller can handle, summarized in table 19.6.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> When writing a controller, it is important to ensure that each combination of the HTTP method and URL pattern that the controller supports is mapped to only one action method. An exception will be thrown when a request can be handled by multiple actions because the MVC Framework is unable to decide which to use.<\/p>\n<p class=\"fm-table-caption\">Table 19.6 The URL patterns<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2059\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">HTTP Method<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">URL Pattern<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Action Method Name<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">GET<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">api\/products<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetProducts<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">GET<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">api\/products\/{id}<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetProduct<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">You can see how the combination of attributes is equivalent to the <code class=\"fm-code-in-text\">MapGet<\/code> methods I used for the same URL patterns when I used endpoints to create a web service earlier in the chapter.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">GET and POST: Pick the right one<\/p>\n<p class=\"fm-sidebar-text\">The rule of thumb is that GET requests should be used for all read-only information retrieval, while POST requests should be used for any operation that changes the application state. In standards-compliance terms, GET requests are for <i class=\"fm-italics\">safe<\/i> interactions (having no side effects besides information retrieval), and POST requests are for <i class=\"fm-italics\">unsafe<\/i> interactions (making a decision or changing something). These conventions are set by the World Wide Web Consortium (W3C), at <a class=\"url\" href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc9110.xhtml\">https:\/\/www.rfc-editor.org\/rfc\/rfc9110.xhtml<\/a>.<\/p>\n<p class=\"fm-sidebar-text\">GET requests are <i class=\"fm-italics\">addressable<\/i>: all the information is contained in the URL, so it's possible to bookmark and link to these addresses. Do not use GET requests for operations that change state. Many web developers learned this the hard way in 2005 when Google Web Accelerator was released to the public. This application prefetched all the content linked from each page, which is legal within the HTTP because GET requests should be safe. Unfortunately, many web developers had ignored the HTTP conventions and placed simple links to \"delete item\" or \"add to shopping cart\" in their applications. Chaos ensued.<\/p>\n<\/div>\n<p class=\"fm-head2\">Understanding action method results<\/p>\n<p class=\"body\">One of the main benefits provided by controllers is that the MVC Framework takes care of setting the response headers and serializing the data objects that are sent to the client. You can see this in the results defined by the action methods, like this:<a id=\"calibre_link-2060\"><\/a><a id=\"calibre_link-1249\"><\/a><\/p>\n<pre class=\"programlisting\">...\n[HttpGet(\"{id}\")]\npublic <b class=\"fm-bold\">Product<\/b> GetProduct() {\n...<\/pre>\n<p class=\"body\">When I used an endpoint, I had to work directly with the JSON serializer to create a string that can be written to the response and set the <code class=\"fm-code-in-text\">Content-Type<\/code> header to tell the client that the response contained JSON data. The action method returns a <code class=\"fm-code-in-text\">Product<\/code> object, which is processed automatically.<\/p>\n<p class=\"body\">To see how the results from the action methods are handled, restart ASP.NET Core and request http:\/\/localhost:5000\/api\/products, which will produce the response shown on the left of figure 19.4, which is produced by the <code class=\"fm-code-in-text\">GetProducts<\/code> action method. Next, request http:\/\/localhost:5000\/api\/products\/1, which will be handled by the <code class=\"fm-code-in-text\">GetProduct<\/code> method and produce the result shown on the right side of figure 19.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre170\" src=\"\/images\/proaspnetcore7\/000172.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.4 Using a controller<\/p>\n<\/div>\n<p class=\"fm-head2\">Using dependency injection in controllers<\/p>\n<p class=\"body\">A new instance of the controller class is created each time one of its actions is used to handle a request. The application's services are used to resolve any dependencies the controller declares through its constructor and any dependencies that the action method defines. This allows services that are required by all actions to be handled through the constructor while still allowing individual actions to declare their own dependencies, as shown in listing 19.7.<a id=\"calibre_link-2061\"><\/a><a id=\"calibre_link-2062\"><\/a><a id=\"calibre_link-2063\"><\/a><a id=\"calibre_link-909\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.7 Using services in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [Route(\"api\/[controller]\")]\n    public class ProductsController : ControllerBase {\n        <b class=\"fm-bold\">private DataContext context;<\/b>\n                \n        <b class=\"fm-bold\">public ProductsController(DataContext ctx) {<\/b>\n            <b class=\"fm-bold\">context = ctx;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        [HttpGet]\n        public IEnumerable&lt;Product&gt; GetProducts() {\n            <b class=\"fm-bold\">return context.Products;<\/b>\n        }\n                \n        [HttpGet(\"{id}\")]\n        <b class=\"fm-bold\">public Product? GetProduct([FromServices]<\/b>\n                <b class=\"fm-bold\">ILogger&lt;ProductsController&gt; logger) {<\/b>\n            <b class=\"fm-bold\">logger.LogInformation(\"GetProduct Action Invoked\");<\/b>\n            <b class=\"fm-bold\">return context.Products<\/b>\n                <b class=\"fm-bold\">.OrderBy(p =&gt; p.ProductId).FirstOrDefault();<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The constructor declares a dependency on the <code class=\"fm-code-in-text\">DataContext<\/code> service, which provides access to the application's data. The services are resolved using the request scope, which means that a controller can request all services, without needing to understand their lifecycle.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">The Entity Framework Core context service lifecycle<\/p>\n<p class=\"fm-sidebar-text\">A new Entity Framework Core context object is created for each controller. Some developers will try to reuse context objects as a perceived performance improvement, but this causes problems because data from one query can affect subsequent queries. Behind the scenes, Entity Framework Core efficiently manages the connections to the database, and you should not try to store or reuse context objects outside of the controller for which they are created.<\/p>\n<\/div>\n<p class=\"body\">The <code class=\"fm-code-in-text\">GetProducts<\/code> action method uses the <code class=\"fm-code-in-text\">DataContext<\/code> to request all the <code class=\"fm-code-in-text\">Product<\/code> objects in the database. The <code class=\"fm-code-in-text\">GetProduct<\/code> method also uses the <code class=\"fm-code-in-text\">DataContext<\/code> service, but it declares a dependency on <code class=\"fm-code-in-text\">ILogger&lt;T&gt;<\/code>, which is the logging service described in chapter 15. Dependencies that are declared by action methods must be decorated with the <code class=\"fm-code-in-text\">FromServices<\/code> attribute, like this:<a id=\"calibre_link-2064\"><\/a><a id=\"calibre_link-2065\"><\/a><a id=\"calibre_link-915\"><\/a><\/p>\n<pre class=\"programlisting\">...\npublic Product GetProduct(<b class=\"fm-bold\">[FromServices]<\/b>\n    ILogger&lt;ProductsController&gt; logger)\n...<\/pre>\n<p class=\"body\">By default, the MVC Framework attempts to find values for action method parameters from the request URL, and the <code class=\"fm-code-in-text\">FromServices<\/code> attribute overrides this behavior. The <code class=\"fm-code-in-text\">FromServices<\/code> attribute can often be omitted, and ASP.NET Core will try to resolve parameters using dependency injection, but this doesn't work for all parameter types, and I prefer to use the attribute to clearly denote that the value for the parameter will be provided by dependency injection.<\/p>\n<p class=\"body\">To see the use of the services in the controller, restart ASP.NET Core and request http:\/\/localhost:5000\/api\/products\/1, which will produce the response shown in figure 19.5. You will also see the following logging message in the application's output:<\/p>\n<pre class=\"programlisting\">...\ninfo: WebApp.Controllers.ProductsController[0]\n      GetProduct Action Invoked\n... <\/pre>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre136\" src=\"\/images\/proaspnetcore7\/000173.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.5 Using services in a controller<\/p>\n<\/div>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> One consequence of the controller lifecycle is that you can't rely on side effects caused by methods being called in a specific sequence. So, for example, I can't assign the <code class=\"fm-code-in-text1\">ILogger&lt;T&gt;<\/code> object received by the <code class=\"fm-code-in-text1\">GetProduct<\/code> method in listing 19.7 to a property that can be read by the <code class=\"fm-code-in-text1\">GetProducts<\/code> action in later requests. Each controller object is used to handle one request, and only one action method will be invoked by the MVC Framework for each object.<\/p>\n<p class=\"fm-head2\">Using model binding to access route data<\/p>\n<p class=\"body\">In the previous section, I noted that the MVC Framework uses the request URL to find values for action method parameters, a process known as <i class=\"fm-italics\">model binding<\/i>. Model binding is described in detail in chapter 28, but listing 19.8 shows a simple example.<a id=\"calibre_link-2066\"><\/a><a id=\"calibre_link-2067\"><\/a><a id=\"calibre_link-1039\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.8 Model binding in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [Route(\"api\/[controller]\")]\n    public class ProductsController : ControllerBase {\n        private DataContext context;\n                \n        public ProductsController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet]\n        public IEnumerable&lt;Product&gt; GetProducts() {\n            return context.Products;\n        }\n                \n        [HttpGet(\"{id}\")]\n        <b class=\"fm-bold\">public Product? GetProduct(long id,<\/b>\n                <b class=\"fm-bold\">[FromServices] ILogger&lt;ProductsController&gt; logger) {<\/b>\n            logger.LogDebug(\"GetProduct Action Invoked\");\n            <b class=\"fm-bold\">return context.Products.Find(id);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The listing adds a <code class=\"fm-code-in-text\">long<\/code> parameter named <code class=\"fm-code-in-text\">id<\/code> to the <code class=\"fm-code-in-text\">GetProduct<\/code> method. When the action method is invoked, the MVC Framework injects the value with the same name from the routing data, automatically converting it to a <code class=\"fm-code-in-text\">long<\/code> value, which is used by the action to query the database using the LINQ <code class=\"fm-code-in-text\">Find<\/code> method. The result is that the action method responds to the URL, which you can see by restarting ASP.NET Core and requesting http:\/\/localhost:5000\/api\/products\/5, which will produce the response shown in figure 19.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre171\" src=\"\/images\/proaspnetcore7\/000174.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.6 Using model binding in an action<\/p>\n<\/div>\n<p class=\"fm-head2\">Model binding from the request body<\/p>\n<p class=\"body\">The model binding feature can also be used on the data in the request body, which allows clients to send data that is easily received by an action method. Listing 19.9 adds a new action method that responds to POST requests and allows clients to provide a JSON representation of the <code class=\"fm-code-in-text\">Product<\/code> object in the request body.<a id=\"calibre_link-2068\"><\/a><a id=\"calibre_link-1050\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.9 Adding an action in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [Route(\"api\/[controller]\")]\n    public class ProductsController : ControllerBase {\n        private DataContext context;\n                \n        public ProductsController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet]\n        public IEnumerable&lt;Product&gt; GetProducts() {\n            return context.Products;\n        }\n                \n        [HttpGet(\"{id}\")]\n        public Product? GetProduct(long id,\n                [FromServices] ILogger&lt;ProductsController&gt; logger) {\n            logger.LogDebug(\"GetProduct Action Invoked\");\n            return context.Products.Find(id);\n        }\n                \n        <b class=\"fm-bold\">[HttpPost]<\/b>\n        <b class=\"fm-bold\">public void SaveProduct([FromBody] Product product) {<\/b>\n            <b class=\"fm-bold\">context.Products.Add(product);<\/b>\n            <b class=\"fm-bold\">context.SaveChanges();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The new action relies on two attributes. The <code class=\"fm-code-in-text\">HttpPost<\/code> attribute is applied to the action method and tells the MVC Framework that the action can process POST requests. The <code class=\"fm-code-in-text\">FromBody<\/code> attribute is applied to the action's parameter, and it specifies that the value for this parameter should be obtained by parsing the request body. When the action method is invoked, the MVC Framework will create a new <code class=\"fm-code-in-text\">Product<\/code> object and populate its properties with the values in the request body. The model binding process can be complex and is usually combined with data validation, as described in chapter 29, but for a simple demonstration, restart ASP.NET Core, open a new PowerShell command prompt, and run the command shown in listing 19.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.10 Sending a POST request to the example application<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/api\/products -Method POST -Body  (@\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>{ Name=\"Soccer Boots\"; Price=89.99; CategoryId=2; SupplierId=2} | \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span> ConvertTo-Json) -ContentType \"application\/json\"<\/pre>\n<p class=\"body\">Once the command has executed, use a web browser to request http:\/\/localhost:5000\/api\/products, and you will see the new object that has been stored in the database, as shown in figure 19.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre172\" src=\"\/images\/proaspnetcore7\/000175.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.7 Storing new data using a controller<\/p>\n<\/div>\n<p class=\"fm-head2\">Adding additional actions<\/p>\n<p class=\"body\">Now that the basic features are in place, I can add actions that allow clients to replace and delete <code class=\"fm-code-in-text\">Product<\/code> objects using the HTTP PUT and DELETE methods, as shown in listing 19.11.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.11 Adding actions in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [Route(\"api\/[controller]\")]\n    public class ProductsController : ControllerBase {\n        private DataContext context;\n                \n        public ProductsController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet]\n        public IEnumerable&lt;Product&gt; GetProducts() {\n            return context.Products;\n        }\n                \n        [HttpGet(\"{id}\")]\n        public Product? GetProduct(long id,\n                [FromServices] ILogger&lt;ProductsController&gt; logger) {\n            logger.LogDebug(\"GetProduct Action Invoked\");\n            return context.Products.Find(id);\n        }\n                \n        [HttpPost]\n        public void SaveProduct([FromBody] Product product) {\n            context.Products.Add(product);\n            context.SaveChanges();\n        }\n                \n        <b class=\"fm-bold\">[HttpPut]<\/b>\n        <b class=\"fm-bold\">public void UpdateProduct([FromBody] Product product) {<\/b>\n            <b class=\"fm-bold\">context.Products.Update(product);<\/b>\n            <b class=\"fm-bold\">context.SaveChanges();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">[HttpDelete(\"{id}\")]<\/b>\n        <b class=\"fm-bold\">public void DeleteProduct(long id) {<\/b>\n            <b class=\"fm-bold\">context.Products.Remove(new Product() {<\/b> \n                <b class=\"fm-bold\">ProductId = id, Name = string.Empty<\/b>\n            <b class=\"fm-bold\">});<\/b>\n            <b class=\"fm-bold\">context.SaveChanges();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UpdateProduct<\/code> action is similar to the <code class=\"fm-code-in-text\">SaveProduct<\/code> action and uses model binding to receive a <code class=\"fm-code-in-text\">Product<\/code> object from the request body. The <code class=\"fm-code-in-text\">DeleteProduct<\/code> action receives a primary key value from the URL and uses it to create a <code class=\"fm-code-in-text\">Product<\/code> that has a value for the <code class=\"fm-code-in-text\">ProductId<\/code> property, which is required because Entity Framework Core works only with objects, but web service clients typically expect to be able to delete objects using just a key value. (The empty string is assigned to the <code class=\"fm-code-in-text\">Name<\/code> property, to which the <code class=\"fm-code-in-text\">required<\/code> keyword has been applied and without which a <code class=\"fm-code-in-text\">Product<\/code> object cannot be created. Entity Framework Core ignores the empty string when identifying the data to delete).<\/p>\n<p class=\"body\">Restart ASP.NET Core and then use a different PowerShell command prompt to run the command shown in listing 19.12, which tests the <code class=\"fm-code-in-text\">UpdateProduct<\/code> action.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.12 Updating an object<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/api\/products -Method PUT -Body  (@{ \nProductId=1; Name=\"Green Kayak\"; Price=275; CategoryId=1; SupplierId=1} | \nConvertTo-Json) -ContentType \"application\/json\"<\/pre>\n<p class=\"body\">The command sends an HTTP PUT request whose body contains a replacement object. The action method receives the object through the model binding feature and updates the database. Next, run the command shown in listing 19.13 to test the <code class=\"fm-code-in-text\">DeleteProduct<\/code> action.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.13 Deleting an object<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/api\/products\/2 -Method DELETE<\/pre>\n<p class=\"body\">This command sends an HTTP DELETE request, which will delete the object whose <code class=\"fm-code-in-text\">ProductId<\/code> property is 2. To see the effect of the changes, use the browser to request http:\/\/localhost:5000\/api\/products, which will send a GET request that is handled by the <code class=\"fm-code-in-text\">GetProducts<\/code> action and produce the response shown in figure 19.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre173\" src=\"\/images\/proaspnetcore7\/000176.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.8 Updating and deleting objects<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-355\">19.5 Improving the web service<\/h2>\n<p class=\"body\">The controller in listing 19.11 re-creates the functionality provided by the separate endpoints, but there are still improvements that can be made, as described in the following sections.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Supporting cross-origin requests<\/p>\n<p class=\"fm-sidebar-text\">If you are supporting third-party JavaScript clients, you may need to enable support for cross-origin requests (CORS). Browsers protect users by only allowing JavaScript code to make HTTP requests within the same origin, which means to URLs that have the same scheme, host, and port as the URL used to load the JavaScript code. CORS loosens this restriction by performing an initial HTTP request to check that the server will allow requests originating from a specific URL, helping prevent malicious code using your service without the user's consent.<a id=\"calibre_link-2069\"><\/a><a id=\"calibre_link-2070\"><\/a><a id=\"calibre_link-899\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">ASP.NET Core provides a built-in service that handles CORS, which is enabled by adding the following statement to the <code class=\"fm-code-in-text1\">Program.cs<\/code> file:<\/p>\n<pre class=\"programlisting\">...\nbuilder.Services.AddCors();\n...<\/pre>\n<p class=\"fm-sidebar-text\">The options pattern is used to configure CORS with the <code class=\"fm-code-in-text1\">CorsOptions<\/code> class defined in the <code class=\"fm-code-in-text1\">Microsoft.AspNetCore.Cors.Infrastructure<\/code> namespace. See <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-gb\/aspnet\/core\/security\/cors\">https:\/\/docs.microsoft.com\/en-gb\/aspnet\/core\/security\/cors<\/a> for details.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-356\">19.5.1 Using asynchronous actions<\/h3>\n<p class=\"body\">The ASP.NET Core platform processes each request by assigning a thread from a pool. The number of requests that can be processed concurrently is limited to the size of the pool, and a thread can't be used to process any other request while it is waiting for an action to produce a result.<\/p>\n<p class=\"body\">Actions that depend on external resources can cause a request thread to wait for an extended period. A database server, for example, may have its own concurrency limits and may queue up queries until they can be executed. The ASP.NET Core request thread is unavailable to process any other requests until the database produces a result for the action, which then produces a response that can be sent to the HTTP client.<\/p>\n<p class=\"body\">This problem can be addressed by defining asynchronous actions, which allow ASP.NET Core threads to process other requests when they would otherwise be blocked, increasing the number of HTTP requests that the application can process simultaneously. Listing 19.14 revises the controller to use asynchronous actions.<a id=\"calibre_link-2071\"><\/a><a id=\"calibre_link-1257\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Asynchronous actions don't produce responses any quicker, and the benefit is only to increase the number of requests that can be processed concurrently.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.14 Async actions in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [Route(\"api\/[controller]\")]\n    public class ProductsController : ControllerBase {\n        private DataContext context;\n                \n        public ProductsController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet]\n        <b class=\"fm-bold\">public IAsyncEnumerable&lt;Product&gt; GetProducts() {<\/b>\n            <b class=\"fm-bold\">return context.Products.AsAsyncEnumerable();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        [HttpGet(\"{id}\")]\n        <b class=\"fm-bold\">public async Task&lt;Product?&gt; GetProduct(long id) {<\/b>\n            <b class=\"fm-bold\">return await context.Products.FindAsync(id);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        [HttpPost]\n        <b class=\"fm-bold\">public async Task SaveProduct([FromBody] Product product) {<\/b>\n            <b class=\"fm-bold\">await context.Products.AddAsync(product);<\/b>\n            <b class=\"fm-bold\">await context.SaveChangesAsync();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        [HttpPut]\n        <b class=\"fm-bold\">public async Task UpdateProduct([FromBody] Product product) {<\/b>\n            <b class=\"fm-bold\">context.Update(product);<\/b>\n            <b class=\"fm-bold\">await context.SaveChangesAsync();<\/b>\n        }\n                \n        [HttpDelete(\"{id}\")]\n        <b class=\"fm-bold\">public async Task DeleteProduct(long id) {<\/b>\n            <b class=\"fm-bold\">context.Products.Remove(new Product() {<\/b> \n                <b class=\"fm-bold\">ProductId = id, Name = string.Empty<\/b>\n            <b class=\"fm-bold\">});<\/b>\n            <b class=\"fm-bold\">await context.SaveChangesAsync();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Entity Framework Core provides asynchronous versions of some methods, such as <code class=\"fm-code-in-text\">FindAsync<\/code>, <code class=\"fm-code-in-text\">AddAsync<\/code>, and <code class=\"fm-code-in-text\">SaveChangesAsync<\/code>, and I have used these with the <code class=\"fm-code-in-text\">await<\/code> keyword. Not all operations can be performed asynchronously, which is why the <code class=\"fm-code-in-text\">Update<\/code> and <code class=\"fm-code-in-text\">Remove<\/code> methods are unchanged within the <code class=\"fm-code-in-text\">UpdateProduct<\/code> and <code class=\"fm-code-in-text\">DeleteProduct<\/code> actions.<\/p>\n<p class=\"body\">For some operations-including LINQ queries to the database-the <code class=\"fm-code-in-text\">IAsyncEnumerable&lt;T&gt;<\/code> interface can be used, which denotes a sequence of objects that should be enumerated asynchronously and prevents the ASP.NET Core request thread from waiting for each object to be produced by the database, as explained in chapter 5.<\/p>\n<p class=\"body\">There is no change to the responses produced by the controller, but the threads that ASP.NET Core assigns to process each request are not necessarily blocked by the action methods.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-357\">19.5.2 Preventing over-binding<\/h3>\n<p class=\"body\">Some of the action methods use the model binding feature to get data from the request body so that it can be used to perform database operations. There is a problem with the <code class=\"fm-code-in-text\">SaveProduct<\/code> action, which can be seen by using a PowerShell prompt to run the command shown in listing 19.15.<a id=\"calibre_link-2072\"><\/a><a id=\"calibre_link-1056\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.15 Saving a product<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/api\/products -Method POST -Body  (@\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>{ ProductId=100; Name=\"Swim Buoy\"; Price=19.99; CategoryId=1; \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>SupplierId=1} | ConvertTo-Json) -ContentType \"application\/json\"<\/pre>\n<p class=\"body\">This command fails with an error. Unlike the command that was used to test the POST method, this command includes a value for the <code class=\"fm-code-in-text\">ProductId<\/code> property. When Entity Framework Core sends the data to the database, the following exception is thrown:<\/p>\n<pre class=\"programlisting\">...\nMicrosoft.Data.SqlClient.SqlException (0x80131904): Cannot insert explicit \nvalue for identity column in table 'Products' when IDENTITY_INSERT \nis set to OFF.\n...<\/pre>\n<p class=\"body\">By default, Entity Framework Core configures the database to assign primary key values when new objects are stored. This means the application doesn't have to worry about keeping track of which key values have already been assigned and allows multiple applications to share the same database without the need to coordinate key allocation. The <code class=\"fm-code-in-text\">Product<\/code> data model class needs a <code class=\"fm-code-in-text\">ProductId<\/code> property, but the model binding process doesn't understand the significance of the property and adds any values that the client provides to the objects it creates, which causes the exception in the <code class=\"fm-code-in-text\">SaveProduct<\/code> action method.<\/p>\n<p class=\"body\">This is known as <i class=\"fm-italics\">over-binding<\/i>, and it can cause serious problems when a client provides values that the developer didn't expect. At best, the application will behave unexpectedly, but this technique has been used to subvert application security and grant users more access than they should have.<\/p>\n<p class=\"body\">The safest way to prevent over-binding is to create separate data model classes that are used only for receiving data through the model binding process. Add a class file named <code class=\"fm-code-in-text\">ProductBindingTarget.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Models<\/code> folder and use it to define the class shown in listing 19.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.16 The ProductBindingTarget.cs file in the WebApp\/Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n    public class ProductBindingTarget {\n        \n        public required string Name { get; set; }\n                \n        public decimal Price { get; set; }\n                \n        public long CategoryId { get; set; }\n                \n        public long SupplierId { get; set; }\n                \n        public Product ToProduct() =&gt; new Product() {\n            Name = this.Name, Price = this.Price,\n            CategoryId = this.CategoryId, SupplierId = this.SupplierId\n        };\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ProductBindingTarget<\/code> class defines only the properties that the application wants to receive from the client when storing a new object. The <code class=\"fm-code-in-text\">ToProduct<\/code> method creates a <code class=\"fm-code-in-text\">Product<\/code> that can be used with the rest of the application, ensuring that the client can provide properties only for the <code class=\"fm-code-in-text\">Name<\/code>, <code class=\"fm-code-in-text\">Price<\/code>, <code class=\"fm-code-in-text\">CategoryId<\/code>, and <code class=\"fm-code-in-text\">SupplierId<\/code> properties. Listing 19.17 uses the binding target class in the <code class=\"fm-code-in-text\">SaveProduct<\/code> action to prevent over-binding.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.17 A binding target in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">...\n[HttpPost]\n<b class=\"fm-bold\">public async Task SaveProduct([FromBody] ProductBindingTarget target) {<\/b>\n    <b class=\"fm-bold\">await context.Products.AddAsync(target.ToProduct());<\/b>\n    await context.SaveChangesAsync();\n}\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core and repeat the command from listing 19.15, and you will see the response shown in figure 19.9. The client has included the <code class=\"fm-code-in-text\">ProductId<\/code> value, but it is ignored by the model binding process, which discards values for read-only properties. (You may see a different value for the <code class=\"fm-code-in-text\">ProductId<\/code> property when you run this example depending on the changes you made to the database before running the command.)<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre174\" src=\"\/images\/proaspnetcore7\/000177.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.9 Discarding unwanted data values<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-358\">19.5.3 Using action results<\/h3>\n<p class=\"body\">ASP.NET Core sets the status code for responses automatically, but you won't always get the result you desire, in part because there are no firm rules for RESTful web services, and the assumptions that Microsoft makes may not match your expectations. To see an example, use a PowerShell command prompt to run the command shown in listing 19.18, which sends a GET request to the web service.<a id=\"calibre_link-2073\"><\/a><a id=\"calibre_link-2074\"><\/a><a id=\"calibre_link-2075\"><\/a><a id=\"calibre_link-2076\"><\/a><a id=\"calibre_link-2077\"><\/a><a id=\"calibre_link-2078\"><\/a><a id=\"calibre_link-2079\"><\/a><a id=\"calibre_link-735\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.18 Sending a GET request<\/p>\n<pre class=\"programlisting\">Invoke-WebRequest http:\/\/localhost:5000\/api\/products\/1000 | Select-Object \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>StatusCode<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Invoke-WebRequest<\/code> command is similar to the <code class=\"fm-code-in-text\">Invoke-RestMethod<\/code> command used in earlier examples but makes it easier to get the status code from the response. The URL requested in listing 19.18 will be handled by the <code class=\"fm-code-in-text\">GetProduct<\/code> action method, which will query the database for an object whose <code class=\"fm-code-in-text\">ProductId<\/code> value is <code class=\"fm-code-in-text\">1000<\/code>, and the command produces the following output:<\/p>\n<pre class=\"programlisting\">StatusCode\n----------\n       204<\/pre>\n<p class=\"body\">There is no matching object in the database, which means that the <code class=\"fm-code-in-text\">GetProduct<\/code> action method returns <code class=\"fm-code-in-text\">null<\/code>. When the MVC Framework receives <code class=\"fm-code-in-text\">null<\/code> from an action method, it returns the 204 status code, which indicates a successful request that has produced no data. Not all web services behave this way, and a common alternative is to return a 404 response, indicating not found.<\/p>\n<p class=\"body\">Similarly, the <code class=\"fm-code-in-text\">SaveProducts<\/code> action will return a 200 response when it stores an object, but since the primary key isn't generated until the data is stored, the client doesn't know what key value was assigned.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> There is no right or wrong when it comes to these kinds of web service implementation details, and you should pick the approaches that best suit your project and personal preferences. This section is an example of how to change the default behavior and not a direction to follow any specific style of web service.<\/p>\n<p class=\"body\">Action methods can direct the MVC Framework to send a specific response by returning an object that implements the <code class=\"fm-code-in-text\">IActionResult<\/code> interface, which is known as an <i class=\"fm-italics\">action result<\/i>. This allows the action method to specify the type of response that is required without having to produce it directly using the <code class=\"fm-code-in-text\">HttpResponse<\/code> object.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ControllerBase<\/code> class provides a set of methods that are used to create action result objects, which can be returned from action methods. Table 19.7 describes the most useful action result methods.<\/p>\n<p class=\"fm-table-caption\">Table 19.7 Useful ControllerBase action result methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2080\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Ok<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a 200 OK status code and sends an optional data object in the response body.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">NoContent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a 204 NO CONTENT status code.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">BadRequest<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a 400 BAD REQUEST status code. The method accepts an optional model state object that describes the problem to the client, as demonstrated in the \"Validating Data\" section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">File<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a 200 OK response, sets the <code class=\"fm-code-in-text1\">Content-Type<\/code> header to the specified type, and sends the specified file to the client.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">NotFound<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a 404 NOT FOUND status code.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Redirect<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectPermanent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by these methods redirects the client to a specified URL.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectToRoute<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectToRoutePermanent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by these methods redirects the client to the specified URL that is created using the routing system, using convention routing, as described in the \"Redirecting Using Route Values\" sidebar.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">LocalRedirect<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">LocalRedirectPermanent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by these methods redirects the client to the specified URL that is local to the application.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectToAction<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectToActionPermanent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by these methods redirects the client to an action method. The URL for the redirection is created using the URL routing system.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectToPage<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectToPagePermanent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by these methods redirects the client to a Razor Page, described in chapter 23.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">StatusCode<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a response with a specific status code.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">When an action method returns an object, it is equivalent to passing the object to the <code class=\"fm-code-in-text\">Ok<\/code> method and returning the result. When an action returns <code class=\"fm-code-in-text\">null<\/code>, it is equivalent to returning the result from the <code class=\"fm-code-in-text\">NoContent<\/code> method. Listing 19.19 revises the behavior of the <code class=\"fm-code-in-text\">GetProduct<\/code> and <code class=\"fm-code-in-text\">SaveProduct<\/code> actions so they use the methods from table 19.7 to override the default behavior for web service controllers.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.19 Action results in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [Route(\"api\/[controller]\")]\n    public class ProductsController : ControllerBase {\n        private DataContext context;\n                \n        public ProductsController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet]\n        public IAsyncEnumerable&lt;Product&gt; GetProducts() {\n            return context.Products.AsAsyncEnumerable();\n        }\n                \n        [HttpGet(\"{id}\")]\n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; GetProduct(long id) {<\/b>\n            <b class=\"fm-bold\">Product? p = await context.Products.FindAsync(id);<\/b>\n            <b class=\"fm-bold\">if (p == null) {<\/b>\n                <b class=\"fm-bold\">return NotFound();<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return Ok(p);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        [HttpPost]\n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt;<\/b>\n                <b class=\"fm-bold\">SaveProduct([FromBody] ProductBindingTarget target) {<\/b>\n            <b class=\"fm-bold\">Product p = target.ToProduct();<\/b>\n            <b class=\"fm-bold\">await context.Products.AddAsync(p);<\/b>\n            await context.SaveChangesAsync();\n            <b class=\"fm-bold\">return Ok(p);<\/b>\n        }\n                \n        [HttpPut]\n        public async Task UpdateProduct([FromBody] Product product) {\n            context.Update(product);\n            await context.SaveChangesAsync();\n        }\n                \n        [HttpDelete(\"{id}\")]\n        public async Task DeleteProduct(long id) {\n            context.Products.Remove(new Product() {\n                ProductId = id, Name = string.Empty\n            });\n            await context.SaveChangesAsync();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and repeat the command from listing 19.18, and you will see an exception, which is how the <code class=\"fm-code-in-text\">Invoke-WebRequest<\/code> command responds to error status codes, such as the 404 Not Found returned by the <code class=\"fm-code-in-text\">GetProduct<\/code> action method.<\/p>\n<p class=\"body\">To see the effect of the change to the <code class=\"fm-code-in-text\">SaveProduct<\/code> action method, use a PowerShell command prompt to run the command shown in listing 19.20, which sends a POST request to the web service.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.20 Sending a POST request<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/api\/products -Method POST -Body  \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>(@{Name=\"Boot Laces\"; Price=19.99; CategoryId=2; SupplierId=2} | \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>ConvertTo-Json) -ContentType \"application\/json\"<\/pre>\n<p class=\"body\">The command will produce the following output, showing the values that were parsed from the JSON data received from the web service:<\/p>\n<pre class=\"programlisting\">productId  : 13\nname       : Boot Laces\nprice      : 19.99\ncategoryId : 2\ncategory   :\nsupplierId : 2\nsupplier   :<\/pre>\n<p class=\"fm-head2\">Performing redirections<\/p>\n<p class=\"body\">Many of the action result methods in table 19.7 relate to redirections, which redirect the client to another URL. The most basic way to perform a redirection is to call the <code class=\"fm-code-in-text\">Redirect<\/code> method, as shown in listing 19.21.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">LocalRedirect<\/code>and<code class=\"fm-code-in-text1\">LocalRedirectPermanent<\/code>methods throw an exception if a controller tries to perform a redirection to any URL that is not local. This is useful when you are redirecting to URLs provided by users, where an <i class=\"fm-italics\">open redirection attack<\/i> is attempted to redirect another user to an untrusted site.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.21 Redirecting in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [Route(\"api\/[controller]\")]\n    public class ProductsController : ControllerBase {\n        private DataContext context;\n                \n        public ProductsController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        \/\/ ...other action methods omitted for brevity...\n                \n        <b class=\"fm-bold\">[HttpGet(\"redirect\")]<\/b>\n        <b class=\"fm-bold\">public IActionResult Redirect() {<\/b>\n            <b class=\"fm-bold\">return Redirect(\"\/api\/products\/1\");<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The redirection URL is expressed as a <code class=\"fm-code-in-text\">string<\/code> argument to the <code class=\"fm-code-in-text\">Redirect<\/code> method, which produces a temporary redirection. Restart ASP.NET Core and use a PowerShell command prompt to run the command shown in listing 19.22, which sends a GET request that will be handled by the new action method.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.22 Testing redirection<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/api\/products\/redirect<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Invoke-RestMethod<\/code> command will receive the redirection response from the web service and send a new request to the URL it is given, producing the following response:<\/p>\n<pre class=\"programlisting\">productId  : 1\nname       : Green Kayak\nprice      : 275.00\ncategoryId : 1\ncategory   :\nsupplierId : 1\nsupplier   :<\/pre>\n<p class=\"fm-head2\">Redirecting to an action method<\/p>\n<p class=\"body\">You can redirect to another action method using the <code class=\"fm-code-in-text\">RedirectToAction<\/code> method (for temporary redirections) or the <code class=\"fm-code-in-text\">RedirectToActionPermanent<\/code> method (for permanent redirections). Listing 19.23 changes the <code class=\"fm-code-in-text\">Redirect<\/code> action method so that the client will be redirected to another action method defined by the controller.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.23 Redirecting in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">...\n[HttpGet(\"redirect\")]\npublic IActionResult Redirect() {\n    <b class=\"fm-bold\">return RedirectToAction(nameof(GetProduct), new { Id = 1 });<\/b>\n}\n...<\/pre>\n<p class=\"body\">The action method is specified as a string, although the <code class=\"fm-code-in-text\">nameof<\/code> expression can be used to select an action method without the risk of a typo. Any additional values required to create the route are supplied using an anonymous object. Restart ASP.NET Core and use a PowerShell command prompt to repeat the command in listing 19.22. The routing system will be used to create a URL that targets the specified action method, producing the following response:<\/p>\n<pre class=\"programlisting\">productId  : 1\nname       : Green Kayak\nprice      : 275.00\ncategoryId : 1\ncategory   :\nsupplierId : 1\nsupplier   :<\/pre>\n<p class=\"body\">If you specify only an action method name, then the redirection will target the current controller. There is an overload of the <code class=\"fm-code-in-text\">RedirectToAction<\/code> method that accepts action and controller names.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Redirecting using route values<\/p>\n<p class=\"fm-sidebar-text\">The <code class=\"fm-code-in-text1\">RedirectToRoute<\/code> and <code class=\"fm-code-in-text1\">RedirectToRoutePermanent<\/code> methods redirect the client to a URL that is created by providing the routing system with values for segment variables and allowing it to select a route to use. This can be useful for applications with complex routing configurations, and caution should be used because it is easy to create a redirection to the wrong URL. Here is an example of redirection with the <code class=\"fm-code-in-text1\">RedirectToRoute<\/code> method:<\/p>\n<pre class=\"programlisting\">...\n[HttpGet(\"redirect\")]\npublic IActionResult Redirect() {\n    return RedirectToRoute(new {\n        controller = \"Products\", action = \"GetProduct\", Id = 1\n    });\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">The set of values in this redirection relies on convention routing to select the controller and action method. Convention routing is typically used with controllers that produce HTML responses, as described in chapter 21.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-359\">19.5.4 Validating data<\/h3>\n<p class=\"body\">When you accept data from clients, you must assume that a lot of the data will be invalid and be prepared to filter out values that the application can't use. The data validation features provided for MVC Framework controllers are described in detail in chapter 29, but for this chapter, I am going to focus on only one problem: ensuring that the client provides values for the properties that are required to store data in the database. The first step in model binding is to apply attributes to the properties of the data model class, as shown in listing 19.24.<a id=\"calibre_link-2081\"><\/a><a id=\"calibre_link-2082\"><\/a><a id=\"calibre_link-900\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.24 Attributes in the ProductBindingTarget.cs file in the Models folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using System.ComponentModel.DataAnnotations;<\/b>\n\nnamespace WebApp.Models {\n    public class ProductBindingTarget {\n        \n        <b class=\"fm-bold\">[Required]<\/b>\n        public required string Name { get; set; }\n                \n        <b class=\"fm-bold\">[Range(1, 1000)]<\/b>\n        public decimal Price { get; set; }\n                \n        <b class=\"fm-bold\">[Range(1, long.MaxValue)]<\/b>\n        public long CategoryId { get; set; }\n                \n        <b class=\"fm-bold\">[Range(1, long.MaxValue)]<\/b>\n        public long SupplierId { get; set; }\n                \n        public Product ToProduct() =&gt; new Product() {\n            Name = this.Name, Price = this.Price,\n            CategoryId = this.CategoryId, SupplierId = this.SupplierId\n        };\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Required<\/code> attribute denotes properties for which the client must provide a value and can be applied to properties that are assigned <code class=\"fm-code-in-text\">null<\/code> when there is no value in the request. The <code class=\"fm-code-in-text\">Range<\/code> attribute requires a value between upper and lower limits and is used for primitive types that will default to zero when there is no value in the request.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The <code class=\"fm-code-in-text1\">Required<\/code> attribute could be omitted from the <code class=\"fm-code-in-text1\">Name<\/code> property because ASP.NET Core will infer the validation constraint from the <code class=\"fm-code-in-text1\">required<\/code> keyword. This is a useful feature, but I like to use the <code class=\"fm-code-in-text1\">Required<\/code> attribute for consistency and to make it obvious that the validation constraint was intentional.<\/p>\n<p class=\"body\">Listing 19.25 updates the <code class=\"fm-code-in-text\">SaveProduct<\/code> action to perform validation before storing the object that is created by the model binding process, ensuring that only objects that contain values for all four properties decorated with the validation attributes are accepted.<a id=\"calibre_link-2083\"><\/a><a id=\"calibre_link-2084\"><\/a><a id=\"calibre_link-2085\"><\/a><a id=\"calibre_link-2086\"><\/a><a id=\"calibre_link-901\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.25 Validation in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">...\n[HttpPost]\npublic async Task&lt;IActionResult&gt;\n        SaveProduct([FromBody] ProductBindingTarget target) {\n    <b class=\"fm-bold\">if (ModelState.IsValid) {<\/b>\n        Product p = target.ToProduct();\n        await context.Products.AddAsync(p);\n        await context.SaveChangesAsync();\n        return Ok(p);\n    <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">return BadRequest(ModelState);<\/b>\n}\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ModelState<\/code> property is inherited from the <code class=\"fm-code-in-text\">ControllerBase<\/code> class, and the <code class=\"fm-code-in-text\">IsValid<\/code> property returns <code class=\"fm-code-in-text\">true<\/code> if the model binding process has produced data that meets the validation criteria. If the data received from the client is valid, then the action result from the <code class=\"fm-code-in-text\">Ok<\/code> method is returned. If the data sent by the client fails the validation check, then the <code class=\"fm-code-in-text\">IsValid<\/code> property will be <code class=\"fm-code-in-text\">false<\/code>, and the action result from the <code class=\"fm-code-in-text\">BadRequest<\/code> method is used instead. The <code class=\"fm-code-in-text\">BadRequest<\/code> method accepts the object returned by the <code class=\"fm-code-in-text\">ModelState<\/code> property, which is used to describe the validation errors to the client. (There is no standard way to describe validation errors, so the client may rely only on the 400 status code to determine that there is a problem.)<\/p>\n<p class=\"body\">To test the validation, restart ASP.NET Core and use a new PowerShell command prompt to run the command shown in listing 19.26.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.26 Testing validation<\/p>\n<pre class=\"programlisting\">Invoke-WebRequest http:\/\/localhost:5000\/api\/products -Method POST -Body  \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>(@{Name=\"Boot Laces\"} | ConvertTo-Json) -ContentType \"application\/json\"<\/pre>\n<p class=\"body\">The command will throw an exception that shows the web service has returned a 400 Bad Request response. Details of the validation errors are not shown because neither the <code class=\"fm-code-in-text\">Invoke-WebRequest<\/code> command nor the <code class=\"fm-code-in-text\">Invoke-RestMethod<\/code> command provides access to error response bodies. Although you can't see it, the body contains a JSON object that has properties for each data property that has failed validation, like this:<\/p>\n<pre class=\"programlisting\">{\n \"Price\":[\"The field Price must be between 1 and 1000.\"],\n \"CategoryId\":[\"The field CategoryId must be between 1\n     and 9.223372036854776E+18.\"],\n \"SupplierId\":[\"The field SupplierId must be between 1\n     and 9.223372036854776E+18.\"]\n}<\/pre>\n<p class=\"body\">You can see examples of working with validation messages in chapter 29 where the validation feature is described in detail.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-360\">19.5.5 Applying the API controller attribute<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ApiController<\/code> attribute can be applied to web service controller classes to change the behavior of the model binding and validation features. The use of the <code class=\"fm-code-in-text\">FromBody<\/code> attribute to select data from the request body and explicitly check the <code class=\"fm-code-in-text\">ModelState.IsValid<\/code> property is not required in controllers that have been decorated with the <code class=\"fm-code-in-text\">ApiController<\/code> attribute. Getting data from the body and validating data are required so commonly in web services that they are applied automatically when the attribute is used, restoring the focus of the code in the controller's action to dealing with the application features, as shown in listing 19.27.<a id=\"calibre_link-2087\"><\/a><a id=\"calibre_link-1251\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.27 The ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    <b class=\"fm-bold\">[ApiController]<\/b>\n        \n    [Route(\"api\/[controller]\")]\n    public class ProductsController : ControllerBase {\n        private DataContext context;\n                \n        public ProductsController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet]\n        public IAsyncEnumerable&lt;Product&gt; GetProducts() {\n            return context.Products.AsAsyncEnumerable();\n        }\n                \n        [HttpGet(\"{id}\")]\n        public async Task&lt;IActionResult&gt; GetProduct(long id) {\n            Product? p = await context.Products.FindAsync(id);\n            if (p == null) {\n                return NotFound();\n            }\n            return Ok(p);\n        }\n                \n        <b class=\"fm-bold\">[HttpPost]<\/b>\n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt;<\/b>\n                <b class=\"fm-bold\">SaveProduct(ProductBindingTarget target) {<\/b>\n            <b class=\"fm-bold\">Product p = target.ToProduct();<\/b>\n            <b class=\"fm-bold\">await context.Products.AddAsync(p);<\/b>\n            <b class=\"fm-bold\">await context.SaveChangesAsync();<\/b>\n            <b class=\"fm-bold\">return Ok(p);<\/b>            \n        <b class=\"fm-bold\">}<\/b>\n                \n        [HttpPut]\n        <b class=\"fm-bold\">public async Task UpdateProduct(Product product) {<\/b>\n            context.Update(product);\n            await context.SaveChangesAsync();\n        }\n                \n        [HttpDelete(\"{id}\")]\n        public async Task DeleteProduct(long id) {\n            context.Products.Remove(new Product() {\n                ProductId = id, Name = string.Empty\n            });\n            await context.SaveChangesAsync();\n        }\n                \n        [HttpGet(\"redirect\")]\n        public IActionResult Redirect() {\n            return RedirectToAction(nameof(GetProduct), new { Id = 1 });\n        }\n    }\n}<\/pre>\n<p class=\"body\">Using the <code class=\"fm-code-in-text\">ApiController<\/code> attribute is optional, but it helps produce concise web service controllers.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-361\">19.5.6 Omitting Null properties<\/h3>\n<p class=\"body\">The final change I am going to make in this chapter is to remove the <code class=\"fm-code-in-text\">null<\/code> values from the data returned by the web service. The data model classes contain navigation properties that are used by Entity Framework Core to associate related data in complex queries, as explained in chapter 20. For the simple queries that are performed in this chapter, no values are assigned to these navigation properties, which means that the client receives properties for which values are never going to be available. To see the problem, use a PowerShell command prompt to run the command shown in listing 19.28.<a id=\"calibre_link-2088\"><\/a><a id=\"calibre_link-1260\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.28 Sending a GET request<\/p>\n<pre class=\"programlisting\">Invoke-WebRequest http:\/\/localhost:5000\/api\/products\/1 | \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>Select-Object Content<\/pre>\n<p class=\"body\">The command sends a GET request and displays the body of the response from the web service, producing the following output:<\/p>\n<pre class=\"programlisting\">Content\n-------\n{\"productId\":1,\"name\":\"Green Kayak\",\"price\":275.00,\n \"categoryId\":1,\"category\":null,\"supplierId\":1,\"supplier\":null}<\/pre>\n<p class=\"body\">The request was handled by the <code class=\"fm-code-in-text\">GetProduct<\/code> action method, and the <code class=\"fm-code-in-text\">category<\/code> and <code class=\"fm-code-in-text\">supplier<\/code> values in the response will always be <code class=\"fm-code-in-text\">null<\/code> because the action doesn't ask Entity Framework Core to populate these properties.<\/p>\n<p class=\"fm-head2\">Projecting selected properties<\/p>\n<p class=\"body\">The first approach is to return just the properties that the client requires. This gives you complete control over each response, but it can become difficult to manage and confusing for client developers if each action returns a different set of values. Listing 19.29 shows how the <code class=\"fm-code-in-text\">Product<\/code> object obtained from the database can be projected so that the navigation properties are omitted.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.29 Omit properties in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">...\n[HttpGet(\"{id}\")]\npublic async Task&lt;IActionResult&gt; GetProduct(long id) {\n    Product? p = await context.Products.FindAsync(id);\n    if (p == null) {\n        return NotFound();\n    }\n    <b class=\"fm-bold\">return Ok(new {<\/b>\n        <b class=\"fm-bold\">p.ProductId, p.Name, p.Price, p.CategoryId, p.SupplierId<\/b>\n    <b class=\"fm-bold\">});<\/b>\n}\n...<\/pre>\n<p class=\"body\">The properties that the client requires are selected and added to an object that is passed to the <code class=\"fm-code-in-text\">Ok<\/code> method. Restart ASP.NET Core and run the command from listing 19.28, and you will receive a response that omits the navigation properties and their <code class=\"fm-code-in-text\">null<\/code> values, like this:<\/p>\n<pre class=\"programlisting\">Content\n-------\n{\"productId\":1,\"name\":\"Green Kayak\",\"price\":275.00,\n \"categoryId\":1,\"supplierId\":1}<\/pre>\n<p class=\"fm-head2\">Configuring the JSON serializer<\/p>\n<p class=\"body\">The JSON serializer can be configured to omit properties when it serializes objects. One way to configure the serializer is with the <code class=\"fm-code-in-text\">JsonIgnore<\/code> attribute, as shown in listing 19.30.<a id=\"calibre_link-2089\"><\/a><a id=\"calibre_link-2090\"><\/a><a id=\"calibre_link-1014\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.30 Configuring the serializer in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations.Schema;\n<b class=\"fm-bold\">using System.Text.Json.Serialization;<\/b>\n\nnamespace WebApp.Models {\n    public class Product {\n        \n        public long ProductId { get; set; }\n                \n        public required string Name { get; set; }\n        [Column(TypeName = \"decimal(8, 2)\")]\n        public decimal Price { get; set; }\n                \n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        public long SupplierId { get; set; }\n                \n        <b class=\"fm-bold\">[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]<\/b>\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Condition<\/code> property is assigned a <code class=\"fm-code-in-text\">JsonIgnoreCondition<\/code> value, as described in table 19.8.<a id=\"calibre_link-2091\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 19.8 The values defined by the JsonIgnoreCondition enum<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2092\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Always<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The property will always be ignored when serializing an object.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Never<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The property will always be included when serializing an object.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">WhenWritingDefault<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The property will be ignored if the value is <code class=\"fm-code-in-text1\">null<\/code> or the default value for the property type.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">WhenWritingNull<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The property will be ignored if the value is <code class=\"fm-code-in-text1\">null<\/code>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">JsonIgnore<\/code> attribute has been applied using the <code class=\"fm-code-in-text\">WhenWritingNull<\/code> value, which means that the <code class=\"fm-code-in-text\">Supplier<\/code> property will be ignored if its value is <code class=\"fm-code-in-text\">null<\/code>. Listing 19.31 updates the controller to use the <code class=\"fm-code-in-text\">Product<\/code> class directly in the <code class=\"fm-code-in-text\">GetProduct<\/code> action method.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.31 A model class in the ProductController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">...\n[HttpGet(\"{id}\")]\npublic async Task&lt;IActionResult&gt; GetProduct(long id) {\n    Product? p = await context.Products.FindAsync(id);\n    if (p == null) {\n        return NotFound();\n    }\n    <b class=\"fm-bold\">return Ok(p);<\/b>\n}\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core and run the command from listing 19.28, and you will receive a response that omits the <code class=\"fm-code-in-text\">supplier<\/code> property, like this:<\/p>\n<pre class=\"programlisting\">Content\n-------\n{\"productId\":1,\"name\":\"Green Kayak\",\"price\":275.00,\"categoryId\":1,\n    \"category\":null,\n \"supplierId\":1}<\/pre>\n<p class=\"body\">The attribute has to be applied to model classes and is useful when a small number of properties should be ignored, but this can be difficult to manage for more complex data models. A general policy can be defined for serialization using the options pattern, as shown in listing 19.32.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.32 Configuring the serializer in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc;<\/b>\n<b class=\"fm-bold\">using System.Text.Json.Serialization;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddControllers();\n\n<b class=\"fm-bold\">builder.Services.Configure&lt;JsonOptions&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.JsonSerializerOptions.DefaultIgnoreCondition<\/b> \n        <b class=\"fm-bold\">= JsonIgnoreCondition.WhenWritingNull;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.MapControllers();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The JSON serializer is configured using the <code class=\"fm-code-in-text\">JsonSerializerOptions<\/code> property of the <code class=\"fm-code-in-text\">JsonOptions<\/code> class, and <code class=\"fm-code-in-text\">null<\/code> values are managed using the <code class=\"fm-code-in-text\">DefaultIgnoreCondition<\/code> property, which is assigned one of the <code class=\"fm-code-in-text\">JsonIgnoreCondition<\/code> values described in table 19.8. (The <code class=\"fm-code-in-text\">Always<\/code> value does not make sense when using the options pattern and will cause an exception when ASP.NET Core is started.)<\/p>\n<p class=\"body\">This configuration change affects all JSON responses and should be used with caution, especially if your data model classes use <code class=\"fm-code-in-text\">null<\/code> values to impart information to the client. To see the effect of the change, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/api\/products, which will produce the response shown in figure 19.10.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">JsonIgnore<\/code> attribute can be used to override the default policy, which is useful if you need to include <code class=\"fm-code-in-text1\">null<\/code> or default values for a particular property.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre175\" src=\"\/images\/proaspnetcore7\/000178.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.10 Configuring the JSON serializer<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-362\">19.5.7 Applying a rate limit<\/h3>\n<p class=\"body\">In chapter 16, I demonstrated the rate limiting feature and showed you how it is applied to individual endpoints. This feature also works for controllers, using an attribute to select the rate limit that will be applied. In preparation, listing 19.33 defines a rate limiting policy and enables rate limits on controllers.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.33 Rate limits in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing System.Text.Json.Serialization;\nusing Microsoft.AspNetCore.RateLimiting;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddControllers();\n\n<b class=\"fm-bold\">builder.Services.AddRateLimiter(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.AddFixedWindowLimiter(\"fixedWindow\", fixOpts =&gt; {<\/b>\n        <b class=\"fm-bold\">fixOpts.PermitLimit = 1;<\/b>\n        <b class=\"fm-bold\">fixOpts.QueueLimit = 0;<\/b>\n        <b class=\"fm-bold\">fixOpts.Window = TimeSpan.FromSeconds(15);<\/b>\n    <b class=\"fm-bold\">});<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nbuilder.Services.Configure&lt;JsonOptions&gt;(opts =&gt; {\n    opts.JsonSerializerOptions.DefaultIgnoreCondition\n        = JsonIgnoreCondition.WhenWritingNull;\n});\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">app.UseRateLimiter();<\/b>\napp.MapControllers();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">This listing sets up the same policy I used in chapter 16, which limits requests to one every 15 seconds with no queue. Listing 19.34 applies the policy to the controller using the <code class=\"fm-code-in-text\">EnableRateLimiting<\/code> and <code class=\"fm-code-in-text\">DisableRateLimiting<\/code> attributes.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> You can apply a single rate limiting policy to all controllers by calling <code class=\"fm-code-in-text1\">app.MapControllers().RequireRateLimiting(\"fixedWindow\")<\/code>. This policy can be overridden for specific controllers and actions using the <code class=\"fm-code-in-text1\">EnableRateLimiting<\/code> and <code class=\"fm-code-in-text1\">DisableRateLimiting<\/code> attributes.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 19.34 Limits in the ProductsController.cs file in the WebApp\/Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.RateLimiting;<\/b>\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"api\/[controller]\")]\n    <b class=\"fm-bold\">[EnableRateLimiting(\"fixedWindow\")]<\/b>\n    public class ProductsController : ControllerBase {\n        private DataContext context;\n                \n        public ProductsController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet]\n        public IAsyncEnumerable&lt;Product&gt; GetProducts() {\n            return context.Products.AsAsyncEnumerable();\n        }\n                \n        [HttpGet(\"{id}\")]\n        <b class=\"fm-bold\">[DisableRateLimiting]<\/b>\n        public async Task&lt;IActionResult&gt; GetProduct(long id) {\n            Product? p = await context.Products.FindAsync(id);\n            if (p == null) {\n                return NotFound();\n            }\n            return Ok(p);\n        }\n                \n        \/\/ ...other action methods omitted for brevity...\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">EnableRateLimiting<\/code> attribute is used to apply a rate limiting policy to the controller, specifying the name of the policy as an argument. This policy will apply to all of the action methods defined by the controller, except the <code class=\"fm-code-in-text\">GetProduct<\/code> method, to which the <code class=\"fm-code-in-text\">DisableRateLimiting<\/code> attribute has been applied and to which no limits will be enforced.<\/p>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/api\/products. Click the browser's reload button and you will exceed the request limit and see a 503 error, as shown in figure 19.11. You can request the URL http:\/\/localhost:5000\/api\/products\/1 as often as you wish without producing an error.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre176\" src=\"\/images\/proaspnetcore7\/000179.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 19.11 An error caused by a rate limit<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-363\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">RESTful web services use the HTTP method and URL to specify an operation to perform.Web services can be created using top-level statements but using controller scales is better for most projects.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The base class for controllers defines properties that access the request data.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Action methods are decorated with attributes to specify the HTTP methods they accept.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core will perform model binding to extract data from the request and pass it to an action method as an object.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Care must be taken to receive only the data that is required from the user.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Data validation can be performed on the data that is produced by model binding, ensuring that clients provide data in a way the ASP.NET Core application can work with.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The rate limiting features described in chapter 16 can also be applied to web services.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-364\">\n<div class=\"calibre1\" id=\"calibre_link-2093\">\n<h1 class=\"tochead\" id=\"calibre_link-2094\"><a id=\"calibre_link-2095\"><\/a><a id=\"calibre_link-2096\"><\/a>20 Advanced web service features<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Managing related data in web service results<\/li>\n<li class=\"co-summary-bullet\">Supporting the PATCH method to make selective changes<\/li>\n<li class=\"co-summary-bullet\">Formatting content produced by web services<\/li>\n<li class=\"co-summary-bullet\">Caching the output from web services<\/li>\n<li class=\"co-summary-bullet\">Generating documentation that describes a web service<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I describe advanced features that can be used to create RESTful web services. I explain how to deal with related data in Entity Framework Core queries, how to add support for the HTTP PATCH method, how to use content negotiations, and how to use OpenAPI to describe your web services. Table 20.1 puts this chapter in context.<\/p>\n<p class=\"fm-table-caption\">Table 20.1 Putting advanced web service features in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2097\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The features described in this chapter provide greater control over how ASP.NET Core web services work, including managing the data sent to the client and the format used for that data.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The default behaviors provided by ASP.NET Core don\u2019t meet the needs of every project, and the features described in this chapter allow web services to be reshaped to fit specific requirements.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The common theme for the features in this chapter is altering the responses produced by action methods.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">It can be hard to decide how to implement web services, especially if they are consumed by third-party clients. The behavior of a web service becomes fixed as soon as clients start using a web service, which means that careful thought is required when using the features described in this chapter.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The features described in this chapter are optional, and you can rely on the default behaviors of ASP.NET Core web services.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 20.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 20.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2098\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using relational data<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">Include<\/code> and <code class=\"fm-code-in-text1\">ThenInclude<\/code> methods in LINQ queries.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">4<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Breaking circular references<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Explicitly set navigation properties to <code class=\"fm-code-in-text1\">null<\/code>.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">5<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Allowing clients to selectively update data<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Support the HTTP PATCH method.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">6&ndash;9<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Supporting a range of response data types<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Support content formatting and negotiation.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">10&ndash;24<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Cache output<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the output caching middleware and the <code class=\"fm-code-in-text1\">OutputCache<\/code> attribute.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">25, 26<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Documenting a web service<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use OpenAPI to describe the web service.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">27&ndash;29<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-365\">20.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the WebApp project created in chapter 18 and modified in chapter 19. To prepare for this chapter, add a file named <code class=\"fm-code-in-text\">SuppliersController.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Controllers<\/code> folder with the content shown in listing 20.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.1 The contents of the SuppliersController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"api\/[controller]\")]\n    public class SuppliersController : ControllerBase {\n        private DataContext context;\n                \n        public SuppliersController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet(\"{id}\")]\n        public async Task&lt;Supplier?&gt; GetSupplier(long id) {\n            return await context.Suppliers.FindAsync(id);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The controller extends the <code class=\"fm-code-in-text\">ControllerBase<\/code> class, declares a dependency on the <code class=\"fm-code-in-text\">DataContext<\/code> service, and defines an action named <code class=\"fm-code-in-text\">GetSupplier<\/code> that handles GET requests for the <code class=\"fm-code-in-text\">\/api\/[controller]\/{id}<\/code> URL pattern.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-366\">20.1.1 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 20.2 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.2 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-367\">20.1.2 Running the example application<\/h3>\n<p class=\"body\">Once the database has been dropped, use the PowerShell command prompt to run the command shown in listing 20.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.3 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">The database will be seeded as part of the application startup. Once ASP.NET Core is running, use a web browser to request http:\/\/localhost:5000\/api\/suppliers\/1, which will produce the response shown in figure 20.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre177\" src=\"\/images\/proaspnetcore7\/000180.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.1 Running the example application<\/p>\n<\/div>\n<p class=\"body\">The response shows the <code class=\"fm-code-in-text\">Supplier<\/code> object whose primary key matches the last segment of the request URL. In chapter 19, the JSON serializer was configured to ignore properties with <code class=\"fm-code-in-text\">null<\/code> values, which is why the response doesn\u2019t include the navigation property defined by the <code class=\"fm-code-in-text\">Supplier<\/code> data model class.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-368\">20.2 Dealing with related data<\/h2>\n<p class=\"body\">Although this isn\u2019t a book about Entity Framework Core, there is one aspect of querying for data that most web services encounter. The data model classes defined in chapter 18 include navigation properties, which Entity Framework Core can populate by following relationships in the database when the <code class=\"fm-code-in-text\">Include<\/code> method is used, as shown in listing 20.4.<a id=\"calibre_link-2099\"><\/a><a id=\"calibre_link-2100\"><\/a><a id=\"calibre_link-944\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.4 Related data in the SuppliersController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n<b class=\"fm-bold\">using Microsoft.EntityFrameworkCore;<\/b>\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"api\/[controller]\")]\n    public class SuppliersController : ControllerBase {\n        private DataContext context;\n\n        public SuppliersController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet(\"{id}\")]\n        public async Task&lt;Supplier?&gt; GetSupplier(long id) {\n            <b class=\"fm-bold\">return await context.Suppliers<\/b>\n                <b class=\"fm-bold\">.Include(s =&gt; s.Products)<\/b>\n                <b class=\"fm-bold\">.FirstAsync(s =&gt; s.SupplierId == id);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Include<\/code> method tells Entity Framework Core to follow a relationship in the database and load the related data. In this case, the <code class=\"fm-code-in-text\">Include<\/code> method selects the <code class=\"fm-code-in-text\">Products<\/code> navigation property defined by the <code class=\"fm-code-in-text\">Supplier<\/code> class, which causes Entity Framework Core to load the <code class=\"fm-code-in-text\">Product<\/code> objects associated with the selected <code class=\"fm-code-in-text\">Supplier<\/code> and assign them to the <code class=\"fm-code-in-text\">Products<\/code> property.<a id=\"calibre_link-2101\"><\/a><a id=\"calibre_link-866\"><\/a><\/p>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/api\/suppliers\/1, which will target the <code class=\"fm-code-in-text\">GetSupplier<\/code> action method. The request fails, and you will see the exception shown in figure 20.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre178\" src=\"\/images\/proaspnetcore7\/000181.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.2 An exception caused by querying for related data<\/p>\n<\/div>\n<p class=\"body\">The JSON serializer has reported an \u201cobject cycle,\u201d which means there is a circular reference in the data that is being serialized for the response.<a id=\"calibre_link-2102\"><\/a><a id=\"calibre_link-2103\"><\/a><\/p>\n<p class=\"body\">Looking at the code in listing 20.4, you might struggle to see why using the <code class=\"fm-code-in-text\">Include<\/code> method has created a circular reference. The problem is caused by an Entity Framework Core feature that attempts to minimize the amount of data read from the database but that causes problems in ASP.NET Core applications.<\/p>\n<p class=\"body\">When Entity Framework Core creates objects, it populates navigation properties with objects that have already been created by the same database context. This can be a useful feature in some kinds of applications, such as desktop apps, where a database context object has a long life and is used to make many requests over time. It isn\u2019t useful for ASP.NET Core applications, where a new context object is created for each HTTP request.<\/p>\n<p class=\"body\">Entity Framework Core queries the database for the <code class=\"fm-code-in-text\">Product<\/code> objects associated with the selected <code class=\"fm-code-in-text\">Supplier<\/code> and assigns them to the <code class=\"fm-code-in-text\">Supplier.Products<\/code> navigation property. The problem is that Entity Framework Core then looks at each <code class=\"fm-code-in-text\">Product<\/code> object it has created and uses the query response to populate the <code class=\"fm-code-in-text\">Product.Supplier<\/code> navigation property as well. For an ASP.NET Core application, this is an unhelpful step to take because it creates a circular reference between the navigation properties of the <code class=\"fm-code-in-text\">Supplier<\/code> and <code class=\"fm-code-in-text\">Product<\/code> objects, as shown in figure 20.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre179\" src=\"\/images\/proaspnetcore7\/000182.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.3 Understanding how Entity Framework Core uses related data<\/p>\n<\/div>\n<p class=\"body\">When the <code class=\"fm-code-in-text\">Supplier<\/code> object is returned by the controller\u2019s action method, the JSON serializer works its way through the properties and follows the references to the <code class=\"fm-code-in-text\">Product<\/code> objects, each of which has a reference back to the <code class=\"fm-code-in-text\">Supplier<\/code> object, which it follows in a loop until the maximum depth is reached and the exception shown in figure 20.2 is thrown.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-369\">20.2.1 Breaking circular references in related data<\/h3>\n<p class=\"body\">There is no way to stop Entity Framework Core from creating circular references in the data it loads in the database. Preventing the exception means presenting the JSON serializer with data that doesn\u2019t contain circular references, which is most easily done by altering the objects after they have been created by Entity Framework Core and before they are serialized, as shown in listing 20.5.<a id=\"calibre_link-2104\"><\/a><a id=\"calibre_link-945\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.5 References in the SuppliersController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"api\/[controller]\")]\n    public class SuppliersController : ControllerBase {\n        private DataContext context;\n                \n        public SuppliersController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet(\"{id}\")]\n        public async Task&lt;Supplier?&gt; GetSupplier(long id) {\n            <b class=\"fm-bold\">Supplier supplier = await context.Suppliers<\/b>\n                <b class=\"fm-bold\">.Include(s =&gt; s.Products)<\/b>\n                <b class=\"fm-bold\">.FirstAsync(s =&gt; s.SupplierId == id);<\/b>\n            <b class=\"fm-bold\">if (supplier.Products != null) {<\/b>\n                <b class=\"fm-bold\">foreach (Product p in supplier.Products) {<\/b>\n                    <b class=\"fm-bold\">p.Supplier = null;<\/b>\n                <b class=\"fm-bold\">};<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return supplier;<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">foreach<\/code> loop sets the <code class=\"fm-code-in-text\">Supplier<\/code> property of each <code class=\"fm-code-in-text\">Product<\/code> object to <code class=\"fm-code-in-text\">null<\/code>, which breaks the circular references. Restart ASP.NET Core and request http:\/\/localhost:5000\/api\/suppliers\/1 to query for a supplier and its related products, which produces the response shown in figure 20.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre180\" src=\"\/images\/proaspnetcore7\/000183.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.4 Querying for related data<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-370\">20.3 Supporting the HTTP PATCH method<\/h2>\n<p class=\"body\">For simple data types, edit operations can be handled by replacing the existing object using the PUT method, which is the approach I took in chapter 19. Even if you only need to change a single property value in the <code class=\"fm-code-in-text\">Product<\/code> class, for example, it isn\u2019t too much trouble to use a PUT method and include the values for all the other <code class=\"fm-code-in-text\">Product<\/code> properties, too.<\/p>\n<p class=\"body\">Not all data types are as easy to work with, either because they define too many properties or because the client has received values only for selected properties. The solution is to use a PATCH request, which sends just the changes to the web service rather than a complete replacement object.<a id=\"calibre_link-2105\"><\/a><a id=\"calibre_link-2106\"><\/a><a id=\"calibre_link-2107\"><\/a><a id=\"calibre_link-1016\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-371\">20.3.1 Understanding JSON Patch<\/h3>\n<p class=\"body\">ASP.NET Core has support for working with the JSON Patch standard, which allows changes to be specified in a uniform way. The JSON Patch standard allows for a complex set of changes to be described, but for this chapter, I am going to focus on just the ability to change the value of a property.<a id=\"calibre_link-2108\"><\/a><a id=\"calibre_link-2109\"><\/a><a id=\"calibre_link-2110\"><\/a><\/p>\n<p class=\"body\">I am not going to go into the details of the JSON Patch standard, which you can read at <a class=\"url\" href=\"https:\/\/tools.ietf.org\/html\/rfc6902\">https:\/\/tools.ietf.org\/html\/rfc6902<\/a>, but the client is going to send the web service JSON data like this in its HTTP PATCH requests:<\/p>\n<pre class=\"programlisting\">[\n { \"op\": \"replace\", \"path\": \"Name\", \"value\": \"Surf Co\"},\n { \"op\": \"replace\", \"path\": \"City\", \"value\": \"Los Angeles\"},\n]<\/pre>\n<p class=\"body\">A JSON Patch document is expressed as an array of operations. Each operation has an <code class=\"fm-code-in-text\">op<\/code> property, which specifies the type of operation, and a <code class=\"fm-code-in-text\">path<\/code> property, which specifies where the operation will be applied.<\/p>\n<p class=\"body\">For the example application&mdash;and, in fact, for most applications&mdash;only the <code class=\"fm-code-in-text\">replace<\/code> operation is required, which is used to change the value of a property. This JSON Patch document sets new values for the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">City<\/code> properties. The properties defined by the <code class=\"fm-code-in-text\">Supplier<\/code> class not mentioned in the JSON Patch document will not be modified.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-372\">20.3.2 Installing and configuring the JSON Patch package<\/h3>\n<p class=\"body\">Support for JSON Patch isn\u2019t installed when a project is created with the Empty template. To install the JSON Patch package, open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 20.6. If you are using Visual Studio, you can install the package by selecting Project &gt; Manage NuGet Packages.<a id=\"calibre_link-2111\"><\/a><a id=\"calibre_link-1015\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.6 Installing the JSON Patch package<\/p>\n<pre class=\"programlisting\">dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson --version 7.0.0<\/pre>\n<p class=\"body\">The Microsoft implementation of JSON Patch relies on the third-party Newtonsoft JSON.NET serializer. Add the statements shown in listing 20.7 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file to enable the JSON.NET serializer.<a id=\"calibre_link-2112\"><\/a><a id=\"calibre_link-2113\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.7 Enabling the serializer in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc;\n<b class=\"fm-bold\">\/\/using System.Text.Json.Serialization;<\/b>\nusing Microsoft.AspNetCore.RateLimiting;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\n<b class=\"fm-bold\">builder.Services.AddControllers().AddNewtonsoftJson();<\/b>\n\nbuilder.Services.AddRateLimiter(opts =&gt; {\n    opts.AddFixedWindowLimiter(\"fixedWindow\", fixOpts =&gt; {\n        fixOpts.PermitLimit = 1;\n        fixOpts.QueueLimit = 0;\n        fixOpts.Window = TimeSpan.FromSeconds(15);\n    });\n});\n\n<b class=\"fm-bold\">\/\/builder.Services.Configure&lt;JsonOptions&gt;(opts =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/    opts.JsonSerializerOptions.DefaultIgnoreCondition<\/b>\n<b class=\"fm-bold\">\/\/        = JsonIgnoreCondition.WhenWritingNull;<\/b>\n<b class=\"fm-bold\">\/\/});<\/b>\n\n<b class=\"fm-bold\">builder.Services.Configure&lt;MvcNewtonsoftJsonOptions&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.SerializerSettings.NullValueHandling<\/b>\n        <b class=\"fm-bold\">= Newtonsoft.Json.NullValueHandling.Ignore;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseRateLimiter();\n\napp.MapControllers();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddNewtonsoftJson<\/code> method enables the JSON.NET serializer, which replaces the standard ASP.NET Core serializer. The JSON.NET serializer has its own configuration class, <code class=\"fm-code-in-text\">MvcNewtonsoftJsonOptions<\/code>, which is applied through the options pattern. Listing 20.7 sets the <code class=\"fm-code-in-text\">NullValueHandling<\/code> value, which tells the serializer to discard properties with <code class=\"fm-code-in-text\">null<\/code> values.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> See <a class=\"url\" href=\"https:\/\/www.newtonsoft.com\/json\">https:\/\/www.newtonsoft.com\/json<\/a> for details of the other configuration options available for the JSON.NET serializer.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-373\">20.3.3 Defining the action method<\/h3>\n<p class=\"body\">To add support for the PATCH method, add the action method shown in listing 20.8 to the <code class=\"fm-code-in-text\">SuppliersController<\/code> class.<a id=\"calibre_link-2114\"><\/a><a id=\"calibre_link-1258\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.8 Adding an action in the SuppliersController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.JsonPatch;<\/b>\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"api\/[controller]\")]\n    public class SuppliersController : ControllerBase {\n        private DataContext context;\n                \n        public SuppliersController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet(\"{id}\")]\n        public async Task&lt;Supplier?&gt; GetSupplier(long id) {\n            Supplier supplier = await context.Suppliers\n                .Include(s =&gt; s.Products)\n                .FirstAsync(s =&gt; s.SupplierId == id);\n            if (supplier.Products != null) {\n                foreach (Product p in supplier.Products) {\n                    p.Supplier = null;\n                };\n            }\n            return supplier;\n        }\n                \n        <b class=\"fm-bold\">[HttpPatch(\"{id}\")]<\/b>\n        <b class=\"fm-bold\">public async Task&lt;Supplier?&gt; PatchSupplier(long id,<\/b>\n                <b class=\"fm-bold\">JsonPatchDocument&lt;Supplier&gt; patchDoc) {<\/b>\n            <b class=\"fm-bold\">Supplier? s = await context.Suppliers.FindAsync(id);<\/b>\n            <b class=\"fm-bold\">if (s != null) {<\/b>\n                <b class=\"fm-bold\">patchDoc.ApplyTo(s);<\/b>\n                <b class=\"fm-bold\">await context.SaveChangesAsync();<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return s;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The action method is decorated with the <code class=\"fm-code-in-text\">HttpPatch<\/code> attribute, which denotes that it will handle HTTP PATCH requests. The model binding feature is used to process the JSON Patch document through a <code class=\"fm-code-in-text\">JsonPatchDocument&lt;T&gt;<\/code> method parameter. The <code class=\"fm-code-in-text\">JsonPatchDocument&lt;T&gt;<\/code> class defines an <code class=\"fm-code-in-text\">ApplyTo<\/code> method, which applies each operation to an object. The action method in listing 20.8 retrieves a <code class=\"fm-code-in-text\">Supplier<\/code> object from the database, applies the JSON PATCH, and stores the modified object.<a id=\"calibre_link-2115\"><\/a><a id=\"calibre_link-1259\"><\/a><\/p>\n<p class=\"body\">Restart ASP.NET Core and use a separate PowerShell command prompt to run the command shown in listing 20.9, which sends an HTTP PATCH request with a JSON PATCH document that changes the value of the <code class=\"fm-code-in-text\">City<\/code> property to <code class=\"fm-code-in-text\">Los Angeles<\/code>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.9 Sending an HTTP PATCH request<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/api\/suppliers\/1 -Method PATCH \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>-ContentType \"application\/json\" -Body '[{\"op\":\"replace\",\"path\":\"City\",\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>\"value\":\"Los Angeles\"}]'<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">PatchSupplier<\/code> action method returns the modified <code class=\"fm-code-in-text\">Supplier<\/code> object as its result, which is serialized and sent to the client in the HTTP response. You can also see the effect of the change by using a web browser to request http:\/\/localhost:5000\/suppliers\/1, which produces the response shown in figure 20.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre181\" src=\"\/images\/proaspnetcore7\/000184.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.5 Updating using a PATCH request<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-374\">20.4 Understanding content formatting<\/h2>\n<p class=\"body\">The web service examples so far have produced JSON results, but this is not the only data format that action methods can produce. The content format selected for an action result depends on four factors: the formats that the client will accept, the formats that the application can produce, the content policy specified by the action method, and the type returned by the action method. Figuring out how everything fits together can be daunting, but the good news is that the default policy works just fine for most applications, and you only need to understand what happens behind the scenes when you need to make a change or when you are not getting results in the format that you expect.<a id=\"calibre_link-2116\"><\/a><a id=\"calibre_link-1252\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-375\">20.4.1 Understanding the default content policy<\/h3>\n<p class=\"body\">The best way to get acquainted with content formatting is to understand what happens when neither the client nor the action method applies any restrictions to the formats that can be used. In this situation, the outcome is simple and predictable.<a id=\"calibre_link-2117\"><\/a><\/p>\n<ol class=\"calibre182\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">If the action method returns a <code class=\"fm-code-in-text\">string<\/code>, the string is sent unmodified to the client, and the <code class=\"fm-code-in-text\">Content-Type<\/code> header of the response is set to <code class=\"fm-code-in-text\">text\/plain<\/code>.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">For all other data types, including other simple types such as <code class=\"fm-code-in-text\">int<\/code>, the data is formatted as JSON, and the <code class=\"fm-code-in-text\">Content-Type<\/code> header of the response is set to <code class=\"fm-code-in-text\">application\/json<\/code>.<\/p>\n<\/li>\n<\/ol>\n<p class=\"body\">Strings get special treatment because they cause problems when they are encoded as JSON. When you encode other simple types, such as the C# <code class=\"fm-code-in-text\">int<\/code> value <code class=\"fm-code-in-text\">2<\/code>, then the result is a quoted string, such as <code class=\"fm-code-in-text\">\"2\"<\/code>. When you encode a string, you end up with two sets of quotes so that <code class=\"fm-code-in-text\">\"Hello\"<\/code> becomes <code class=\"fm-code-in-text\">\"\"Hello\"\"<\/code>. Not all clients cope well with this double encoding, so it is more reliable to use the <code class=\"fm-code-in-text\">text\/plain<\/code> format and sidestep the issue entirely. This is rarely an issue because few applications send <code class=\"fm-code-in-text\">string<\/code> values; it is more common to send objects in the JSON format. To see the default policy, add a class file named <code class=\"fm-code-in-text\">ContentController.cs<\/code> to the <code class=\"fm-code-in-text\">WebApps\/Controllers<\/code> folder with the code shown in listing 20.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.10 The contents of the ContentController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/[controller]\")]\n    public class ContentController : ControllerBase {\n        private DataContext context;\n                \n        public ContentController(DataContext dataContext) {\n            context = dataContext;\n        }\n                \n        [HttpGet(\"string\")]\n        public string GetString() =&gt; \"This is a string response\";\n                \n        [HttpGet(\"object\")]\n        public async Task&lt;Product&gt; GetObject() {\n            return await context.Products.FirstAsync();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The controller defines actions that return string and object results. Restart ASP.NET Core and use a separate PowerShell prompt to run the command shown in listing 20.11; this command sends a request that invokes the <code class=\"fm-code-in-text\">GetString<\/code> action method, which returns a string.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.11 Requesting a string response<\/p>\n<pre class=\"programlisting\">Invoke-WebRequest http:\/\/localhost:5000\/api\/content\/string | select \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>@{n='Content-Type';e={ $_.Headers.\"Content-Type\" }}, Content<\/pre>\n<p class=\"body\">This command sends a GET request to the <code class=\"fm-code-in-text\">\/api\/content\/string<\/code> URL and processes the response to display the <code class=\"fm-code-in-text\">Content-Type<\/code> header and the content from the response. The command produces the following output, which shows the <code class=\"fm-code-in-text\">Content-Type<\/code> header for the response:<\/p>\n<pre class=\"programlisting\">Content-Type              Content\n------------              -------\ntext\/plain; charset=utf-8 This is a string response<\/pre>\n<p class=\"body\">Next, run the command shown in listing 20.12, which sends a request that will be handled by the <code class=\"fm-code-in-text\">GetObject<\/code> action method.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.12 Requesting an object response<\/p>\n<pre class=\"programlisting\">Invoke-WebRequest <b class=\"fm-bold\">http:\/\/localhost:5000\/api\/content\/object<\/b> | select \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>@{n='Content-Type';e={ $_.Headers.\"Content-Type\" }}, Content<\/pre>\n<p class=\"body\">This command produces the following output, formatted for clarity, that shows that the response has been encoded as JSON:<\/p>\n<pre class=\"programlisting\">Content-Type                    Content\n------------                    -------\napplication\/json; charset=utf-8 {\"productId\":1,\"name\":\"Kayak\",\n    \"price\":275.00,\"categoryId\":1,\"supplierId\":1}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-376\">20.4.2 Understanding content negotiation<\/h3>\n<p class=\"body\">Most clients include an <code class=\"fm-code-in-text\">Accept<\/code> header in a request, which specifies the set of formats that they are willing to receive in the response, expressed as a set of MIME types. Here is the <code class=\"fm-code-in-text\">Accept<\/code> header that Google Chrome sends in requests:<a id=\"calibre_link-2118\"><\/a><a id=\"calibre_link-2119\"><\/a><a id=\"calibre_link-892\"><\/a><\/p>\n<pre class=\"programlisting\">Accept: text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/avif,\n    image\/webp,image\/apng,*\/*;q=0.8,application\/signed-exchange;v=b3;q=0.9<\/pre>\n<p class=\"body\">This header indicates that Chrome can handle the HTML and XHTML formats (XHTML is an XML-compliant dialect of HTML), XML, and the AVIF, WEBP, and APNG formats. Chrome also supports the <code class=\"fm-code-in-text\">application\/signed-exchange<\/code>, which is the data type used for signed exchanges, which allow the origin of content to be validated regardless of how it has been delivered.<a id=\"calibre_link-2120\"><\/a><\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">q<\/code> values in the header specify relative preference, where the value is 1.0 by default. Specifying a <code class=\"fm-code-in-text\">q<\/code> value of 0.9 for <code class=\"fm-code-in-text\">application\/xml<\/code> tells the server that Chrome will accept XML data but prefers to deal with HTML or XHTML. The <code class=\"fm-code-in-text\">*\/*<\/code> item tells the server that Chrome will accept any format, but its <code class=\"fm-code-in-text\">q<\/code> value specifies that it is the lowest preference of the specified types. Putting this together means that the <code class=\"fm-code-in-text\">Accept<\/code> header sent by Chrome provides the server with the following information:<\/p>\n<ol class=\"calibre182\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Chrome prefers to receive HTML or XHTML data and AVIF, WEBP, and APNG images.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">If those formats are not available, then the next most preferred format is XML or a signed exchange.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">If none of the preferred formats is available, then Chrome will accept any format.<\/p>\n<\/li>\n<\/ol>\n<p class=\"body\">You might assume from this that you can change the format produced by the ASP.NET Core application by setting the <code class=\"fm-code-in-text\">Accept<\/code> header, but it doesn\u2019t work that way&mdash;or, rather, it doesn\u2019t work that way just yet because there is some preparation required.<\/p>\n<p class=\"body\">To see what happens when the <code class=\"fm-code-in-text\">Accept<\/code> header is changed, use a PowerShell prompt to run the command shown in listing 20.13, which sets the <code class=\"fm-code-in-text\">Accept<\/code> header to tell ASP.NET Core that the client is willing to receive only XML data.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.13 Requesting XML data<\/p>\n<pre class=\"programlisting\">Invoke-WebRequest http:\/\/localhost:5000\/api\/content\/object \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span><b class=\"fm-bold\">-Headers @{Accept=\"application\/xml\"}<\/b> | select @{n='Content-Type';e={ \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>$_.Headers.\"Content-Type\" }}, Content<\/pre>\n<p class=\"body\">Here are the results, which show that the application has sent an <code class=\"fm-code-in-text\">application\/json<\/code> response:<\/p>\n<pre class=\"programlisting\">Content-Type                    Content\n------------                    -------\napplication\/json; charset=utf-8 {\"productId\":1,\"name\":\"Kayak\",\n \"price\":275.00,\"categoryId\":1,\"supplierId\":1}<\/pre>\n<p class=\"body\">Including the <code class=\"fm-code-in-text\">Accept<\/code> header has no effect on the format, even though the ASP.NET Core application sent the client a format that it hasn\u2019t specified. The problem is that, by default, the MVC Framework is configured to only use JSON. Rather than return an error, the MVC Framework sends JSON data in the hope that the client can process it, even though it was not one of the formats specified by the request <code class=\"fm-code-in-text\">Accept<\/code> header.<\/p>\n<p class=\"fm-head2\">Enabling XML formatting<\/p>\n<p class=\"body\">For content negotiation to work, the application must be configured so there is some choice in the formats that can be used. Although JSON has become the default format for web applications, the MVC Framework can also support encoding data as XML, as shown in listing 20.14.<a id=\"calibre_link-2121\"><\/a><a id=\"calibre_link-2122\"><\/a><a id=\"calibre_link-2123\"><\/a><a id=\"calibre_link-1253\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can create your own content format by deriving from the <code class=\"fm-code-in-text1\">Microsoft.AspNetCore.Mvc.Formatters.OutputFormatter<\/code> class. This is rarely used because creating a custom data format isn\u2019t a useful way of exposing the data in your application, and the most common formats&mdash;JSON and XML&mdash;are already implemented.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.14 Enabling XML formatting in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.RateLimiting;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\n<b class=\"fm-bold\">builder.Services.AddControllers()<\/b>\n    <b class=\"fm-bold\">.AddNewtonsoftJson().AddXmlDataContractSerializerFormatters();<\/b>\n        \nbuilder.Services.AddRateLimiter(opts =&gt; {\n    opts.AddFixedWindowLimiter(\"fixedWindow\", fixOpts =&gt; {\n        fixOpts.PermitLimit = 1;\n        fixOpts.QueueLimit = 0;\n        fixOpts.Window = TimeSpan.FromSeconds(15);\n    });\n});\n\nbuilder.Services.Configure&lt;MvcNewtonsoftJsonOptions&gt;(opts =&gt; {\n    opts.SerializerSettings.NullValueHandling\n        = Newtonsoft.Json.NullValueHandling.Ignore;\n});\n\nvar app = builder.Build();\n\napp.UseRateLimiter();\n\napp.MapControllers();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The XML Serializer has some limitations, including the inability to deal with Entity Framework Core navigation properties because they are defined through an interface. To create an object that can be serialized, listing 20.15 uses <code class=\"fm-code-in-text\">ProductBindingTarget<\/code> defined in chapter 19.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.15 Creating an object in the ContentController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/[controller]\")]\n    public class ContentController : ControllerBase {\n        private DataContext context;\n                \n        public ContentController(DataContext dataContext) {\n            context = dataContext;\n        }\n                \n        [HttpGet(\"string\")]\n        public string GetString() =&gt; \"This is a string response\";\n                \n        [HttpGet(\"object\")]\n        <b class=\"fm-bold\">public async Task&lt;ProductBindingTarget&gt; GetObject() {<\/b>\n            <b class=\"fm-bold\">Product p = await context.Products.FirstAsync();<\/b>\n            <b class=\"fm-bold\">return new ProductBindingTarget() {<\/b>\n                <b class=\"fm-bold\">Name = p.Name, Price = p.Price, CategoryId = p.CategoryId,<\/b>\n                <b class=\"fm-bold\">SupplierId = p.SupplierId<\/b>\n            <b class=\"fm-bold\">};<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">When the MVC Framework had only the JSON format available, it had no choice but to encode responses as JSON. Now that there is a choice, you can see the content negotiation process working more fully. Restart ASP.NET Core MVC and run the command in listing 20.13 again to request XML data, and you will see the following output (from which I have omitted the namespace attributes for brevity):<\/p>\n<pre class=\"programlisting\">Content-Type                   Content\n------------                   -------\napplication\/xml; charset=utf-8 &lt;ProductBindingTarget&gt;                                \n                                 &lt;Name&gt;Kayak&lt;\/Name&gt;                                \n                                 &lt;Price&gt;275.00&lt;\/Price&gt;                                 \n                                 &lt;CategoryId&gt;1&lt;\/CategoryId&gt;                                 \n                                 &lt;SupplierId&gt;1&lt;\/SupplierId&gt;                               \n                               &lt;\/ProductBindingTarget&gt;<\/pre>\n<p class=\"fm-head2\">Fully respecting accept headers<\/p>\n<p class=\"body\">The MVC Framework will always use the JSON format if the <code class=\"fm-code-in-text\">Accept<\/code> header contains <code class=\"fm-code-in-text\">*\/*<\/code>, indicating any format, even if there are other supported formats with a higher preference. This is an odd feature that is intended to deal with requests from browsers consistently, although it can be a source of confusion. Run the command shown in listing 20.16 to send a request with an <code class=\"fm-code-in-text\">Accept<\/code> header that requests XML but will accept any other format if XML isn\u2019t available.<a id=\"calibre_link-1255\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.16 Requesting an XML respo<a id=\"calibre_link-2124\"><\/a>nse with a fallback<\/p>\n<pre class=\"programlisting\">Invoke-WebRequest http:\/\/localhost:5000\/api\/content\/object -Headers \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span><b class=\"fm-bold\">@{Accept=\"application\/xml,*\/*;q=0.8\"}<\/b> | select @{n='Content-Type';\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>e={ $_.Headers.\"Content-Type\" }}, Content<\/pre>\n<p class=\"body\">Even though the <code class=\"fm-code-in-text\">Accept<\/code> header tells the MVC Framework that the client prefers XML, the presence of the <code class=\"fm-code-in-text\">*\/*<\/code> fallback means that a JSON response is sent. A related problem is that a JSON response will be sent when the client requests a format that the MVC Framework hasn\u2019t been configured to produce, which you can see by running the command shown in listing 20.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.17 Requesting a PNG response<\/p>\n<pre class=\"programlisting\">Invoke-WebRequest http:\/\/localhost:5000\/api\/content\/object -Headers \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span><b class=\"fm-bold\">@{Accept=\"img\/png\"}<\/b> | select @{n='Content-Type';e={ $_.Headers.\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>\"Content-Type\" }}, Content<\/pre>\n<p class=\"body\">The commands in listing 20.16 and listing 20.17 both produce this response:<\/p>\n<pre class=\"programlisting\">Content-Type                    Content\n------------                    -------\napplication\/json; charset=utf-8 {\"name\":\"Kayak\",\"price\":275.00,                                \n                                 \"categoryId\":1,\"supplierId\":1}<\/pre>\n<p class=\"body\">In both cases, the MVC Framework returns JSON data, which may not be what the client is expecting. Two configuration settings are used to tell the MVC Framework to respect the <code class=\"fm-code-in-text\">Accept<\/code> setting sent by the client and not send JSON data by default. To change the configuration, add the statements shown in listing 20.18 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<a id=\"calibre_link-2125\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.18 Configuring negotiation in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.RateLimiting;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddControllers()\n    .AddNewtonsoftJson().AddXmlDataContractSerializerFormatters();\n        \nbuilder.Services.AddRateLimiter(opts =&gt; {\n    opts.AddFixedWindowLimiter(\"fixedWindow\", fixOpts =&gt; {\n        fixOpts.PermitLimit = 1;\n        fixOpts.QueueLimit = 0;\n        fixOpts.Window = TimeSpan.FromSeconds(15);\n    });\n});\n\nbuilder.Services.Configure&lt;MvcNewtonsoftJsonOptions&gt;(opts =&gt; {\n    opts.SerializerSettings.NullValueHandling\n        = Newtonsoft.Json.NullValueHandling.Ignore;\n});\n\n<b class=\"fm-bold\">builder.Services.Configure&lt;MvcOptions&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.RespectBrowserAcceptHeader = true;<\/b>\n    <b class=\"fm-bold\">opts.ReturnHttpNotAcceptable = true;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseRateLimiter();\n\napp.MapControllers();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The options pattern is used to set the properties of an <code class=\"fm-code-in-text\">MvcOptions<\/code> object. Setting <code class=\"fm-code-in-text\">RespectBrowserAcceptHeader<\/code> to <code class=\"fm-code-in-text\">true<\/code> disables the fallback to JSON when the <code class=\"fm-code-in-text\">Accept<\/code> header contains <code class=\"fm-code-in-text\">*\/*<\/code>. Setting <code class=\"fm-code-in-text\">ReturnHttpNotAcceptable<\/code> to <code class=\"fm-code-in-text\">true<\/code> disables the fallback to JSON when the client requests an unsupported data format.<\/p>\n<p class=\"body\">Restart ASP.NET Core and repeat the command from listing 20.16. Instead of a JSON response, the format preferences specified by the <code class=\"fm-code-in-text\">Accept<\/code> header will be respected, and an XML response will be sent. Repeat the command from listing 20.17, and you will receive a response with the 406 status code.<\/p>\n<pre class=\"programlisting\">...\nInvoke-WebRequest : The remote server returned an error:\n    (406) Not Acceptable.\n...<\/pre>\n<p class=\"body\">Sending a 406 code indicates there is no overlap between the formats the client can handle and the formats that the MVC Framework can produce, ensuring that the client doesn\u2019t receive a data format it cannot process.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-377\">20.4.3 Specifying an action result format<\/h3>\n<p class=\"body\"><a id=\"calibre_link-2126\"><\/a>The data formats that the MVC Framework can use for an action method result can be constrained using the <code class=\"fm-code-in-text\">Produces<\/code> attribute, as shown in listing 20.19.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">Produces<\/code> attribute is an example of a filter, which allows attributes to alter requests and responses. See chapter 30 for more details.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.19 Data formats in the ContentController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/[controller]\")]\n    public class ContentController : ControllerBase {\n        private DataContext context;\n                \n        public ContentController(DataContext dataContext) {\n            context = dataContext;\n        }\n                \n        [HttpGet(\"string\")]\n        public string GetString() =&gt; \"This is a string response\";\n                \n        [HttpGet(\"object\")]\n        <b class=\"fm-bold\">[Produces(\"application\/json\")]<\/b>\n        public async Task&lt;ProductBindingTarget&gt; GetObject() {\n            Product p = await context.Products.FirstAsync();\n            return new ProductBindingTarget() {\n                Name = p.Name, Price = p.Price, CategoryId = p.CategoryId,\n                SupplierId = p.SupplierId\n            };\n        }\n    }\n}<\/pre>\n<p class=\"body\">The argument for the attribute specifies the format that will be used for the result from the action, and more than one type can be specified. The <code class=\"fm-code-in-text\">Produces<\/code> attribute restricts the types that the MVC Framework will consider when processing an <code class=\"fm-code-in-text\">Accept<\/code> header. To see the effect of the <code class=\"fm-code-in-text\">Produces<\/code> attribute, use a PowerShell prompt to run the command shown in listing 20.20.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.20 Requesting data<\/p>\n<pre class=\"programlisting\">Invoke-WebRequest http:\/\/localhost:5000\/api\/content\/object -Headers \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span><b class=\"fm-bold\">@{Accept=\"application\/xml,application\/json;q=0.8\"}<\/b> | select \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>@{n='Content-Type';e={ $_.Headers.\"Content-Type\" }}, Content<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Accept<\/code> header tells the MVC Framework that the client prefers XML data but will accept JSON. The <code class=\"fm-code-in-text\">Produces<\/code> attribute means that XML data isn\u2019t available as the data format for the <code class=\"fm-code-in-text\">GetObject<\/code> action method and so the JSON serializer is selected, which produces the following response:<\/p>\n<pre class=\"programlisting\">Content-Type                    Content\n------------                    -------\napplication\/json; charset=utf-8 {\"name\":\"Kayak\",\"price\":275.00,                                \n                                 \"categoryId\":1,\"supplierId\":1}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-378\">20.4.4 Requesting a format in the URL<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Accept<\/code> header isn\u2019t always under the control of the programmer who is writing the client. In such situations, it can be helpful to allow the data format for the response to be requested using the URL. This feature is enabled by decorating an action method with the <code class=\"fm-code-in-text\">FormatFilter<\/code> attribute and ensuring there is a <code class=\"fm-code-in-text\">format<\/code> segment variable in the action method\u2019s route, as shown in listing 20.21.<a id=\"calibre_link-2127\"><\/a><a id=\"calibre_link-1256\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.21 Formatting in the ContentController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/[controller]\")]\n    public class ContentController : ControllerBase {\n        private DataContext context;\n                \n        public ContentController(DataContext dataContext) {\n            context = dataContext;\n        }\n                \n        [HttpGet(\"string\")]\n        public string GetString() =&gt; \"This is a string response\";\n                \n        <b class=\"fm-bold\">[HttpGet(\"object\/{format?}\")]<\/b>\n        <b class=\"fm-bold\">[FormatFilter]<\/b>\n        <b class=\"fm-bold\">[Produces(\"application\/json\", \"application\/xml\")]<\/b>\n        public async Task&lt;ProductBindingTarget&gt; GetObject() {\n            Product p = await context.Products.FirstAsync();\n            return new ProductBindingTarget() {\n                Name = p.Name, Price = p.Price, CategoryId = p.CategoryId,\n                SupplierId = p.SupplierId\n            };\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">FormatFilter<\/code> attribute is an example of a filter, which is an attribute that can modify requests and responses, as described in chapter 30. This filter gets the value of the <code class=\"fm-code-in-text\">format<\/code> segment variable from the route that matched the request and uses it to override the <code class=\"fm-code-in-text\">Accept<\/code> header sent by the client. I have also expanded the range of types specified by the <code class=\"fm-code-in-text\">Produces<\/code> attribute so that the action method can return both JSON and XML responses.<\/p>\n<p class=\"body\">Each data format supported by the application has a shorthand: <code class=\"fm-code-in-text\">xml<\/code> for XML data, and <code class=\"fm-code-in-text\">json<\/code> for JSON data. When the action method is targeted by a URL that contains one of these shorthand names, the <code class=\"fm-code-in-text\">Accept<\/code> header is ignored, and the specified format is used. To see the effect, restart ASP.NET Core and use the browser to request http:\/\/localhost:5000\/api\/content\/object\/json and http:\/\/localhost:5000\/api\/content\/object\/xml, which produce the responses shown in figure 20.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre183\" src=\"\/images\/proaspnetcore7\/000185.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.6 Requesting data formats in the URL<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-379\">20.4.5 Restricting the formats received by an action method<\/h3>\n<p class=\"body\">Most content formatting decisions focus on the data formats the ASP.NET Core application sends to the client, but the same serializers that deal with results are used to deserialize the data sent by clients in request bodies. The deserialization process happens automatically, and most applications will be happy to accept data in all the formats they are configured to send. The example application is configured to send JSON and XML data, which means that clients can send JSON and XML data in requests.<\/p>\n<p class=\"body\"><a id=\"calibre_link-1254\"><\/a>The <code class=\"fm-code-in-text\">Consumes<\/code> attribute can be applied to action methods to restrict the data types it will handle, as shown in listing 20.22.<a id=\"calibre_link-2128\"><\/a><a id=\"calibre_link-2129\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.22 Adding actions in the ContentController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/[controller]\")]\n    public class ContentController : ControllerBase {\n        private DataContext context;\n                \n        public ContentController(DataContext dataContext) {\n            context = dataContext;\n        }\n                \n        [HttpGet(\"string\")]\n        public string GetString() =&gt; \"This is a string response\";\n\n        [HttpGet(\"object\/{format?}\")]\n        [FormatFilter]\n        [Produces(\"application\/json\", \"application\/xml\")]\n        public async Task&lt;ProductBindingTarget&gt; GetObject() {\n            Product p = await context.Products.FirstAsync();\n            return new ProductBindingTarget() {\n                Name = p.Name, Price = p.Price, CategoryId = p.CategoryId,\n                SupplierId = p.SupplierId\n            };\n        }\n                \n        <b class=\"fm-bold\">[HttpPost]<\/b>\n        <b class=\"fm-bold\">[Consumes(\"application\/json\")]<\/b>\n        <b class=\"fm-bold\">public string SaveProductJson(ProductBindingTarget product) {<\/b>\n            <b class=\"fm-bold\">return $\"JSON: {product.Name}\";<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">[HttpPost]<\/b>\n        <b class=\"fm-bold\">[Consumes(\"application\/xml\")]<\/b>\n        <b class=\"fm-bold\">public string SaveProductXml(ProductBindingTarget product) {<\/b>\n            <b class=\"fm-bold\">return $\"XML: {product.Name}\";<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The new action methods are decorated with the <code class=\"fm-code-in-text\">Consumes<\/code> attribute, restricting the data types that each can handle. The combination of attributes means that HTTP POST attributes whose <code class=\"fm-code-in-text\">Content-Type<\/code> header is <code class=\"fm-code-in-text\">application\/json<\/code> will be handled by the <code class=\"fm-code-in-text\">SaveProductJson<\/code> action method. HTTP POST requests whose <code class=\"fm-code-in-text\">Content-Type<\/code> header is <code class=\"fm-code-in-text\">application\/xml<\/code> will be handled by the <code class=\"fm-code-in-text\">SaveProductXml<\/code> action method. Restart ASP.NET Core and use a PowerShell command prompt to run the command shown in listing 20.23 to send JSON data to the example application.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.23 Sending JSON data<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/api\/content -Method POST -Body  \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>(@{ Name=\"Swimming Goggles\"; Price=12.75; CategoryId=1; SupplierId=1} | \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>ConvertTo-Json) -ContentType \"application\/json\"<\/pre>\n<p class=\"body\">The request is automatically routed to the correct action method, which produces the following response:<\/p>\n<pre class=\"programlisting\">JSON: Swimming Goggles<\/pre>\n<p class=\"body\">Run the command shown in listing 20.24 to send XML data to the example application.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.24 Sending XML data<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/api\/content -Method POST -Body \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>\"&lt;ProductBindingTarget xmlns=`\"http:\/\/schemas.datacontract.org\/2004\/07\/\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>WebApp.Models`\"&gt; &lt;CategoryId&gt;1&lt;\/CategoryId&gt;&lt;Name&gt;Kayak&lt;\/Name&gt;&lt;Price&gt;\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>275.00&lt;\/Price&gt; &lt;SupplierId&gt;1&lt;\/SupplierId&gt;&lt;\/ProductBindingTarget&gt;\"\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span> -ContentType \"application\/xml\"<\/pre>\n<p class=\"body\">The request is routed to the <code class=\"fm-code-in-text\">SaveProductXml<\/code> action method and produces the following response:<\/p>\n<pre class=\"programlisting\">XML: Kayak<\/pre>\n<p class=\"body\">The MVC Framework will send a <code class=\"fm-code-in-text\">415 - Unsupported Media Type<\/code> response if a request is sent with a <code class=\"fm-code-in-text\">Content-Type<\/code> header that doesn\u2019t match the data types that the application supports.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-380\">20.4.6 Caching output<\/h3>\n<p class=\"body\">In chapter 7, I demonstrated the output caching middleware, which allows caching policies to be defined and applied to endpoints. This feature can be extended to controllers using attributes, allowing fine-grained control over how responses from controllers are cached. To prepare, listing 20.25 configures the example application to set up a caching policy.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.25 Configuring caching in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.RateLimiting;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n\/\/ <b class=\"fm-bold\">...<i class=\"fm-italics\">statements omitted for brevity<\/i>...<\/b>\n\nbuilder.Services.Configure&lt;MvcOptions&gt;(opts =&gt; {\n    opts.RespectBrowserAcceptHeader = true;\n    opts.ReturnHttpNotAcceptable = true;\n});\n\n<b class=\"fm-bold\">builder.Services.AddOutputCache(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.AddPolicy(\"30sec\", policy =&gt; {<\/b>\n        <b class=\"fm-bold\">policy.Cache();<\/b>\n        <b class=\"fm-bold\">policy.Expire(TimeSpan.FromSeconds(30));<\/b>\n    <b class=\"fm-bold\">});<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseRateLimiter();\n<b class=\"fm-bold\">app.UseOutputCache();<\/b>\napp.MapControllers();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">This configuration creates an output caching policy named <code class=\"fm-code-in-text\">30sec<\/code>, which caches content for 30 seconds. The caching policy is applied to the controller using the <code class=\"fm-code-in-text\">OutputCache<\/code> attribute, as shown in listing 20.26.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.26 Caching in the ContentController.cs file in the WebApp\/Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.OutputCaching;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers \n\n    [ApiController]\n    [Route(\"\/api\/[controller]\")]\n    public class ContentController : ControllerBase {\n        private DataContext context;\n                \n        public ContentController(DataContext dataContext) {\n            context = dataContext;\n        }\n                \n        [HttpGet(\"string\")]\n        <b class=\"fm-bold\">[OutputCache(PolicyName = \"30sec\")]<\/b>\n        <b class=\"fm-bold\">[Produces(\"application\/json\")]<\/b>\n        <b class=\"fm-bold\">public string GetString() =&gt;<\/b> \n            <b class=\"fm-bold\">$\"{DateTime.Now.ToLongTimeString()} String response\";<\/b>\n        \n                \n        \/\/ <i class=\"fm-italics\">...other actions omitted for brevity...<\/i>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OutputCache<\/code> attribute can be applied to the entire controller, which causes the responses for all action methods, or applied to individual actions. The attribute accepts a policy name as an argument or can be used to create a custom policy. The attribute in listing 20.26 has been applied a single action and applies the policy created in listing 20.25. (I added the <code class=\"fm-code-in-text\">Produces<\/code> attribute to force a JSON response, which is not required for caching but makes the response easier to see in the browser window).<\/p>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/api\/content\/string. Reload the browser and you will see the same time displayed, illustrating that output is cached for 30 seconds, as shown in figure 20.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre184\" src=\"\/images\/proaspnetcore7\/000186.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.7 Cached output from a controller action<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-381\">20.5 Documenting and exploring web services<\/h2>\n<p class=\"body\">When you are responsible for developing both the web service and its client, the purpose of each action and its results are obvious and are usually written at the same time. If you are responsible for a web service that is consumed by third-party developers, then you may need to provide documentation that describes how the web service works. The OpenAPI specification, which is also known as Swagger, describes web services in a way that can be understood by other programmers and consumed programmatically. In this section, I demonstrate how to use OpenAPI to describe a web service and show you how to fine-tune that description.<a id=\"calibre_link-2130\"><\/a><a id=\"calibre_link-2131\"><\/a><a id=\"calibre_link-1082\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-382\">20.5.1 Resolving action conflicts<\/h3>\n<p class=\"body\">The OpenAPI discovery process requires a unique combination of the HTTP method and URL pattern for each action method. The process doesn\u2019t support the <code class=\"fm-code-in-text\">Consumes<\/code> attribute, so a change is required to the <code class=\"fm-code-in-text\">ContentController<\/code> to remove the separate actions for receiving XML and JSON data, as shown in listing 20.27.<a id=\"calibre_link-2132\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.27 Removing actions in the ContentController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.OutputCaching;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/[controller]\")]\n    public class ContentController : ControllerBase {\n        private DataContext context;\n                \n        \/\/ <b class=\"fm-bold\">...<i class=\"fm-italics\">methods omitted for brevity<\/i>...<\/b>\n                \n        [HttpPost]\n        [Consumes(\"application\/json\")]\n        public string SaveProductJson(ProductBindingTarget product) {\n            return $\"JSON: {product.Name}\";\n        }\n                \n        <b class=\"fm-bold\">\/\/[HttpPost]<\/b>\n        <b class=\"fm-bold\">\/\/[Consumes(\"application\/xml\")]<\/b>\n        <b class=\"fm-bold\">\/\/public string SaveProductXml(ProductBindingTarget product) {<\/b>\n        <b class=\"fm-bold\">\/\/    return $\"XML: {product.Name}\";<\/b>\n        <b class=\"fm-bold\">\/\/}<\/b>        \n    }\n}<\/pre>\n<p class=\"body\">Commenting out one of the action methods ensures that each remaining action has a unique combination of HTTP method and URL.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-383\">20.5.2 Installing and configuring the Swashbuckle package<\/h3>\n<p class=\"body\">The Swashbuckle package is the most popular ASP.NET Core implementation of the OpenAPI specification and will automatically generate a description for the web services in an ASP.NET Core application. The package also includes tools that consume that description to allow the web service to be inspected and tested.<\/p>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the commands shown in listing 20.28 to install the NuGet package.<a id=\"calibre_link-2133\"><\/a><a id=\"calibre_link-1262\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.28 Adding a package to the project<\/p>\n<pre class=\"programlisting\">dotnet add package Swashbuckle.AspNetCore --version 6.4.0<\/pre>\n<p class=\"body\">Add the statements shown in listing 20.29 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file to add the services and middleware provided by the Swashbuckle package.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.29 Configuring Swashbuckle in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.RateLimiting;\n<b class=\"fm-bold\">using Microsoft.OpenApi.Models;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddControllers()\n    .AddNewtonsoftJson().AddXmlDataContractSerializerFormatters();\n        \nbuilder.Services.AddRateLimiter(opts =&gt; {\n    opts.AddFixedWindowLimiter(\"fixedWindow\", fixOpts =&gt; {\n        fixOpts.PermitLimit = 1;\n        fixOpts.QueueLimit = 0;\n        fixOpts.Window = TimeSpan.FromSeconds(15);\n    });\n});\n\nbuilder.Services.Configure&lt;MvcNewtonsoftJsonOptions&gt;(opts =&gt; {\n    opts.SerializerSettings.NullValueHandling\n        = Newtonsoft.Json.NullValueHandling.Ignore;\n});\n\nbuilder.Services.Configure&lt;MvcOptions&gt;(opts =&gt; {\n    opts.RespectBrowserAcceptHeader = true;\n    opts.ReturnHttpNotAcceptable = true;\n});\n\nbuilder.Services.AddOutputCache(opts =&gt; {\n    opts.AddPolicy(\"30sec\", policy =&gt; {\n        policy.Cache();\n        policy.Expire(TimeSpan.FromSeconds(30));\n    });\n});\n\n<b class=\"fm-bold\">builder.Services.AddSwaggerGen(c =&gt; {<\/b>\n    <b class=\"fm-bold\">c.SwaggerDoc(\"v1\", new OpenApiInfo {<\/b> \n        <b class=\"fm-bold\">Title = \"WebApp\", Version = \"v1\"<\/b> \n    <b class=\"fm-bold\">});<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseRateLimiter();\napp.UseOutputCache();\napp.MapControllers();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\n<b class=\"fm-bold\">app.UseSwagger();<\/b>\n<b class=\"fm-bold\">app.UseSwaggerUI(options =&gt; {<\/b>\n    <b class=\"fm-bold\">options.SwaggerEndpoint(\"\/swagger\/v1\/swagger.json\", \"WebApp\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run(); <\/pre>\n<p class=\"body\">There are two features set up by the statements in listing 20.29. The feature generates an OpenAPI description of the web services that the application contains. You can see the description by restarting ASP.NET Core and using the browser to request the URL http:\/\/localhost:5000\/swagger\/v1\/swagger.json, which produces the response shown in figure 20.8. The OpenAPI format is verbose, but you can see each URL that the web service controllers support, along with details of the data each expects to receive and the range of responses that it will generate.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre185\" src=\"\/images\/proaspnetcore7\/000187.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.8 The OpenAPI description of the web service<\/p>\n<\/div>\n<p class=\"body\">The second feature is a UI that consumes the OpenAPI description of the web service and presents the information in a more easily understood way, along with support for testing each action. Use the browser to request http:\/\/localhost:5000\/swagger, and you will see the interface shown in figure 20.9. You can expand each action to see details, including the data that is expected in the request and the different responses that the client can expect.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre186\" src=\"\/images\/proaspnetcore7\/000188.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.9 The OpenAPI explorer interface<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-384\">20.5.3 Fine-Tuning the API description<\/h3>\n<p class=\"body\">Relying on the API discovery process can produce a result that doesn\u2019t truly capture the web service. You can see this by examining the entry in the Products section that describes GET requests matched by the <code class=\"fm-code-in-text\">\/api\/Products\/{id}<\/code> URL pattern. Expand this item and examine the response section, and you will see there is only one status code response that will be returned, as shown in figure 20.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre187\" src=\"\/images\/proaspnetcore7\/000189.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.10 The data formats listed in the OpenAPI web service description<\/p>\n<\/div>\n<p class=\"body\">The API discovery process makes assumptions about the responses produced by an action method and doesn\u2019t always reflect what can really happen. In this case, the <code class=\"fm-code-in-text\">GetProduct<\/code> action method in the <code class=\"fm-code-in-text\">ProductsController<\/code> class can return another response that the discovery process hasn\u2019t detected.<\/p>\n<pre class=\"programlisting\">...\n[HttpGet(\"{id}\")]\n[DisableRateLimiting]\npublic async Task&lt;IActionResult&gt; GetProduct(long id) {\n    Product? p = await context.Products.FindAsync(id);\n    if (p == null) {\n        <b class=\"fm-bold\">return NotFound();<\/b>\n    }\n    return Ok(p);\n}\n...<\/pre>\n<p class=\"body\">If a third-party developer attempts to implement a client for the web service using the OpenAPI data, they won\u2019t be expecting the 404 - Not Found response that the action sends when it can\u2019t find an object in the database.<a id=\"calibre_link-2134\"><\/a><a id=\"calibre_link-1261\"><\/a><\/p>\n<p class=\"fm-head2\">Running the API analyzer<\/p>\n<p class=\"body\">ASP.NET Core includes an analyzer that inspects web service controllers and highlights problems like the one described in the previous section. To enable the analyzer, add the elements shown in listing 20.30 to the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file. (If you are using Visual Studio, right-click the WebApp project item in the Solution Explorer and select Edit Project File from the pop-up menu.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.30 Enabling the analyzer in the WebApp.csproj file in the WebApp folder<\/p>\n<pre class=\"programlisting\">&lt;Project Sdk=\"Microsoft.NET.Sdk.Web\"&gt;\n\n  &lt;PropertyGroup&gt;\n    &lt;TargetFramework&gt;net7.0&lt;\/TargetFramework&gt;\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\n  &lt;\/PropertyGroup&gt;\n  \n  &lt;ItemGroup&gt;\n    &lt;PackageReference Include=\"Microsoft.AspNetCore.Mvc.NewtonsoftJson\" \n                                          Version=\"7.0.0\" \/&gt;\n    &lt;PackageReference Include=\"Microsoft.EntityFrameworkCore.Design\" \n                                          Version=\"7.0.0\"&gt;\n      &lt;IncludeAssets&gt;\n                  runtime; build; native; contentfiles; analyzers; \n                  buildtransitive\n          &lt;\/IncludeAssets&gt;\n      &lt;PrivateAssets&gt;all&lt;\/PrivateAssets&gt;\n    &lt;\/PackageReference&gt;\n    &lt;PackageReference Include=\"Microsoft.EntityFrameworkCore.SqlServer\" \n                                          Version=\"7.0.0\" \/&gt;\n    &lt;PackageReference Include=\"Swashbuckle.AspNetCore\" Version=\"6.4.0\" \/&gt;\n  &lt;\/ItemGroup&gt;\n  \n  <b class=\"fm-bold\">&lt;PropertyGroup&gt;<\/b>\n    <b class=\"fm-bold\">&lt;IncludeOpenAPIAnalyzers&gt;true&lt;\/IncludeOpenAPIAnalyzers&gt;<\/b>\n  <b class=\"fm-bold\">&lt;\/PropertyGroup&gt;<\/b>\n  \n&lt;\/Project&gt;<\/pre>\n<p class=\"body\">If you are using Visual Studio, you will see any problems detected by the API analyzer shown in the controller class file, as shown in figure 20.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre188\" src=\"\/images\/proaspnetcore7\/000190.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.11 A problem detected by the API analyzer<\/p>\n<\/div>\n<p class=\"body\">If you are using Visual Studio Code, you will see warning messages when the project is compiled, either using the <code class=\"fm-code-in-text\">dotnet build<\/code> command or when it is executed using the <code class=\"fm-code-in-text\">dotnet run<\/code> command. When the project is compiled, you will see this message that describes the issue in the <code class=\"fm-code-in-text\">ProductController<\/code> class:<\/p>\n<pre class=\"programlisting\">Controllers\\ProductsController.cs(27,24): warning API1000:\n    Action method returns undeclared status code '404'. \n[C:\\WebApp\\WebApp.csproj]\n    1 Warning(s)\n    0 Error(s)<\/pre>\n<p class=\"fm-head2\">Declaring the action method result type<\/p>\n<p class=\"body\">To fix the problem detected by the analyzer, the <code class=\"fm-code-in-text\">ProducesResponseType<\/code> attribute can be used to declare each of the response types that the action method can produce, as shown in listing 20.31.<a id=\"calibre_link-2135\"><\/a><a id=\"calibre_link-1263\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 20.31 Declaring results in the ProductsController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">...\n[HttpGet(\"{id}\")]\n[DisableRateLimiting]\n<b class=\"fm-bold\">[ProducesResponseType(StatusCodes.Status200OK)]<\/b>\n<b class=\"fm-bold\">[ProducesResponseType(StatusCodes.Status404NotFound)]<\/b>\npublic async Task&lt;IActionResult&gt; GetProduct(long id) {\n    Product? p = await context.Products.FindAsync(id);\n    if (p == null) {\n        return NotFound();\n    }\n    return Ok(p);\n}\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/swagger, and you will see the description for the action method has been updated to reflect the 404 response, as shown in figure 20.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre189\" src=\"\/images\/proaspnetcore7\/000191.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 20.12 Reflecting all the status codes produced by an action method<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-385\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The Entity Framework Core-related data feature can cause serialization problems when used in web service results.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Cycles in object data must be broken before serialization.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The PATCH method is used to specify fine-grained changes to data and is supported by ASP.NET Core.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core uses JSON to serialize data by default but supports a content negotiation process that allows clients to specify the formats they can accept.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The output from web services can be cached, using the caching middleware that can be applied to any endpoint.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Documentation for a web service can be generated using the OpenAPI specification.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-386\">\n<div class=\"calibre1\" id=\"calibre_link-2136\">\n<h1 class=\"tochead\" id=\"calibre_link-2137\"><a id=\"calibre_link-2138\"><\/a><a id=\"calibre_link-2139\"><\/a>21 Using controllers with views, part I<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2140\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using controllers with views to programmatically generate HTML content<\/li>\n<li class=\"co-summary-bullet\">Using the Razor view syntax to mix code and markup<\/li>\n<li class=\"co-summary-bullet\">Selecting a view in an action method<\/li>\n<li class=\"co-summary-bullet\">Using view models to pass data from an action to a view<\/li>\n<\/ul>\n<p class=\"body\"><a id=\"calibre_link-1080\"><\/a>In this chapter, I introduce the <i class=\"fm-italics\">Razor view engine<\/i>, which is responsible for generating HTML responses that can be displayed directly to the user (as opposed to the JSON and XML responses, which are typically consumed by other applications). <i class=\"fm-italics\">Views<\/i> are files that contain C# expressions and HTML fragments that are processed by the view engine to generate HTML responses. I show how views work, explain how they are used in action methods, and describe the different types of C# expressions they contain. In chapter 22, I describe some of the other features that views support. Table 21.1 puts Razor views in context.<a id=\"calibre_link-2141\"><\/a><a id=\"calibre_link-2142\"><\/a><a id=\"calibre_link-2143\"><\/a><a id=\"calibre_link-2144\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 21.1 Putting Razor Views in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2145\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Views are files that contain a mix of static HTML content and C# expressions.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Views are used to create HTML responses for HTTP requests. The C# expressions are evaluated and combined with the HTML content to create a response.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">View<\/code> method defined by the <code class=\"fm-code-in-text1\">Controller<\/code> class creates an action response that uses a view.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">It can take a little time to get used to the syntax of view files and the way they combine code and content.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">There are third-party view engines that can be used in ASP.NET Core MVC, but their use is limited.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 21.2 provides a guide to the chapter<\/p>\n<p class=\"fm-table-caption\">Table 21.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2146\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Enabling views<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">AddControllersWithViews<\/code> and <code class=\"fm-code-in-text1\">MapControllerRoute<\/code> methods to set up the required services and endpoints.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">4, 6<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Returning an HTML response from a controller action method<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">View<\/code> method to create a <code class=\"fm-code-in-text1\">ViewResult<\/code>.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">5<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating dynamic HTML content<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create a Razor View that uses expressions for dynamic content.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">7, 8, 19, 20<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Selecting a view by name<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Provide the view name as an argument to the <code class=\"fm-code-in-text1\">View<\/code> method.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">9, 10<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a view that can be used by multiple controllers<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create a shared view.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">11&ndash;13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Specifying a model type for a view<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use an <code class=\"fm-code-in-text1\">@model<\/code> expression.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14&ndash;18<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Allow for null view model values<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a nullable type in the <code class=\"fm-code-in-text1\">@model<\/code> expression<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">19&ndash;21<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Generating content selectively<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use <code class=\"fm-code-in-text1\">@if<\/code>, <code class=\"fm-code-in-text1\">@switch<\/code>, or <code class=\"fm-code-in-text1\">@foreach<\/code> expressions.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">22&ndash;28<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Including C# code in a view<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a code block.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">29<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-387\">21.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the <code class=\"fm-code-in-text\">WebApp<\/code> project from chapter 20. To prepare for this chapter, replace the contents of the <code class=\"fm-code-in-text\">Program.cs<\/code> file with the statements shown in listing 21.1, which removes some of the services and middleware used in earlier chapters.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.1 Replacing the contents of the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapControllers();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-388\">21.1.1 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 21.2 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.2 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-389\">21.1.2 Running the example application<\/h3>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 21.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.3 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet watch<\/pre>\n<p class=\"body\">The database will be seeded as part of the application startup. Once ASP.NET Core is running, use a web browser to request http:\/\/localhost:5000\/api\/products, which will produce the response shown in figure 21.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre190\" src=\"\/images\/proaspnetcore7\/000192.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.1 Running the example application<\/p>\n<\/div>\n<p class=\"body\">This chapter uses <code class=\"fm-code-in-text\">dotnet watch<\/code>, rather than the <code class=\"fm-code-in-text\">dotnet run<\/code> command used in earlier chapters. The <code class=\"fm-code-in-text\">dotnet watch<\/code> command is useful when working with views because changes are pushed to the browser automatically. At some point, however, you will make a change that cannot be processed by the <code class=\"fm-code-in-text\">dotnet watch<\/code> command, and you will see a message like this at the command prompt:<\/p>\n<pre class=\"programlisting\">watch : Unable to apply hot reload because of a rude edit.\nwatch : Do you want to restart your app - Yes (y) \/ No (n) \/\n     Always (a) \/ Never (v)?<\/pre>\n<p class=\"body\">The point at which this arises depends on the editor you have chosen, but when this happens, select the <code class=\"fm-code-in-text\">Always<\/code> option so that the application will always be restarted when a reload cannot be performed.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-390\">21.2 Getting started with views<\/h2>\n<p class=\"body\">I started this chapter with a web service controller to demonstrate the similarity with a controller that uses views. It is easy to think about web service and view controllers as being separate, but it is important to understand that the same underlying features are used for both types of response. In the sections that follow, I configure the application to support HTML applications and repurpose the <code class=\"fm-code-in-text\">Home<\/code> controller so that it produces an HTML response.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-391\">21.2.1 Configuring the application<\/h3>\n<p class=\"body\">The first step is to configure ASP.NET Core to enable HTML responses, as shown in listing 21.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.4 Changing the configuration in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\n<b class=\"fm-bold\">builder.Services.AddControllersWithViews();<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapControllers();\n<b class=\"fm-bold\">app.MapControllerRoute(\"Default\",<\/b>\n    <b class=\"fm-bold\">\"{controller=Home}\/{action=Index}\/{id?}\");<\/b>\n        \nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">HTML responses are created using views, which are files containing a mix of HTML elements and C# expressions. The <code class=\"fm-code-in-text\">AddControllers<\/code> method I used in chapter 19 to enable the MVC Framework only supports web service controllers. To enable support for views, the <code class=\"fm-code-in-text\">AddControllersWithViews<\/code> method is used.<\/p>\n<p class=\"body\">The second change is the addition of the <code class=\"fm-code-in-text\">MapControllerRoute<\/code> method in the endpoint routing configuration. Controllers that generate HTML responses don\u2019t use the same routing attributes that are applied to web service controllers and rely on a feature named <i class=\"fm-italics\">convention routing,<\/i> which I describe in the next section.<a id=\"calibre_link-2147\"><\/a><a id=\"calibre_link-2148\"><\/a><a id=\"calibre_link-2149\"><\/a><a id=\"calibre_link-2150\"><\/a><a id=\"calibre_link-1121\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-392\">21.2.2 Creating an HTML controller<\/h3>\n<p class=\"body\">Controllers for HTML applications are similar to those used for web services but with some important differences. To create an HTML controller, add a class file named <code class=\"fm-code-in-text\">HomeController.cs<\/code> to the <code class=\"fm-code-in-text\">Controllers<\/code> folder with the statements shown in listing 21.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.5 The contents of the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        private DataContext context;\n                \n        public HomeController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            return View(await context.Products.FindAsync(id));\n        }\n    }\n}<\/pre>\n<p class=\"body\">The base class for HTML controllers is <code class=\"fm-code-in-text\">Controller<\/code>, which is derived from the <code class=\"fm-code-in-text\">ControllerBase<\/code> class used for web service controllers and provides additional methods that are specific to working with views.<\/p>\n<pre class=\"programlisting\">...\npublic class HomeController: <b class=\"fm-bold\">Controller<\/b> {\n...<\/pre>\n<p class=\"body\">Action methods in HTML controllers return objects that implement the <code class=\"fm-code-in-text\">IActionResult<\/code> interface, which is the same result type used in chapter 19 to return specific status code responses. The <code class=\"fm-code-in-text\">Controller<\/code> base class provides the <code class=\"fm-code-in-text\">View<\/code> method, which is used to select a view that will be used to create a response.<\/p>\n<pre class=\"programlisting\">...\nreturn <b class=\"fm-bold\">View<\/b>(await context.Products.FindAsync(id));\n...<\/pre>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Notice that the controller in listing 21.5 hasn\u2019t been decorated with attributes. The <code class=\"fm-code-in-text1\">ApiController<\/code> attribute is applied only to web service controllers and should not be used for HTML controllers. The <code class=\"fm-code-in-text1\">Route<\/code> and HTTP method attributes are not required because HTML controllers rely on convention-based routing, which was configured in listing 21.4 and which is introduced shortly.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">View<\/code> method creates an instance of the <code class=\"fm-code-in-text\">ViewResult<\/code> class, which implements the <code class=\"fm-code-in-text\">IActionResult<\/code> interface and tells the MVC Framework that a view should be used to produce the response for the client. The argument to the <code class=\"fm-code-in-text\">View<\/code> method is called the <i class=\"fm-italics\">view model<\/i> and provides the view with the data it needs to generate a response.<\/p>\n<p class=\"body\">There are no views for the MVC Framework to use at the moment, but if you restart ASP.NET Core and use a browser to request http:\/\/localhost:5000, you will see an error message that shows how the MVC Framework responds to the <code class=\"fm-code-in-text\">ViewResult<\/code> it received from the <code class=\"fm-code-in-text\">Index<\/code> action method, as shown in figure 21.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre191\" src=\"\/images\/proaspnetcore7\/000193.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.2 Using a view result<\/p>\n<\/div>\n<p class=\"body\">Behind the scenes, there are two important conventions at work, which are described in the following sections.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Two features can expand the range of search locations. The search will include the <code class=\"fm-code-in-text1\">\/Pages\/Shared<\/code> folder if the project uses Razor Pages, as explained in chapter 23.<\/p>\n<p class=\"fm-head2\">Understanding convention routing<\/p>\n<p class=\"body\">HTML controllers rely on <i class=\"fm-italics\">convention routing<\/i> instead of the <code class=\"fm-code-in-text\">Route<\/code> attribute. The convention in this term refers to the use of the controller class name and the action method name used to configure the routing system, which was done in listing 21.5 by adding this statement to the endpoint routing configuration:<\/p>\n<pre class=\"programlisting\">...\napp.MapControllerRoute(\"Default\",\n    \"{controller=Home}\/{action=Index}\/{id?}\");\n...<\/pre>\n<p class=\"body\">The route that this statement sets up matches two- and three-segment URLs. The value of the first segment is used as the name of the controller class, without the <code class=\"fm-code-in-text\">Controller<\/code> suffix, so that <code class=\"fm-code-in-text\">Home<\/code> refers to the <code class=\"fm-code-in-text\">HomeController<\/code> class. The second segment is the name of the action method, and the optional third segment allows action methods to receive a parameter named <code class=\"fm-code-in-text\">id<\/code>. Default values are used to select the <code class=\"fm-code-in-text\">Index<\/code> action method on the <code class=\"fm-code-in-text\">Home<\/code> controller for URLs that do not contain all the segments. This is such a common convention that the same routing configuration can be set up without having to specify the URL pattern, as shown in listing 21.6.<a id=\"calibre_link-2151\"><\/a><a id=\"calibre_link-1130\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.6 The default routing convention in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddControllersWithViews();\n\nvar app = builder.Build();\n\napp.UseStaticFiles(); \napp.MapControllers();\n<b class=\"fm-bold\">app.MapDefaultControllerRoute();<\/b>\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">MapDefaultControllerRoute<\/code> method avoids the risk of mistyping the URL pattern and sets up the convention-based routing. I have configured one route in this chapter, but an application can define as many routes as it needs, and later chapters expand the routing configuration to make examples easier to follow.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The MVC Framework assumes that any <code class=\"fm-code-in-text1\">public<\/code> method defined by an HTML controller is an action method and that action methods support all HTTP methods. If you need to define a method in a controller that is not an action, you can make it <code class=\"fm-code-in-text1\">private<\/code> or, if that is not possible, decorate the method with the <code class=\"fm-code-in-text1\">NonAction<\/code> attribute. You can restrict an action method to support specific HTTP methods by applying attributes so that the <code class=\"fm-code-in-text1\">HttpGet<\/code> attribute denotes an action that handles GET requests, the <code class=\"fm-code-in-text1\">HttpPost<\/code> method denotes an action that handles POST requests, and so on.<\/p>\n<p class=\"fm-head2\">Understanding the Razor View convention<\/p>\n<p class=\"body\">When the <code class=\"fm-code-in-text\">Index<\/code> action method defined by the <code class=\"fm-code-in-text\">Home<\/code> controller is invoked, it uses the value of the <code class=\"fm-code-in-text\">id<\/code> parameter to retrieve an object from the database and passes it to the <code class=\"fm-code-in-text\">View<\/code> method.<\/p>\n<pre class=\"programlisting\">...\npublic async Task&lt;IActionResult&gt; Index(long id = 1) {\n    return <b class=\"fm-bold\">View<\/b>(await context.Products.FindAsync(id));\n}\n...<\/pre>\n<p class=\"body\">When an action method invokes the <code class=\"fm-code-in-text\">View<\/code> method, it creates a <code class=\"fm-code-in-text\">ViewResult<\/code> that tells the MVC Framework to use the default convention to locate a view. The Razor view engine looks for a view with the same name as the action method, with the addition of the <code class=\"fm-code-in-text\">cshtml<\/code> file extension, which is the file type used by the Razor view engine. Views are stored in the <code class=\"fm-code-in-text\">Views<\/code> folder, grouped by the controller they are associated with. The first location searched is the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder, since the action method is defined by the <code class=\"fm-code-in-text\">Home<\/code> controller (the name of which is taken by dropping <code class=\"fm-code-in-text\">Controller<\/code> from the name of the controller class). If the <code class=\"fm-code-in-text\">Index.cshtml<\/code> file cannot be found in the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder, then the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder is checked, which is the location where views that are shared between controllers are stored.<\/p>\n<p class=\"body\">While most controllers have their own views, views can also be shared so that common functionality doesn\u2019t have to be duplicated, as demonstrated in the \u201cUsing Shared Views\u201d section.<\/p>\n<p class=\"body\">The exception response in figure 21.2 shows the result of both conventions. The routing conventions are used to process the request using the <code class=\"fm-code-in-text\">Index<\/code> action method defined by the <code class=\"fm-code-in-text\">Home<\/code> controller, which tells the Razor view engine to use the view search convention to locate a view. The view engine uses the name of the action method and controller to build its search pattern and checks for the <code class=\"fm-code-in-text\">Views\/Home\/Index.cshtml<\/code> and <code class=\"fm-code-in-text\">Views\/Shared\/Index.cshtml<\/code> files.<a id=\"calibre_link-2152\"><\/a><a id=\"calibre_link-2153\"><\/a><a id=\"calibre_link-1122\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-393\">21.2.3 Creating a Razor View<\/h3>\n<p class=\"body\">To provide the MVC Framework with a view to display, create the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder and add to it a file named <code class=\"fm-code-in-text\">Index.cshtml<\/code> with the content shown in listing 21.7. If you are using Visual Studio, create the view by right-clicking the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder, selecting Add &gt; New Item from the pop-up menu, and selecting the Razor View - Empty item in the ASP.NET Core &gt; Web category, as shown in figure 21.3.<a id=\"calibre_link-2154\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre192\" src=\"\/images\/proaspnetcore7\/000194.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.3 Creating a view using Visual Studio<\/p>\n<\/div>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> There is a menu item for creating views in the Add popup menu, but this relies on the Visual Studio scaffolding feature, which adds template content to create different types of view. I don\u2019t rely on the scaffolding in this book and instead show you how to create views from scratch.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.7 The contents of the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n        Product Table\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The view file contains standard HTML elements that are styled using the Bootstrap CSS framework, which is applied through the <code class=\"fm-code-in-text\">class<\/code> attribute. The key view feature is the ability to generate content using C# expressions, like this:<a id=\"calibre_link-2155\"><\/a><a id=\"calibre_link-2156\"><\/a><a id=\"calibre_link-1213\"><\/a><\/p>\n<pre class=\"programlisting\">...\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;<b class=\"fm-bold\">@Model.Name<\/b>&lt;\/td&gt;&lt;\/tr&gt;\n&lt;tr&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;<b class=\"fm-bold\">@Model.Price.ToString(\"c\")<\/b>&lt;\/td&gt;&lt;\/tr&gt;\n...<\/pre>\n<p class=\"body\">I explain how these expressions work in the \u201cUnderstanding the Razor Syntax\u201d section, but for now, it is enough to know that these expressions insert the value of the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Price<\/code> properties from the <code class=\"fm-code-in-text\">Product<\/code> view model passed to the <code class=\"fm-code-in-text\">View<\/code> method by the action method in listing 21.5. Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000, and you will see the HTML response shown in figure 21.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre193\" src=\"\/images\/proaspnetcore7\/000195.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.4 A view response<\/p>\n<\/div>\n<p class=\"fm-head2\">Modifying a Razor View<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">dotnet watch<\/code> command detects and recompiles Razor Views automatically, meaning that the ASP.NET Core runtime doesn\u2019t have to be restarted when views are edited. To demonstrate the recompilation process, listing 21.8 adds new elements to the <code class=\"fm-code-in-text\">Index<\/code> view.<a id=\"calibre_link-2157\"><\/a><a id=\"calibre_link-1235\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.8 Adding Elements in the Index.cshtml File in the Views\/Home Folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n        Product Table\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model.CategoryId&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Save the changes to the view; the change will be detected, and the browser will be automatically reloaded to display the change, as shown in figure 21.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre194\" src=\"\/images\/proaspnetcore7\/000196.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.5 Modifying a Razor View<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-394\">21.2.4 Selecting a View by name<\/h3>\n<p class=\"body\">The action method in listing 21.5 relies entirely on convention, leaving Razor to select the view that is used to generate the response. Action methods can select a view by providing a name as an argument to the <code class=\"fm-code-in-text\">View<\/code> method, as shown in listing 21.9.<a id=\"calibre_link-2158\"><\/a><a id=\"calibre_link-1240\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.9 Selecting a view in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        private DataContext context;\n                \n        public HomeController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            <b class=\"fm-bold\">Product? prod = await context.Products.FindAsync(id);<\/b>\n            <b class=\"fm-bold\">if (prod?.CategoryId == 1) {<\/b>\n                <b class=\"fm-bold\">return View(\"Watersports\", prod);<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return View(prod);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The action method selects the view based on the <code class=\"fm-code-in-text\">CategoryId<\/code> property of the <code class=\"fm-code-in-text\">Product<\/code> object that is retrieved from the database. If the <code class=\"fm-code-in-text\">CategoryId<\/code> is 1, the action method invokes the <code class=\"fm-code-in-text\">View<\/code> method with an additional argument that selects a view named <code class=\"fm-code-in-text\">Watersports<\/code>.<\/p>\n<pre class=\"programlisting\">...\nreturn View(<b class=\"fm-bold\">\"Watersports\"<\/b>, prod);\n...<\/pre>\n<p class=\"body\">Notice that the action method doesn\u2019t specify the file extension or the location for the view. It is the job of the view engine to translate <code class=\"fm-code-in-text\">Watersports<\/code> into a view file.<\/p>\n<p class=\"body\">If you save the <code class=\"fm-code-in-text\">HomeController.cs<\/code> file, <code class=\"fm-code-in-text\">dotnet watch<\/code> will detect the change and reload the browser, which will cause an error because the view file doesn\u2019t exist. To create the view, add a Razor View file named <code class=\"fm-code-in-text\">Watersports.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder with the content shown in listing 21.10.<a id=\"calibre_link-2159\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.10 The contents of the Watersports.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Watersports\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The new view follows the same pattern as the <code class=\"fm-code-in-text\">Index<\/code> view but has a different title above the table. Save the change and request http:\/\/localhost:5000\/home\/index\/1 and http:\/\/localhost:5000\/home\/index\/4. The action method selects the <code class=\"fm-code-in-text\">Watersports<\/code> view for the first URL and the default view for the second URL, producing the two responses shown in figure 21.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre195\" src=\"\/images\/proaspnetcore7\/000197.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.6 Selecting views<\/p>\n<\/div>\n<p class=\"fm-head2\">Using shared views<\/p>\n<p class=\"body\">When the Razor view engine locates a view, it looks in the <code class=\"fm-code-in-text\">Views\/[controller]<\/code> folder and then the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder. This search pattern means that views that contain common content can be shared between controllers, avoiding duplication. To see how this process works, add a Razor View file named <code class=\"fm-code-in-text\">Common.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder with the content shown in listing 21.11.<a id=\"calibre_link-2160\"><\/a><a id=\"calibre_link-1241\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.11 The contents of the Common.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Shared View\n    &lt;\/h6&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Next, add an action method to the <code class=\"fm-code-in-text\">Home<\/code> controller that uses the new view, as shown in listing 21.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.12 Adding an action in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        private DataContext context;\n                \n        public HomeController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            Product? prod = await context.Products.FindAsync(id);\n            if (prod?.CategoryId == 1) {\n                return View(\"Watersports\", prod);\n            } else {\n                return View(prod);\n            }\n        }\n                \n        <b class=\"fm-bold\">public IActionResult Common() {<\/b>\n            <b class=\"fm-bold\">return View();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The new action relies on the convention of using the method name as the name of the view. When a view doesn\u2019t require any data to display to the user, the <code class=\"fm-code-in-text\">View<\/code> method can be called without arguments. Next, create a new controller by adding a class file named <code class=\"fm-code-in-text\">SecondController.cs<\/code> to the <code class=\"fm-code-in-text\">Controllers<\/code> folder, with the code shown in listing 21.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.13 The contents of the SecondController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Controllers {\n\n    public class SecondController : Controller {\n        \n        public IActionResult Index() {\n            return View(\"Common\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The new controller defines a single action, named <code class=\"fm-code-in-text\">Index<\/code>, which invokes the <code class=\"fm-code-in-text\">View<\/code> method to select the <code class=\"fm-code-in-text\">Common<\/code> view. Wait for the application to be built and navigate to http:\/\/localhost:5000\/home\/common and http:\/\/localhost:5000\/second, both of which will render the <code class=\"fm-code-in-text\">Common<\/code> view, producing the responses shown in figure 21.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre196\" src=\"\/images\/proaspnetcore7\/000198.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.7 Using a shared view<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Specifying a view location<\/p>\n<p class=\"fm-sidebar-text\">The Razor view engine will look for a controller-specific view before a shared view. You can change this behavior by specifying the complete path to a view file, which can be useful if you want to select a shared view that would otherwise be ignored because there is a controller-specific view with the same name.<\/p>\n<pre class=\"programlisting\">...\npublic IActionResult Index() {\n    return View(<b class=\"fm-bold\">\"\/Views\/Shared\/Common.cshtml\"<\/b>);\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">When specifying the view, the path relative to the project folder must be specified, starting with the <code class=\"fm-code-in-text1\">\/<\/code> character. Notice that the full name of the file, including the file extension, is used.<\/p>\n<p class=\"fm-sidebar-text\">This is a technique that should be used sparingly because it creates a dependency on a specific file, rather than allowing the view engine to select the file.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-395\">21.3 Working with Razor Views<\/h2>\n<p class=\"body\">Razor Views contain HTML elements and C# expressions. Expressions are mixed in with the HTML elements and denoted with the <code class=\"fm-code-in-text\">@<\/code> character, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;<b class=\"fm-bold\">@Model.Name<\/b>&lt;\/td&gt;&lt;\/tr&gt;\n...<\/pre>\n<p class=\"body\">When the view is used to generate a response, the expressions are evaluated, and the results are included in the content sent to the client. This expression gets the name of the <code class=\"fm-code-in-text\">Product<\/code> view model object provided by the action method and produces output like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;<b class=\"fm-bold\">Corner Flags<\/b>&lt;\/td&gt;&lt;\/tr&gt;\n...<\/pre>\n<p class=\"body\">This transformation can seem like magic, but Razor is simpler than it first appears. Razor Views are converted into C# classes that inherit from the <code class=\"fm-code-in-text\">RazorPage<\/code> class, which are then compiled like any other C# class.<a id=\"calibre_link-2161\"><\/a><a id=\"calibre_link-1223\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Seeing the compiled output from Razor Views<\/p>\n<p class=\"fm-sidebar-text\">By default, Razor Views are compiled directly into a DLL, and the generated C# classes are not written to the disk during the build process. You can see the generated classes, by adding the following setting to the <code class=\"fm-code-in-text1\">WebApp.csproj<\/code> file, which is accessed in Visual Studio by right-clicking the WebApp item in the Solution Explorer and selecting Edit Project File from the pop-up menu:<\/p>\n<pre class=\"programlisting\">...\n&lt;PropertyGroup&gt;\n    &lt;EmitCompilerGeneratedFiles&gt;true&lt;\/EmitCompilerGeneratedFiles&gt;\n&lt;\/PropertyGroup&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">Save the project file and build the project using the <code class=\"fm-code-in-text1\">dotnet build<\/code> command. The C# files generated from the Razor Views will be written to the <code class=\"fm-code-in-text1\">obj\/Debug\/net7.0\/generated<\/code> folder. You may have to dig around in the subfolders to find specific files.<\/p>\n<\/div>\n<p class=\"body\">The view from listing 21.10, for example, would be transformed into a class like this:<\/p>\n<pre class=\"programlisting\">namespace AspNetCoreGeneratedDocument {\n\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Threading.Tasks;\n    using Microsoft.AspNetCore.Mvc;\n    using Microsoft.AspNetCore.Mvc.Rendering;\n    using Microsoft.AspNetCore.Mvc.ViewFeatures;\n        \n    internal sealed class Views_Home_Watersports : RazorPage&lt;dynamic&gt; {\n        \n        public async override Task ExecuteAsync() {\n            WriteLiteral(\"&lt;!DOCTYPE html&gt;\\r\\n&lt;html&gt;\\r\\n\");\n            WriteLiteral(\"&lt;link href=\\\"\");\n            WriteLiteral(\"\/lib\/bootstrap\/css\/bootstrap.min.css\\\"\");\n            WriteLiteral(\"rel=\\\"stylesheet\\\" \/&gt;\\r\\n\");\n            HeadTagHelper = CreateTagHelper&lt;TagHelpers.HeadTagHelper&gt;();\n            __tagHelperExecutionContext.Add(HeadTagHelper);\n            Write(__tagHelperExecutionContext.Output);\n            WriteLiteral(\"\\r\\n\");\n            __tagHelperExecutionContext =\n                    __tagHelperScopeManager.Begin(\"body\", \n                TagMode.StartTagAndEndTag, \"76ad69...\", async() =&gt; {\n                WriteLiteral(\"&lt;h6 class=\\\"bg-secondary text-white \");\n                WriteLiteral(\"text-center m-2 p-2\\\"&gt;Watersports&lt;\/h6&gt;\\n\");\n                WriteLiteral(\"&lt;div class=\\\"m-2\\\"&gt;&lt;table class=\\\"table \");\n                WriteLiteral(\"table-sm table-striped table-bordered\\\"&gt;\");           \n                WriteLiteral(\"&lt;tbody&gt;\\r\\n &lt;tr&gt;\");\n                WriteLiteral(\"&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;\");\n                Write(Model.Name);\n                WriteLiteral(\"&lt;\/td&gt;&lt;\/tr&gt;\\r\\n&lt;tr&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;\");\n                Write(Model.Price.ToString(\"c\"));\n                WriteLiteral(\"&lt;\/td&gt;&lt;\/tr&gt;\\r\\n&lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;\");\n                Write(Model.CategoryId);\n                WriteLiteral(\"&lt;\/td&gt;&lt;\/tr&gt;\\r\\n&lt;\/tbody&gt;\\r\\n&lt;\/table&gt;\\r\\n\");\n                WriteLiteral(\"&lt;\/div&gt;\\r\\n\");\n            });\n            BodyTagHelper = CreateTagHelper&lt;TagHelpers.BodyTagHelper&gt;();\n            __tagHelperExecutionContext.Add(BodyTagHelper);\n            Write(__tagHelperExecutionContext.Output);\n            WriteLiteral(\"\\r\\n&lt;\/html&gt;\\r\\n\");\n        }\n                \n        public IModelExpressionProvider ModelExpressionProvider \n           { get; private set; }\n        public IUrlHelper Url { get; private set; }\n        public IViewComponentHelper Component { get; private set; }\n        public IJsonHelper Json { get; private set; }\n        public IHtmlHelper&lt;dynamic&gt; Html { get; private set; }        \n    }\n}<\/pre>\n<p class=\"body\">This class is a simplification of the code that is generated so that I can focus on the features that are most important for this chapter. The first point to note is that the class generated from the view inherits from the <code class=\"fm-code-in-text\">RazorPage&lt;T&gt;<\/code> class.<a id=\"calibre_link-2162\"><\/a><a id=\"calibre_link-2163\"><\/a><a id=\"calibre_link-1238\"><\/a><\/p>\n<pre class=\"programlisting\">...\ninternal sealed class Views_Home_Watersports : <b class=\"fm-bold\">RazorPage&lt;dynamic&gt;<\/b> {\n...<\/pre>\n<p class=\"body\">Table 21.3 describes the most useful properties and methods defined by <code class=\"fm-code-in-text\">RazorPage&lt;T&gt;<\/code>.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Caching responses<\/p>\n<p class=\"fm-sidebar-text\">Responses from views can be cached by applying the <code class=\"fm-code-in-text1\">ResponseCache<\/code> attribute to action methods (or to the controller class, which caches the responses from all the action methods). See chapter 17 for details of how response caching is enabled.<\/p>\n<\/div>\n<p class=\"fm-table-caption\">Table 21.3 Useful RazorPage&lt;T&gt; members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2164\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Context<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the <code class=\"fm-code-in-text1\">HttpContext<\/code> object for the current request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExecuteAsync()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to generate the output from the view.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Layout<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to set the view layout, as described in chapter 22.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Model<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the view model passed to the <code class=\"fm-code-in-text1\">View<\/code> method by the action.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RenderBody()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used in layouts to include content from a view, as described in chapter 22.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RenderSection()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used in layouts to include content from a section in a view, as described in chapter 22.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">TempData<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to access the temp data feature, which is described in chapter 22.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ViewBag<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to access the view bag, which is described in chapter 22.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ViewContext<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">ViewContext<\/code> object that provides context data.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ViewData<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the view data, which I used for unit testing controllers in the SportsStore application.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Write(str)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method writes a string, which will be safely encoded for use in HTML.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">WriteLiteral(str)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method writes a string without encoding it for safe use in HTML.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The expressions in the view are translated into calls to the <code class=\"fm-code-in-text\">Write<\/code> method, which encodes the result of the expression so that it can be included safely in an HTML document. The <code class=\"fm-code-in-text\">WriteLiteral<\/code> method is used to deal with the static HTML regions of the view, which don\u2019t need further encoding. The result is a fragment like this from the <code class=\"fm-code-in-text\">CSHTML<\/code> file:<\/p>\n<pre class=\"programlisting\">...\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n...<\/pre>\n<p class=\"body\">This is converted into a series of C# statements like these in the <code class=\"fm-code-in-text\">ExecuteAsync<\/code> method:<\/p>\n<pre class=\"programlisting\">...\nWriteLiteral(\"&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;\");\nWrite(Model.Name);\nWriteLiteral(\"&lt;\/td&gt;&lt;\/tr&gt;\");\n...<\/pre>\n<p class=\"body\">When the <code class=\"fm-code-in-text\">ExecuteAsync<\/code> method is invoked, the response is generated with a mix of the static HTML and the expressions contained in the view. The results from evaluating the expressions are written to the response, producing HTML like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;Kayak&lt;\/td&gt;&lt;\/tr&gt;\n...<\/pre>\n<p class=\"body\">In addition to the properties and methods inherited from the <code class=\"fm-code-in-text\">RazorPage&lt;T&gt;<\/code> class, the generated view class defines the properties described in table 21.4, some of which are used for features described in later chapters.<a id=\"calibre_link-2165\"><\/a><a id=\"calibre_link-1239\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 21.4 The additional View properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2166\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Component<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a helper for working with view components, which is accessed through the <code class=\"fm-code-in-text1\">vc<\/code> tag helper described in chapter 25.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Html<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an implementation of the <code class=\"fm-code-in-text1\">IHtmlHelper<\/code> interface. This property is used to manage HTML encoding, as described in chapter 22.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Json<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an implementation of the <code class=\"fm-code-in-text1\">IJsonHelper<\/code> interface, which is used to encode data as JSON, as described in chapter 22.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ModelExpressionProvider<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property provides access to expressions that select properties from the model, which is used through tag helpers, described in chapters 25&ndash;27.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Url<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a helper for working with URLs, as described in chapter 26.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-396\">21.3.1 Setting the view model type<\/h3>\n<p class=\"body\">The generated class for the <code class=\"fm-code-in-text\">Watersports.cshtml<\/code> file is derived from <code class=\"fm-code-in-text\">RazorPage&lt;T&gt;<\/code>, but Razor doesn\u2019t know what type will be used by the action method for the view model, so it has selected <code class=\"fm-code-in-text\">dynamic<\/code> as the generic type argument. This means that the <code class=\"fm-code-in-text\">@Model<\/code> expression can be used with any property or method name, which is evaluated at runtime when a response is generated. To demonstrate what happens when a nonexistent member is used, add the content shown in listing 21.14 to the <code class=\"fm-code-in-text\">Watersports.cshtml<\/code> file.<a id=\"calibre_link-2167\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.14 Adding content in the Watersports.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Watersports\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n                <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Tax Rate&lt;\/th&gt;&lt;td&gt;@Model.TaxRate&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000, and you will see the exception shown in figure 21.8. You may need to start ASP.NET Core to see this error because the <code class=\"fm-code-in-text\">dotnet watch<\/code> command can be confused when it is unable to load the compiled view.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre197\" src=\"\/images\/proaspnetcore7\/000199.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.8 Using a nonexistent property in a view expression<\/p>\n<\/div>\n<p class=\"body\">To check expressions during development, the type of the <code class=\"fm-code-in-text\">Model<\/code> object can be specified using the <code class=\"fm-code-in-text\">model<\/code> keyword, as shown in listing 21.15.<a id=\"calibre_link-2168\"><\/a><a id=\"calibre_link-1234\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> It is easy to get the two terms confused. <code class=\"fm-code-in-text1\">Model<\/code>, with an uppercase <code class=\"fm-code-in-text1\">M<\/code>, is used in expressions to access the view model object provided by the action method, while <code class=\"fm-code-in-text1\">model<\/code>, with a lowercase <code class=\"fm-code-in-text1\">m<\/code>, is used to specify the type of the view model.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.15 Declaring the type in the Watersports.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@model WebApp.Models.Product<\/b>\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Watersports\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Tax Rate&lt;\/th&gt;&lt;td&gt;@Model.TaxRate&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">An error warning will appear in the editor after a few seconds, as Visual Studio or Visual Studio Code checks the view in the background, as shown in figure 21.9. When the <code class=\"fm-code-in-text\">dotnet watch<\/code> command is used, an error will be displayed in the browser, also shown in figure 21.9. The compiler will also report an error if you build the project or use the <code class=\"fm-code-in-text\">dotnet build<\/code> or <code class=\"fm-code-in-text\">dotnet run<\/code> command.<a id=\"calibre_link-2169\"><\/a><a id=\"calibre_link-1224\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre198\" src=\"\/images\/proaspnetcore7\/000200.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.9 An error warning in a view file<\/p>\n<\/div>\n<p class=\"body\">When the C# class for the view is generated, the view model type is used as the generic type argument for the base class, like this:<\/p>\n<pre class=\"programlisting\">...\ninternal sealed class Views_Home_Watersports :\nRazorPage&lt;<b class=\"fm-bold\">WebApp.Models.Product<\/b>&gt; {\n...<\/pre>\n<p class=\"body\">Specifying a view model type allows Visual Studio and Visual Studio Code to suggest property and method names as you edit views. Replace the nonexistent property with the one shown in listing 21.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.16 Using a property in the Watersports.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model WebApp.Models.Product\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Watersports\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n                <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Supplier ID&lt;\/th&gt;&lt;td&gt;@Model.SupplierId&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">As you type, the editor will prompt you with the possible member names defined by the view model class, as shown in figure 21.10. This figure shows the Visual Studio code editor, but Visual Studio Code has a comparable feature.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre199\" src=\"\/images\/proaspnetcore7\/000201.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.10 Editor suggestions when using a view model type<\/p>\n<\/div>\n<p class=\"fm-head2\">Using a View Imports file<\/p>\n<p class=\"body\"><a id=\"calibre_link-1246\"><\/a>When I declared the view model object at the start of the <code class=\"fm-code-in-text\">Watersports.cshtml<\/code> file, I had to include the namespace that contains the class, like this:<a id=\"calibre_link-2170\"><\/a><\/p>\n<pre class=\"programlisting\">...\n@model <b class=\"fm-bold\">WebApp.Models<\/b>.Product\n...<\/pre>\n<p class=\"body\">By default, all types that are referenced in a Razor View must be qualified with a namespace. This isn\u2019t a big deal when the only type reference is for the model object, but it can make a view more difficult to read when writing more complex Razor expressions such as the ones I describe later in this chapter.<\/p>\n<p class=\"body\">You can specify a set of namespaces that should be searched for types by adding a <i class=\"fm-italics\">view imports<\/i> file to the project. The view imports file is placed in the <code class=\"fm-code-in-text\">Views<\/code> folder and is named <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code>.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Files in the <code class=\"fm-code-in-text1\">Views<\/code> folder whose names begin with an underscore (the <code class=\"fm-code-in-text1\">_<\/code> character) are not returned to the user, which allows the file name to differentiate between views that you want to render and the files that support them. View imports files and layouts (which I describe shortly) are prefixed with an underscore.<\/p>\n<p class=\"body\">If you are using Visual Studio, right-click the <code class=\"fm-code-in-text\">Views<\/code> folder in the Solution Explorer, select Add &gt; New Item from the pop-up menu, and select the Razor View Imports template from the ASP.NET Core category, as shown in figure 21.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre200\" src=\"\/images\/proaspnetcore7\/000202.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.11 Creating a view imports file<\/p>\n<\/div>\n<p class=\"body\">Visual Studio will automatically set the name of the file to <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code>, and clicking the Add button will create the file, which will be empty. If you are using Visual Studio Code, simply select the <code class=\"fm-code-in-text\">Views<\/code> folder and add a new file called <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code>. Regardless of which editor you used, add the expression shown listing 21.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.17 The contents of the _ViewImports.cshtml file in the Views folder<\/p>\n<pre class=\"programlisting\">@using WebApp.Models<\/pre>\n<p class=\"body\">The namespaces that should be searched for classes used in Razor Views are specified using the <code class=\"fm-code-in-text\">@using<\/code> expression, followed by the namespace. In listing 21.17, I have added an entry for the <code class=\"fm-code-in-text\">WebApp.Models<\/code> namespace that contains the view model class used in the <code class=\"fm-code-in-text\">Watersports.cshtml<\/code> view.<a id=\"calibre_link-2171\"><\/a><a id=\"calibre_link-1216\"><\/a><\/p>\n<p class=\"body\">Now that the namespace is included in the view imports file, I can remove the namespace from the view, as shown in listing 21.18.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can also add an <code class=\"fm-code-in-text1\">@using<\/code> expression to individual view files, which allows types to be used without namespaces in a single view.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.18 Changing type in the Watersports.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@model Product<\/b>\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Watersports\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Supplier ID&lt;\/th&gt;&lt;td&gt;@Model.SupplierId&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Save the view file and use a browser to request http:\/\/localhost:5000, and you will see the response shown in figure 21.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre201\" src=\"\/images\/proaspnetcore7\/000203.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.12 Using a view imports file<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-397\">21.3.2 Understanding the view model type pitfall<\/h3>\n<p class=\"body\">There is a pitfall waiting for the unwary, which is that the object passed to the <code class=\"fm-code-in-text\">View<\/code> method to set the view model isn\u2019t type checked before it is used. Here is the definition of the <code class=\"fm-code-in-text\">View<\/code> method in the <code class=\"fm-code-in-text\">Controller<\/code> class:<\/p>\n<pre class=\"programlisting\">...\npublic virtual ViewResult View(<b class=\"fm-bold\">object?<\/b> model) {\n    return View(viewName: null, model: model);\n}\n...<\/pre>\n<p class=\"body\">When the <code class=\"fm-code-in-text\">@model<\/code> expression is used to set the view model type, it changes the base class for the view, and allows the expressions in the view to be checked by the compiler, but it doesn\u2019t prevent a controller from using an object with a completely different as the view model. As a simple demonstration, listing 21.19 defines an action method that uses the <code class=\"fm-code-in-text\">Watersports<\/code> view but doesn\u2019t use the <code class=\"fm-code-in-text\">Product<\/code> model type the view expects.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.19 Adding an action in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        private DataContext context;\n                \n        public HomeController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        \/\/ <i class=\"fm-italics\">...other actions omitted for brevity...<\/i>\n                \n        <b class=\"fm-bold\">public IActionResult WrongModel() {<\/b>\n            <b class=\"fm-bold\">return View(\"Watersports\", \"Hello, World!\");<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">This mistake isn\u2019t detected by the compiler because the <code class=\"fm-code-in-text\">WrongModel<\/code> action method is able to pass any object to the <code class=\"fm-code-in-text\">View<\/code> method. The problem will only become apparent at runtime, which you can see by using a browser to request http:\/\/localhost:5000\/home\/wrongmodel. When the view is rendered, the mismatch between type of view model and the type expected by the view is detected, producing the error shown in figure 21.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre202\" src=\"\/images\/proaspnetcore7\/000204.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.13 A view model type mismatch<\/p>\n<\/div>\n<p class=\"fm-head2\">Understanding the nullable type pitfall<\/p>\n<p class=\"body\">A complete type mismatch produces the kind of error shown in figure 21.13. This kind of problem is easy to detect and fix because the error is displayed as soon as the action method is invoked. There is, however, a more subtle mismatch, which can be harder to detect because it doesn\u2019t always produce an error. To help illustrate the issue, listing 21.20 sets the view model type in the <code class=\"fm-code-in-text\">Index<\/code> view.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.20 Setting the model type in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@model Product<\/b>\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n        Product Table\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request two URLs: http:\/\/localhost:5000\/home\/index\/1 and http:\/\/localhost:5000\/home\/index\/100. These URLs target the same action method, which renders the same view, but the second one produces an error, as shown in figure 21.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre203\" src=\"\/images\/proaspnetcore7\/000205.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.14 An error caused by a view model type mismatch<\/p>\n<\/div>\n<p class=\"body\">This issue arises when an action method uses a nullable type as the view model, which is how I wrote the <code class=\"fm-code-in-text\">Index<\/code> action method in the <code class=\"fm-code-in-text\">Home<\/code> controller in listing 21.9:<\/p>\n<pre class=\"programlisting\">...\npublic async Task&lt;IActionResult&gt; Index(long id = 1) {\n    <b class=\"fm-bold\">Product? prod<\/b> = await context.Products.FindAsync(id);\n    if (prod?.CategoryId == 1) {\n        return View(\"Watersports\", prod);\n    } else {\n        return View(prod);\n    }\n}\n...<\/pre>\n<p class=\"body\">The result of the LINQ query is a nullable <code class=\"fm-code-in-text\">Product<\/code>, which allows for queries for which there is no data in the database. The action method passes on the result to the <code class=\"fm-code-in-text\">View<\/code> method without filtering out the <code class=\"fm-code-in-text\">null<\/code> values, which mean that requests for which the database contains data work, because they product a <code class=\"fm-code-in-text\">Product<\/code> object, but requests for which there is no data fail, because they produce a <code class=\"fm-code-in-text\">null<\/code> value.<\/p>\n<p class=\"body\">One way to deal with this is to ensure that the action method doesn\u2019t pass <code class=\"fm-code-in-text\">null<\/code> values to the <code class=\"fm-code-in-text\">View<\/code> method. But another approach is to update the view so that expects a nullable view model type, as shown in listing 21.21.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.21 Changing type in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@model Product?<\/b>\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n        Product Table\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    <b class=\"fm-bold\">&lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;<\/b>\n                &lt;\/tr&gt;\n                <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The listing changes the view model type to <code class=\"fm-code-in-text\">Product?<\/code>, which is a nullable type. This change requires the use of the null conditional operator to safely deal with null values, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@<b class=\"fm-bold\">Model?<\/b>.Name&lt;\/td&gt;&lt;\/tr&gt;\n...<\/pre>\n<p class=\"body\">This technique is useful when you want to render the view even when the action method produces a null value. Save the changes to the view and use the browser to request http:\/\/localhost:5000\/home\/index\/100. The view will be rendered without an exception, but the null conditional operator will produce an empty table, because the null values the operator produces are discarded, as shown in figure 21.15.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre204\" src=\"\/images\/proaspnetcore7\/000206.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.15 Using a nullable view model type<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-398\">21.4 Understanding the Razor syntax<\/h2>\n<p class=\"body\">The Razor compiler separates the static fragments of HTML from the C# expressions, which are then handled separately in the generated class file. There are several types of expression that can be included in views, which I describe in the sections that follow.<a id=\"calibre_link-2172\"><\/a><a id=\"calibre_link-1214\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-399\">21.4.1 Understanding directives<\/h3>\n<p class=\"body\">Directives are expressions that give instructions to the Razor view engine. The <code class=\"fm-code-in-text\">@model<\/code> expression is a directive, for example, that tells the view engine to use a specific type for the view model, while the <code class=\"fm-code-in-text\">@using<\/code> directive tells the view engine to import a namespace. Table 21.5 describes the most useful Razor directives.<a id=\"calibre_link-2173\"><\/a><a id=\"calibre_link-2174\"><\/a><a id=\"calibre_link-2175\"><\/a><a id=\"calibre_link-2176\"><\/a><a id=\"calibre_link-2177\"><\/a><a id=\"calibre_link-2178\"><\/a><a id=\"calibre_link-2179\"><\/a><a id=\"calibre_link-2180\"><\/a><a id=\"calibre_link-2181\"><\/a><a id=\"calibre_link-2182\"><\/a><a id=\"calibre_link-2183\"><\/a><a id=\"calibre_link-2184\"><\/a><a id=\"calibre_link-1217\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 21.5 Useful Razor directives<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2185\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@model<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive specifies the type of the view model.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@using<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive imports a namespace.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@page<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive denotes a Razor Page, described in chapter 23.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@section<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive denotes a layout section, as described in chapter 22.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@addTagHelper<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive adds tag helpers to a view, as described in chapter 25.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@namespace<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive sets the namespace for the C# class generated from a view.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@functions<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive adds C# properties and methods to the C# class generated from a view and is commonly used in Razor Pages, as described in chapter 23.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@attribute<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive adds an attribute to the C# class generated from a view. I use this feature to apply authorization restrictions in chapter 38.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@implements<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive declares that the C# class generated from a view implements an interface. This feature is demonstrated in chapter 36.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@inherits<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive sets the base class for the C# class generated from a view. This feature is demonstrated in chapter 36.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@inject<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This directive provides a view with direct access to a service through dependency injection. This feature is demonstrated in chapter 23.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-400\">21.4.2 Understanding content expressions<\/h3>\n<p class=\"body\">Razor content expressions produce content that is included in the output generated by a view. Table 21.6 describes the most useful content expressions, which are demonstrated in the sections that follow.<a id=\"calibre_link-2186\"><\/a><a id=\"calibre_link-2187\"><\/a><a id=\"calibre_link-2188\"><\/a><a id=\"calibre_link-2189\"><\/a><a id=\"calibre_link-2190\"><\/a><a id=\"calibre_link-2191\"><\/a><a id=\"calibre_link-2192\"><\/a><a id=\"calibre_link-2193\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 21.6 Useful Razor content expressions<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2194\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@&lt;expression&gt;<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This is the basic Razor expression, which is evaluated, and the result it produces is inserted into the response.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@if<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This expression is used to select regions of content based on the result of an expression. See the \u201cUsing Conditional Expressions\u201d section for examples.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@switch<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This expression is used to select regions of content based on the result of an expression. See the \u201cUsing Conditional Expressions\u201d section for examples.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@foreach<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This expression generates the same region of content for each element in a sequence. See the \u201cEnumerating Sequences\u201d for examples.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@{ ... }<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This expression defines a code block. See the \u201cUsing Razor Code Blocks\u201d section for an example.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@:<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This expression denotes a section of content that is not enclosed in HTML elements. See the \u201cUsing Conditional Expressions\u201d section for an example.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@try<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This expression is used to catch exceptions.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@await<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This expression is used to perform an asynchronous operation, the result of which is inserted into the response. See chapter 24 for examples.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-401\">21.4.3 Setting element content<\/h3>\n<p class=\"body\">The simplest expressions are evaluated to produce a single value that is used as the content for an HTML element in the response sent to the client. The most common type of expression inserts a value from the view model object, like these expressions from the <code class=\"fm-code-in-text\">Watersports.cshtml<\/code> view file:<a id=\"calibre_link-2195\"><\/a><a id=\"calibre_link-1221\"><\/a><\/p>\n<pre class=\"programlisting\">...\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;<b class=\"fm-bold\">@Model.Name<\/b>&lt;\/td&gt;&lt;\/tr&gt;\n&lt;tr&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;<b class=\"fm-bold\">@Model.Price.ToString(\"c\")<\/b>&lt;\/td&gt;&lt;\/tr&gt;\n...<\/pre>\n<p class=\"body\">This type of expression can read property values or invoke methods, as these examples demonstrate. Views can contain more complex expressions, but these need to be enclosed in parentheses so that the Razor compiler can differentiate between the code and static content, as shown in listing 21.22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.22 Expressions in the Watersports.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Watersports\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;@Model.Price * 0.2m&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n                <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;@(Model.Price * 0.2m)&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000; the response, shown in figure 21.16, shows why parentheses are important.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre201\" src=\"\/images\/proaspnetcore7\/000207.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.16 Expressions with and without parentheses<\/p>\n<\/div>\n<p class=\"body\">The Razor View compiler matches expressions conservatively and has assumed that the asterisk and the numeric value in the first expression are static content. This problem is avoided by parentheses for the second expression.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-402\">21.4.4 Setting attribute values<\/h3>\n<p class=\"body\">An expression can be used to set the values of element attributes, as shown in listing 21.23.<a id=\"calibre_link-2196\"><\/a><a id=\"calibre_link-1218\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.23 Setting Attributes in the Watersports.cshtml File in the Views\/Home Folder<\/p>\n<pre class=\"programlisting\">@model Product\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Watersports\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        <b class=\"fm-bold\">&lt;table class=\"table table-sm table-striped table-bordered\"<\/b>\n               <b class=\"fm-bold\">data-id=\"@Model.ProductId\"&gt;<\/b>\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;@Model.Price * 0.2m&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;@(Model.Price * 0.2m)&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">I used the Razor expressions to set the value for a <code class=\"fm-code-in-text\">data<\/code> attribute on the <code class=\"fm-code-in-text\">table<\/code> element.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Data attributes, which are attributes whose names are prefixed by <code class=\"fm-code-in-text1\">data-<\/code>, have been an informal way of creating custom attributes for many years and were made part of the formal standard as part of HTML5. They are most often applied so that JavaScript code can locate specific elements or so that CSS styles can be more narrowly applied.<\/p>\n<p class=\"body\">If you request http:\/\/localhost:5000 and look at the HTML source that is sent to the browser, you will see that Razor has set the values of the attribute, like this:<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">...<\/b>\n<b class=\"fm-bold\">&lt;table class=\"table table-sm table-striped table-bordered\" data-id=\"1\"&gt;<\/b>\n    &lt;tbody&gt;\n        &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;Kayak&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;$275.00&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;275.00 * 0.2m&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;55.000&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n...<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-403\">21.4.5 Using conditional expressions<\/h3>\n<p class=\"body\">Razor supports conditional expressions, which means that the output can be tailored based on the view model. This technique is at the heart of Razor and allows you to create complex and fluid responses from views that are simple to read and maintain. In listing 21.24, I have added a conditional statement to the <code class=\"fm-code-in-text\">Watersports<\/code> view.<a id=\"calibre_link-2197\"><\/a><a id=\"calibre_link-1219\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.24 An if expression in the Watersports.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Watersports\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"\n               data-id=\"@Model.ProductId\"&gt;\n            &lt;tbody&gt;\n                <b class=\"fm-bold\">@if (Model.Price &gt; 200) {<\/b>\n                        <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;Luxury @Model.Name&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n                <b class=\"fm-bold\">} else {<\/b>\n                        <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;Basic @Model.Name&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n                <b class=\"fm-bold\">}<\/b>\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;@Model.Price * 0.2m&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;@(Model.Price * 0.2m)&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@<\/code> character is followed by the <code class=\"fm-code-in-text\">if<\/code> keyword and a condition that will be evaluated at runtime. The <code class=\"fm-code-in-text\">if<\/code> expression supports optional <code class=\"fm-code-in-text\">else<\/code> and <code class=\"fm-code-in-text\">elseif<\/code> clauses and is terminated with a close brace (the <code class=\"fm-code-in-text\">}<\/code> character). If the condition is met, then the content in the <code class=\"fm-code-in-text\">if<\/code> clause is inserted into the response; otherwise, the content in the <code class=\"fm-code-in-text\">else<\/code> clause is used instead.<a id=\"calibre_link-1220\"><\/a><\/p>\n<p class=\"body\">Notice that the <code class=\"fm-code-in-text\">@<\/code> prefix isn\u2019t required to access a <code class=\"fm-code-in-text\">Model<\/code> property in the condition.<\/p>\n<pre class=\"programlisting\">...\n@if (<b class=\"fm-bold\">Model<\/b>.Price &gt; 200) {\n...<\/pre>\n<p class=\"body\">But the <code class=\"fm-code-in-text\">@<\/code> prefix is required inside the <code class=\"fm-code-in-text\">if<\/code> and <code class=\"fm-code-in-text\">else<\/code> clauses, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;Luxury <b class=\"fm-bold\">@Model.Name<\/b>&lt;\/td&gt;&lt;\/tr&gt;\n...<\/pre>\n<p class=\"body\">To see the effect of the conditional statement, use a browser to request http:\/\/localhost:5000\/home\/index\/1 and http:\/\/localhost:5000\/home\/index\/2. The conditional statement will produce different HTML elements for these URLs, as shown in figure 21.17.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre205\" src=\"\/images\/proaspnetcore7\/000208.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.17 Using a conditional statement<\/p>\n<\/div>\n<p class=\"body\">Razor also supports <code class=\"fm-code-in-text\">@switch<\/code> expressions, which can be a more concise way of handling multiple conditions, as shown in listing 21.25.<a id=\"calibre_link-2198\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.25 A switch in the Watersports.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Watersports\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"\n               data-id=\"@Model.ProductId\"&gt;\n            &lt;tbody&gt;\n                <b class=\"fm-bold\">@switch (Model.Name) {<\/b>\n                    <b class=\"fm-bold\">case \"Kayak\":<\/b>\n                        <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;Small Boat&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n                        <b class=\"fm-bold\">break;<\/b>\n                    <b class=\"fm-bold\">case \"Lifejacket\":<\/b>\n                        <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;Flotation Aid&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n                        <b class=\"fm-bold\">break;<\/b>\n                    <b class=\"fm-bold\">default:<\/b>\n                        <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Name&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n                        <b class=\"fm-bold\">break;<\/b>\n                <b class=\"fm-bold\">}<\/b>\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;@Model.Price * 0.2m&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;@(Model.Price * 0.2m)&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Conditional expressions can lead to the same blocks of content being duplicated for each result clause. In the <code class=\"fm-code-in-text\">switch<\/code> expression, for example, each <code class=\"fm-code-in-text\">case<\/code> clause differs only in the content of the <code class=\"fm-code-in-text\">td<\/code> element, while the <code class=\"fm-code-in-text\">tr<\/code> and <code class=\"fm-code-in-text\">th<\/code> elements remain the same. To remove this duplication, conditional expressions can be used within an element, as shown in listing 21.26.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.26 Setting content in the Watersports.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Watersports\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"\n               data-id=\"@Model.ProductId\"&gt;\n            &lt;tbody&gt;\n                 <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;<\/b>\n                    <b class=\"fm-bold\">@switch (Model.Name) {<\/b>\n                        <b class=\"fm-bold\">case \"Kayak\":<\/b>\n                            <b class=\"fm-bold\">@:Small Boat<\/b>\n                            <b class=\"fm-bold\">break;<\/b>\n                        <b class=\"fm-bold\">case \"Lifejacket\":<\/b>\n                            <b class=\"fm-bold\">@:Flotation Aid<\/b>\n                            <b class=\"fm-bold\">break;<\/b>\n                        <b class=\"fm-bold\">default:<\/b>\n                            <b class=\"fm-bold\">@Model.Name<\/b>\n                            <b class=\"fm-bold\">break;<\/b>\n                    <b class=\"fm-bold\">}<\/b>\n                &lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;@Model.Price * 0.2m&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Tax&lt;\/th&gt;&lt;td&gt;@(Model.Price * 0.2m)&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The Razor compiler needs help with literal values that are not enclosed in HTML elements, requiring the <code class=\"fm-code-in-text\">@:<\/code> prefix, like this:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">@:<\/b>Small Boat\n...<\/pre>\n<p class=\"body\">The compiler copes with HTML elements because it detects the open tag, but this additional help is required for text content. To see the effect of the <code class=\"fm-code-in-text\">switch<\/code> statement, use a web browser to request http:\/\/localhost:5000\/home\/index\/2, which produces the response shown in figure 21.18.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre206\" src=\"\/images\/proaspnetcore7\/000209.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.18 Using a switch expression with literal content<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-404\">21.4.6 Enumerating sequences<\/h3>\n<p class=\"body\">The Razor <code class=\"fm-code-in-text\">@foreach<\/code> expression generates content for each object in an array or a collection, which is a common requirement when processing data. Listing 21.27 adds an action method to the <code class=\"fm-code-in-text\">Home<\/code> controller that produces a sequence of objects.<a id=\"calibre_link-1222\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.27 Adding an action in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        private DataContext context;\n                \n        public HomeController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            Product? prod = await context.Products.FindAsync(id);\n            if (prod?.CategoryId == 1) {\n                return View(\"Watersports\", prod);\n            } else {\n                return View(prod);\n            }\n        }\n                \n        public IActionResult Common() {\n            return View();\n        }\n                \n        public IActionResult WrongModel() {\n            return View(\"Watersports\", \"Hello, World!\");\n        }\n                \n        <b class=\"fm-bold\">public IActionResult List() {<\/b>\n            <b class=\"fm-bold\">return View(context.Products);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The new action is called <code class=\"fm-code-in-text\">List<\/code>, and it provides its view with the sequence of <code class=\"fm-code-in-text\">Product<\/code> objects obtained from the Entity Framework Core data context. Add a Razor View file named <code class=\"fm-code-in-text\">List.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder and add the content shown in listing 21.28.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.28 The contents of the List.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;Product&gt;\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;Products&lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n                @foreach (Product p in Model) {\n                    &lt;tr&gt;&lt;td&gt;@p.Name&lt;\/td&gt;&lt;td&gt;@p.Price&lt;\/td&gt;&lt;\/tr&gt;\n                }\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">foreach<\/code> expression follows the same format as the C# <code class=\"fm-code-in-text\">foreach<\/code> statement, and I used the <code class=\"fm-code-in-text\">??<\/code> operator to fall back to an empty collection when the model is <code class=\"fm-code-in-text\">null<\/code>. In the example, the variable <code class=\"fm-code-in-text\">p<\/code> is assigned each object in the sequence provided by the action method. The content within the expression is duplicated for each object and inserted into the response after the expressions it contains are evaluated. In this case, the content in the <code class=\"fm-code-in-text\">foreach<\/code> expression generates a table row with cells that have their own expressions.<\/p>\n<pre class=\"programlisting\">...\n&lt;td&gt;<b class=\"fm-bold\">@p.Name<\/b>&lt;\/td&gt;&lt;td&gt;<b class=\"fm-bold\">@p.Price<\/b>&lt;\/td&gt;\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core so that the new action method will be available and use a browser to request http:\/\/localhost:5000\/home\/list, which produces the result shown in figure 21.19, showing how the <code class=\"fm-code-in-text\">foreach<\/code> expression populates a table body.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre207\" src=\"\/images\/proaspnetcore7\/000210.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.19 Using a foreach expression<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-405\">21.4.7 Using Razor code blocks<\/h3>\n<p class=\"body\">Code blocks are regions of C# content that do not generate content but that can be useful to perform tasks that support the expressions that do. Listing 21.29 adds a code block that calculates an average value.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The most common use of code blocks is to select a layout, which is described in chapter 22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 21.29 Using a code block in the List.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;Product&gt;\n<b class=\"fm-bold\">@{<\/b> \n    <b class=\"fm-bold\">decimal average = Model.Average(p =&gt; p.Price);<\/b>\n<b class=\"fm-bold\">}<\/b>\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;Products&lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n                @foreach (Product p in Model) {\n                    &lt;tr&gt;\n                        &lt;td&gt;@p.Name&lt;\/td&gt;&lt;td&gt;@p.Price&lt;\/td&gt;\n                        <b class=\"fm-bold\">&lt;td&gt;@((p.Price \/ average * 100).ToString(\"F1\"))<\/b>\n                            <b class=\"fm-bold\">% of average<\/b>\n                        <b class=\"fm-bold\">&lt;\/td&gt;<\/b>\n                    &lt;\/tr&gt;\n                }\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The code block is denoted by <code class=\"fm-code-in-text\">@{<\/code> and <code class=\"fm-code-in-text\">}<\/code> and contains standard C# statements. The code block in listing 21.29 uses LINQ to calculate a value that is assigned to a variable named <code class=\"fm-code-in-text\">average<\/code>, which is used in an expression to set the contents of a table cell, avoiding the need to repeat the average calculation for each object in the view model sequence. Use a browser to request http:\/\/localhost:5000\/home\/list, and you will see the response shown in figure 21.20.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Code blocks can become difficult to manage if they contain more than a few statements. For more complex tasks, consider using the view bag, described in chapter 22, or adding a nonaction method to the controller.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre208\" src=\"\/images\/proaspnetcore7\/000211.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 21.20 Using a code block<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-406\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Razor views are files that combine HTML and code expressions.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Views are compiled into C# classes whose methods are invoked to generate HTML content.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Views are selected as results in action methods, optionally passing data that will be used to generate the HTML result.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Views can be defined with a view model, which allows the code expressions in the view to be type-checked.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-407\">\n<div class=\"calibre1\" id=\"calibre_link-2199\">\n<h1 class=\"tochead\" id=\"calibre_link-2200\"><a id=\"calibre_link-2201\"><\/a><a id=\"calibre_link-2202\"><\/a>22 Using controllers with views, part II<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2203\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using the view bag and temp data to pass data from an action to a view<\/li>\n<li class=\"co-summary-bullet\">Using layouts to define common content<\/li>\n<li class=\"co-summary-bullet\">Using partial views to define reusable sections of content<\/li>\n<li class=\"co-summary-bullet\">Encoding data within views<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I describe more of the features provided by Razor Views. I show you how to pass additional data to a view using the view bag and how to use layouts and layout sections to reduce duplication. I also explain how the results from expressions are encoded and how to disable the encoding process. Table 22.1 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 22.1 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2204\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Providing unstructured data to a view<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the view bag.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">5, 6<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Providing temporary data to a view<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use temp data.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">7, 8<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using the same content in multiple views<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a layout.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">9&ndash;12, 15&ndash;18<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Selecting the default layout for views<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a view start file.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">13, 14<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Interleaving unique and common content<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use layout sections.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">19&ndash;24<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating reusable sections of content<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a partial view.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">25&ndash;29<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Inserting HTML into a response using a Razor expression<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Encode the HTML.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">30&ndash;32<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Including JSON in a view<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the JSON encoder.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">33<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-408\">22.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the WebApp project from chapter 21. To prepare for this chapter, replace the contents of the <code class=\"fm-code-in-text\">HomeController.cs<\/code> file with the code shown in listing 22.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.1 The contents of the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        private DataContext context;\n                \n        public HomeController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            return View(await context.Products.FindAsync(id));\n        }\n                \n        public IActionResult List() {\n            return View(context.Products);\n        }\n    }\n}<\/pre>\n<p class=\"body\">One of the features used in this chapter requires the session feature, which was described in chapter 16. To enable sessions, add the statements shown in listing 22.2 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.2 Enabling sessions in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddControllersWithViews();\n\n<b class=\"fm-bold\">builder.Services.AddDistributedMemoryCache();<\/b>\n<b class=\"fm-bold\">builder.Services.AddSession(options =&gt; {<\/b>\n    <b class=\"fm-bold\">options.Cookie.IsEssential = true;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n<b class=\"fm-bold\">app.UseSession();<\/b>\napp.MapControllers();\napp.MapDefaultControllerRoute();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-409\">22.1.1 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 22.3 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.3 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-410\">22.1.2 Running the example application<\/h3>\n<p class=\"body\">Once the database has been dropped, use the PowerShell command prompt to run the command shown in listing 22.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.4 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet watch<\/pre>\n<p class=\"body\">The database will be seeded as part of the application startup. Once ASP.NET Core is running, use a web browser to request http:\/\/localhost:5000, which will produce the response shown in figure 22.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre209\" src=\"\/images\/proaspnetcore7\/000212.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.1 Running the example application<\/p>\n<\/div>\n<p class=\"body\">As noted in chapter 21, the <code class=\"fm-code-in-text\">dotnet watch<\/code> command is useful when working with views, but when you make a change that cannot be handled without restarting ASP.NET Core, you will see a message like this at the command prompt:<\/p>\n<pre class=\"programlisting\">watch : Unable to apply hot reload because of a rude edit.\nwatch : Do you want to restart your app - Yes (y) \/ No (n) \/\n    Always (a) \/ Never (v)?<\/pre>\n<p class=\"body\">The point at which this arises depends on the editor you have chosen, but when this happens, select the <code class=\"fm-code-in-text\">Always<\/code> option so that the application will always be restarted when a reload cannot be performed.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-411\">22.2 Using the view bag<\/h2>\n<p class=\"body\">Action methods provide views with data to display with a view model, but sometimes additional information is required. Action methods can use the <i class=\"fm-italics\">view bag<\/i> to provide a view with extra data, as shown in listing 22.5.<a id=\"calibre_link-2205\"><\/a><a id=\"calibre_link-1245\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.5 Using the view bag in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n<b class=\"fm-bold\">using Microsoft.EntityFrameworkCore;<\/b>\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        private DataContext context;\n                \n        public HomeController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            <b class=\"fm-bold\">ViewBag.AveragePrice =<\/b> \n                <b class=\"fm-bold\">await context.Products.AverageAsync(p =&gt; p.Price);<\/b>\n            return View(await context.Products.FindAsync(id));\n        }\n                \n        public IActionResult List() {\n            return View(context.Products);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ViewBag<\/code> property is inherited from the <code class=\"fm-code-in-text\">Controller<\/code> base class and returns a <code class=\"fm-code-in-text\">dynamic<\/code> object. This allows action methods to create new properties just by assigning values to them, as shown in the listing. The values assigned to the <code class=\"fm-code-in-text\">ViewBag<\/code> property by the action method are available to the view through a property also called <code class=\"fm-code-in-text\">ViewBag<\/code>, as shown in listing 22.6.<a id=\"calibre_link-2206\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.6 Using the view bag in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n        Product Table\n    &lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Price&lt;\/th&gt;\n                    &lt;td&gt;\n                        <b class=\"fm-bold\">@Model?.Price.ToString(\"c\")<\/b>\n                        <b class=\"fm-bold\">(@(((Model?.Price \/ ViewBag.AveragePrice)<\/b>\n                               <b class=\"fm-bold\">* 100).ToString(\"F2\"))% of average price)<\/b>     \n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ViewBag<\/code> property conveys the object from the action to the view, alongside the view model object. In the listing, the action method queries for the average of the <code class=\"fm-code-in-text\">Product.Price<\/code> properties in the database and assigns it to a view bag property named <code class=\"fm-code-in-text\">AveragePrice<\/code>, which the view uses in an expression. Use a browser to request http:\/\/localhost:5000, which produces the response shown in figure 22.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre209\" src=\"\/images\/proaspnetcore7\/000213.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.2 Using the view bag<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">When to use the view bag<\/p>\n<p class=\"fm-sidebar-text\">The view bag works best when it is used to provide the view with small amounts of supplementary data without having to create new view model classes for each action method. The problem with the view bag is that the compiler cannot check the use of the properties on dynamic objects, much like views that don\u2019t use an <code class=\"fm-code-in-text1\">@model<\/code> expression. It can be difficult to judge when a new view model class should be used, and my rule of thumb is to create a new view model class when the same view bag property is used by multiple actions or when an action method adds more than two or three properties to the view bag.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-412\">22.3 Using temp data<\/h2>\n<p class=\"body\">The temp data feature allows a controller to preserve data from one request to another, which is useful when performing redirections. Temp data is stored using a cookie unless session state is enabled when it is stored as session data. Unlike session data, temp data values are marked for deletion when they are read and removed when the request has been processed.<a id=\"calibre_link-2207\"><\/a><a id=\"calibre_link-2208\"><\/a><a id=\"calibre_link-1195\"><\/a><\/p>\n<p class=\"body\">Add a class file called <code class=\"fm-code-in-text\">CubedController.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Controllers<\/code> folder and use it to define the controller shown in listing 22.7.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.7 The contents of the CubedController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Controllers {\n    public class CubedController: Controller {\n        \n        public IActionResult Index() {\n            return View(\"Cubed\");\n        }\n                \n        public IActionResult Cube(double num) {\n            TempData[\"value\"] = num.ToString();\n            TempData[\"result\"] = Math.Pow(num, 3).ToString();\n            return RedirectToAction(nameof(Index));\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Cubed<\/code> controller defines an <code class=\"fm-code-in-text\">Index<\/code> method that selects a view named <code class=\"fm-code-in-text\">Cubed<\/code>. There is also a <code class=\"fm-code-in-text\">Cube<\/code> action, which relies on the model binding process to obtain a value for its <code class=\"fm-code-in-text\">num<\/code> parameter from the request (a process described in detail in chapter 28). The <code class=\"fm-code-in-text\">Cubed<\/code> action method performs its calculation and stores the <code class=\"fm-code-in-text\">num<\/code> value and the calculation result using <code class=\"fm-code-in-text\">TempData<\/code> property, which returns a dictionary that is used to store key-value pairs. Since the temp data feature is built on top of the sessions feature, only values that can be serialized to strings can be stored, which is why I convert both double values to strings in listing 22.7. Once the values are stored as temp data, the <code class=\"fm-code-in-text\">Cube<\/code> method performs a redirection to the <code class=\"fm-code-in-text\">Index<\/code> method. To provide the controller with a view, add a Razor View file named <code class=\"fm-code-in-text\">Cubed.cshtml<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Views\/Shared<\/code> folder with the content shown in listing 22.8.<a id=\"calibre_link-2209\"><\/a><a id=\"calibre_link-1243\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.8 The contents of the Cubed.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;Cubed&lt;\/h6&gt;\n    &lt;form method=\"get\" action=\"\/cubed\/cube\" class=\"m-2\"&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Value&lt;\/label&gt;\n            &lt;input name=\"num\" class=\"form-control\" \n            value=\"@(TempData[\"value\"])\" \/&gt;\n        &lt;\/div&gt;\n        &lt;button class=\"btn btn-primary mt-1\" type=\"submit\"&gt;\n            Submit\n        &lt;\/button&gt;\n    &lt;\/form&gt;\n    @if (TempData[\"result\"] != null) {\n            &lt;div class=\"bg-info text-white m-2 p-2\"&gt;\n                The cube of @TempData[\"value\"] is @TempData[\"result\"]\n            &lt;\/div&gt;\n    } \n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The base class used for Razor Views provides access to the temp data through a <code class=\"fm-code-in-text\">TempData<\/code> property, allowing values to be read within expressions. In this case, temp data is used to set the content of an <code class=\"fm-code-in-text\">input<\/code> element and display a results summary. Reading a temp data value doesn\u2019t remove it immediately, which means that values can be read repeatedly in the same view. It is only once the request has been processed that the marked values are removed.<\/p>\n<p class=\"body\">To see the effect, use a browser to navigate to http:\/\/localhost:5000\/cubed, enter a value into the form field, and click the Submit button. The browser will send a request that will set the temp data and trigger the redirection. The temp data values are preserved for the new request, and the results are displayed to the user. But reading the data values marks them for deletion, and if you reload the browser, the contents of the <code class=\"fm-code-in-text\">input<\/code> element and the results summary are no longer displayed, as shown in figure 22.3.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The object returned by the <code class=\"fm-code-in-text1\">TempData<\/code> property provides a <code class=\"fm-code-in-text1\">Peek<\/code> method, which allows you to get a data value without marking it for deletion, and a <code class=\"fm-code-in-text1\">Keep<\/code> method, which can be used to prevent a previously read value from being deleted. The <code class=\"fm-code-in-text1\">Keep<\/code> method doesn\u2019t protect a value forever. If the value is read again, it will be marked for removal once more. Use session data if you want to store items so that they won\u2019t be removed when the request is processed.<a id=\"calibre_link-2210\"><\/a><a id=\"calibre_link-2211\"><\/a><a id=\"calibre_link-1242\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre210\" src=\"\/images\/proaspnetcore7\/000214.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.3 Using temp data<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Using the temp data attribute<\/p>\n<p class=\"fm-sidebar-text\">Controllers can define properties that are decorated with the <code class=\"fm-code-in-text1\">TempData<\/code> attribute, which is an alternative to using the <code class=\"fm-code-in-text1\">TempData<\/code> property, like this:<a id=\"calibre_link-2212\"><\/a><\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Controllers {\n    public class CubedController: Controller {\n        \n        public IActionResult Index() {\n            return View(\"Cubed\");\n        }\n                \n        public IActionResult Cube(double num) {\n            <b class=\"fm-bold\">Value = num.ToString();<\/b>\n            <b class=\"fm-bold\">Result = Math.Pow(num, 3).ToString();<\/b>\n            return RedirectToAction(nameof(Index));\n        }\n                \n        <b class=\"fm-bold\">[TempData]<\/b>\n        <b class=\"fm-bold\">public string? Value { get; set; }<\/b>\n                \n        <b class=\"fm-bold\">[TempData]<\/b>\n        <b class=\"fm-bold\">public string? Result { get; set; }<\/b>\n    }\n}<\/pre>\n<p class=\"fm-sidebar-text\">The values assigned to these properties are automatically added to the temp data store, and there is no difference in the way they are accessed in the view. My preference is to use the <code class=\"fm-code-in-text1\">TempData<\/code> dictionary to store values because it makes the intent of the action method obvious to other developers. However, both approaches are entirely valid, and choosing between them is a matter of preference.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-413\">22.4 Working with layouts<\/h2>\n<p class=\"body\">The views in the example application contain duplicate elements that deal with setting up the HTML document, defining the <code class=\"fm-code-in-text\">head<\/code> section, loading the Bootstrap CSS file, and so on. Razor supports <i class=\"fm-italics\">layouts<\/i>, which consolidate common content in a single file that can be used by any view.<a id=\"calibre_link-2213\"><\/a><a id=\"calibre_link-1226\"><\/a><\/p>\n<p class=\"body\">Layouts are typically stored in the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder because they are usually used by the action methods of more than one controller. If you are using Visual Studio, right-click the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder, select Add &gt; New Item from the pop-up menu, and choose the Razor Layout template, as shown in figure 22.4. Make sure the name of the file is <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> and click the Add button to create the new file. Replace the content added to the file by Visual Studio with the elements shown in listing 22.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre211\" src=\"\/images\/proaspnetcore7\/000215.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.4 Creating a layout<\/p>\n<\/div>\n<p class=\"body\">If you are using Visual Studio Code, create a file named <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> in the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder and add the content shown in listing 22.9.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.9 The contents of the _Layout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Shared View&lt;\/h6&gt;\n    @RenderBody()\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The layout contains the common content that will be used by multiple views. The content that is unique to each view is inserted into the response by calling the <code class=\"fm-code-in-text\">RenderBody<\/code> method, which is inherited by the <code class=\"fm-code-in-text\">RazorPage&lt;T&gt;<\/code> class, as described in chapter 21. Views that use layouts can focus on just their unique content, as shown in listing 22.10.<a id=\"calibre_link-2214\"><\/a><a id=\"calibre_link-1232\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.10 Using a layout in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n<b class=\"fm-bold\">@{<\/b>\n    <b class=\"fm-bold\">Layout = \"_Layout\";<\/b>\n<b class=\"fm-bold\">}<\/b>\n&lt;div class=\"m-2\"&gt;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;tbody&gt;\n            &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;tr&gt;\n                &lt;th&gt;Price&lt;\/th&gt;\n                &lt;td&gt;\n                    @Model?.Price.ToString(\"c\")\n                    (@(((Model?.Price \/ ViewBag.AveragePrice)\n                            * 100).ToString(\"F2\"))% of average price)     \n                &lt;\/td&gt;\n            &lt;\/tr&gt;\n            &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">The layout is selected by adding a code block, denoted by the <code class=\"fm-code-in-text\">@{<\/code> and <code class=\"fm-code-in-text\">}<\/code> characters, that sets the <code class=\"fm-code-in-text\">Layout<\/code> property inherited from the <code class=\"fm-code-in-text\">RazorPage&lt;T&gt;<\/code> class. In this case, the <code class=\"fm-code-in-text\">Layout<\/code> property is set to the name of the layout file. As with normal views, the layout is specified without a path or file extension, and the Razor engine will search in the <code class=\"fm-code-in-text\">\/Views\/[controller]<\/code> and <code class=\"fm-code-in-text\">\/Views\/Shared<\/code> folders to find a matching file. Restart ASP.NET Core and use the browser to request http:\/\/localhost:5000, and you will see the response shown in figure 22.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre209\" src=\"\/images\/proaspnetcore7\/000216.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.5 Using a layout<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-414\">22.4.1 Configuring layouts using the view bag<\/h3>\n<p class=\"body\">The view can provide the layout with data values, allowing the common content provided by the view to be customized. The view bag properties are defined in the code block that selects the layout, as shown in listing 22.11.<a id=\"calibre_link-2215\"><\/a><a id=\"calibre_link-1227\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.11 Setting a property in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{\n    Layout = \"_Layout\";\n    <b class=\"fm-bold\">ViewBag.Title = \"Product Table\";<\/b>\n}\n&lt;div class=\"m-2\"&gt;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;tbody&gt;\n            &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;tr&gt;\n                &lt;th&gt;Price&lt;\/th&gt;\n                &lt;td&gt;\n                    @Model?.Price.ToString(\"c\")\n                    (@(((Model?.Price \/ ViewBag.AveragePrice)\n                            * 100).ToString(\"F2\"))% of average price)     \n                &lt;\/td&gt;\n            &lt;\/tr&gt;\n            &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">The view sets a <code class=\"fm-code-in-text\">Title<\/code> property, which can be used in the layout, as shown in listing 22.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.12 Using a property in the _Layout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    <b class=\"fm-bold\">&lt;title&gt;@ViewBag.Title&lt;\/title&gt;<\/b>\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    <b class=\"fm-bold\">&lt;h6 class=\"bg-primary text-white text-center m-2 p-2\"&gt;<\/b>\n        <b class=\"fm-bold\">@(ViewBag.Title ?? \"Layout\")<\/b>\n    <b class=\"fm-bold\">&lt;\/h6&gt;<\/b>\n    @RenderBody()\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Title<\/code> property is used to set the content of the <code class=\"fm-code-in-text\">title<\/code> element and <code class=\"fm-code-in-text\">h6<\/code> element in the <code class=\"fm-code-in-text\">body<\/code> section. Layouts cannot rely on view bag properties being defined, which is why the expression in the <code class=\"fm-code-in-text\">h6<\/code> element provides a fallback value if the view doesn\u2019t define a <code class=\"fm-code-in-text\">Title<\/code> property. To see the effect of the view bag property, use a browser to request http:\/\/localhost:5000, which produces the response shown in figure 22.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre209\" src=\"\/images\/proaspnetcore7\/000217.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.6 Using a view bag property to configure a layout<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding view bag precedence<\/p>\n<p class=\"fm-sidebar-text\">The values defined by the view take precedence if the same view bag property is defined by the view and the action method. If you want to allow the action to override the value defined in the view, then use a statement like this in the view code block:<\/p>\n<pre class=\"programlisting\">...\n@{  \n    Layout = \"_Layout\";\n    <b class=\"fm-bold\">ViewBag.Title = ViewBag.Title ?? \"Product Table\";<\/b>\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">This statement will set the value for the <code class=\"fm-code-in-text1\">Title<\/code> property only if it has not already been defined by the action method.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-415\">22.4.2 Using a view start file<\/h3>\n<p class=\"body\">Instead of setting the <code class=\"fm-code-in-text\">Layout<\/code> property in every view, you can add a <i class=\"fm-italics\">view start<\/i> file to the project that provides a default <code class=\"fm-code-in-text\">Layout<\/code> value. If you are using Visual Studio, right-click the <code class=\"fm-code-in-text\">Views<\/code> folder item in the Solution Explorer, select Add &gt; New Item, and locate the Razor View Start template, as shown in figure 22.7. Make sure the name of the file is <code class=\"fm-code-in-text\">_ViewStart.cshtml<\/code> and click the Add button to create the file, which will have the content shown in listing 22.13.<a id=\"calibre_link-2216\"><\/a><a id=\"calibre_link-1247\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre212\" src=\"\/images\/proaspnetcore7\/000218.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.7 Creating a view start file<\/p>\n<\/div>\n<p class=\"body\">If you are using Visual Studio Code, then add a file named <code class=\"fm-code-in-text\">_ViewStart.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views<\/code> folder and add the content shown in listing 22.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.13 The contents of the _ViewStart.cshtml file in the Views folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = \"_Layout\";\n}<\/pre>\n<p class=\"body\">The file sets the <code class=\"fm-code-in-text\">Layout<\/code> property, and the value will be used as the default. Listing 22.14 simplifies the <code class=\"fm-code-in-text\">Common.cshtml<\/code> file, leaving just content that is unique to the view.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.14 Removing content in the Common.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;Shared View&lt;\/h6&gt;<\/pre>\n<p class=\"body\">The view doesn\u2019t define a view model type and doesn\u2019t need to set the <code class=\"fm-code-in-text\">Layout<\/code> property because the project contains a view start file. The result is that the content in listing 22.14 will be added to the <code class=\"fm-code-in-text\">body<\/code> section of the HTML content of the response. Use a browser to navigate to http:\/\/localhost:5000\/second, and you will see the response in figure 22.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre213\" src=\"\/images\/proaspnetcore7\/000219.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.8 Using a view start file<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-416\">22.4.3 Overriding the default layout<\/h3>\n<p class=\"body\">There are two situations where you may need to define a <code class=\"fm-code-in-text\">Layout<\/code> property in a view even when there is a view start file in the project. In the first situation, a view requires a different layout from the one specified by the view start file. To demonstrate, add a Razor layout file named <code class=\"fm-code-in-text\">_ImportantLayout.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder with the content shown in listing 22.15.<a id=\"calibre_link-2217\"><\/a><a id=\"calibre_link-1230\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.15 The _ImportantLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h3 class=\"bg-warning text-white text-center p-2 m-2\"&gt;Important&lt;\/h3&gt;\n    @RenderBody()\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">In addition to the HTML document structure, this file contains a header element that displays <code class=\"fm-code-in-text\">Important<\/code> in large text. Views can select this layout by assigning its name to the <code class=\"fm-code-in-text\">Layout<\/code> property, as shown in listing 22.16.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> If you need to use a different layout for all the actions of a single controller, then add a view start file to the <code class=\"fm-code-in-text1\">Views\/[controller]<\/code> folder that selects the view you require. The Razor engine will use the layout specified by the controller-specific view start file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.16 Using a specific layout in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{\n    <b class=\"fm-bold\">Layout = \"_ImportantLayout\";<\/b>\n    ViewBag.Title = \"Product Table\";\n}\n&lt;div class=\"m-2\"&gt;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;tbody&gt;\n            &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;tr&gt;\n                &lt;th&gt;Price&lt;\/th&gt;\n                &lt;td&gt;\n                    @Model?.Price.ToString(\"c\")\n                    (@(((Model?.Price \/ ViewBag.AveragePrice)\n                            * 100).ToString(\"F2\"))% of average price)     \n                &lt;\/td&gt;\n            &lt;\/tr&gt;\n            &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Layout<\/code> value in the view start file is overridden by the value in the view, allowing different layouts to be applied. Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000, and the response will be produced using the new layout, as shown in figure 22.9.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Selecting a layout programmatically<\/p>\n<p class=\"fm-sidebar-text\">The value that a view assigns to the <code class=\"fm-code-in-text1\">Layout<\/code> property can be the result of an expression that allows layouts to be selected by the view, similar to the way that action methods can select views. Here is an example that selects the layout based on a property defined by the view model object:<a id=\"calibre_link-2218\"><\/a><a id=\"calibre_link-1233\"><\/a><\/p>\n<pre class=\"programlisting\">...\n@model Product?\n@{\n    <b class=\"fm-bold\">Layout = Model.Price &gt; 100 ? \"_ImportantLayout\" : \"_Layout\";<\/b>\n    ViewBag.Title = \"Product Table\";\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">The layout named <code class=\"fm-code-in-text1\">_ImportantLayout<\/code> is selected when the value of the view model object\u2019s <code class=\"fm-code-in-text1\">Price<\/code> property is greater than 100; otherwise, <code class=\"fm-code-in-text1\">_Layout<\/code> is used.<\/p>\n<\/div>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre214\" src=\"\/images\/proaspnetcore7\/000220.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.9 Specifying a layout in a view<\/p>\n<\/div>\n<p class=\"body\">The second situation where a <code class=\"fm-code-in-text\">Layout<\/code> property can be needed is when a view contains a complete HTML document and doesn\u2019t require a layout at all. To see the problem, open a new PowerShell command prompt and run the command shown in listing 22.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.17 Sending an HTTP request<\/p>\n<pre class=\"programlisting\">Invoke-WebRequest http:\/\/localhost:5000\/home\/list |\n     Select-Object -expand Content<\/pre>\n<p class=\"body\">This command sends an HTTP GET request whose response will be produced using the <code class=\"fm-code-in-text\">List.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder. This view contains a complete HTML document, which is combined with the content in the view specified by the view start file, producing a malformed HTML document, like this:<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Layout&lt;\/h6&gt;\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;Products&lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n            &lt;tbody&gt;\n                        \n                <i class=\"fm-italics\">&lt;!-- ...table rows omitted for brevity... &gt;<\/i>\n                                \n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The structural elements for the HTML document are duplicated, so there are two <code class=\"fm-code-in-text\">html<\/code>, <code class=\"fm-code-in-text\">head<\/code>, <code class=\"fm-code-in-text\">body<\/code>, and <code class=\"fm-code-in-text\">link<\/code> elements. Browsers are adept at handling malformed HTML but don\u2019t always cope with poorly structured content. Where a view contains a complete HTML document, the <code class=\"fm-code-in-text\">Layout<\/code> property can be set to <code class=\"fm-code-in-text\">null<\/code>, as shown in listing 22.18.<a id=\"calibre_link-2219\"><\/a><a id=\"calibre_link-1228\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.18 Disabling layouts in the List.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;Product&gt;\n@{\n    <b class=\"fm-bold\">Layout = null;<\/b>\n    decimal average = Model.Average(p =&gt; p.Price);\n}\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;Products&lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n                @foreach (Product p in Model) {\n                    &lt;tr&gt;\n                        &lt;td&gt;@p.Name&lt;\/td&gt;&lt;td&gt;@p.Price&lt;\/td&gt;\n                        &lt;td&gt;@((p.Price \/ average * 100).ToString(\"F1\"))\n                            % of average\n                        &lt;\/td&gt;\n                    &lt;\/tr&gt;\n                }\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Save the view and run the command shown in listing 22.17 again, and you will see that the response contains only the elements in the view and that the layout has been disabled.<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;Products&lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n            &lt;tbody&gt;\n                <i class=\"fm-italics\">&lt;!-- ...table rows omitted for brevity... &gt;<\/i>\n                                \n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-417\">22.4.4 Using layout sections<\/h3>\n<p class=\"body\">The Razor View engine supports the concept of <i class=\"fm-italics\">sections<\/i>, which allow you to provide regions of content within a layout. Razor sections give greater control over which parts of the view are inserted into the layout and where they are placed. To demonstrate the sections feature, I have edited the <code class=\"fm-code-in-text\">\/Views\/Home\/Index.cshtml<\/code> file, as shown in listing 22.19. The browser will display an error when you save the changes in this listing, which will be resolved when you make corresponding changes in the next listing.<a id=\"calibre_link-2220\"><\/a><a id=\"calibre_link-1231\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.19 Defining sections in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{\n    <b class=\"fm-bold\">Layout = \"_Layout\";<\/b>\n    ViewBag.Title = \"Product Table\";\n}\n\n@section Header {\n    Product Information\n}\n\n<b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n<b class=\"fm-bold\">&lt;tr&gt;<\/b>\n    <b class=\"fm-bold\">&lt;th&gt;Price&lt;\/th&gt;<\/b>\n    <b class=\"fm-bold\">&lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n<b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n\n<b class=\"fm-bold\">@section Footer {<\/b>\n    <b class=\"fm-bold\">@(((Model?.Price \/ ViewBag.AveragePrice)<\/b>\n            <b class=\"fm-bold\">* 100).ToString(\"F2\"))% of average price<\/b>\n<b class=\"fm-bold\">}<\/b><\/pre>\n<p class=\"body\">Sections are defined using the Razor <code class=\"fm-code-in-text\">@section<\/code> expression followed by a name for the section. Listing 22.19 defines sections named <code class=\"fm-code-in-text\">Header<\/code> and <code class=\"fm-code-in-text\">Footer<\/code>, and sections can contain the same mix of HTML content and expressions, just like the main part of the view. Sections are applied in a layout with the <code class=\"fm-code-in-text\">@RenderSection<\/code> expression, as shown in listing 22.20.<a id=\"calibre_link-2221\"><\/a><a id=\"calibre_link-2222\"><\/a><a id=\"calibre_link-2223\"><\/a><a id=\"calibre_link-1215\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.20 Using sections in the _Layout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    <b class=\"fm-bold\">&lt;div class=\"bg-info text-white m-2 p-1\"&gt;<\/b>\n        <b class=\"fm-bold\">This is part of the layout<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        \n    <b class=\"fm-bold\">&lt;h6 class=\"bg-primary text-white text-center m-2 p-2\"&gt;<\/b>\n        <b class=\"fm-bold\">@RenderSection(\"Header\")<\/b>\n    <b class=\"fm-bold\">&lt;\/h6&gt;<\/b>\n        \n    <b class=\"fm-bold\">&lt;div class=\"bg-info text-white m-2 p-1\"&gt;<\/b>\n        <b class=\"fm-bold\">This is part of the layout<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        \n    <b class=\"fm-bold\">&lt;div class=\"m-2\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;table class=\"table table-sm table-striped table-bordered\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;tbody&gt;<\/b>\n                @RenderBody()\n            <b class=\"fm-bold\">&lt;\/tbody&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/table&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        \n    <b class=\"fm-bold\">&lt;div class=\"bg-info text-white m-2 p-1\"&gt;<\/b>\n        <b class=\"fm-bold\">This is part of the layout<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        \n    <b class=\"fm-bold\">&lt;h6 class=\"bg-primary text-white text-center m-2 p-2\"&gt;<\/b>\n        <b class=\"fm-bold\">@RenderSection(\"Footer\")<\/b>\n    <b class=\"fm-bold\">&lt;\/h6&gt;<\/b>\n        \n    <b class=\"fm-bold\">&lt;div class=\"bg-info text-white m-2 p-1\"&gt;<\/b>\n        <b class=\"fm-bold\">This is part of the layout<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">When the layout is applied, the <code class=\"fm-code-in-text\">RenderSection<\/code> expression inserts the content of the specified section into the response. The regions of the view that are not contained within a section are inserted into the response by the <code class=\"fm-code-in-text\">RenderBody<\/code> method. To see how the sections are applied, use a browser to request http:\/\/localhost:5000, which provides the response shown in figure 22.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre215\" src=\"\/images\/proaspnetcore7\/000221.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.10 Using sections in a layout<\/p>\n<\/div>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> A view can define only the sections that are referred to in the layout. The view engine throws an exception if you define sections in the view for which there is no corresponding <code class=\"fm-code-in-text1\">@RenderSection<\/code> expression in the layout.<\/p>\n<p class=\"body\">Sections allow views to provide fragments of content to the layout without specifying how they are used. As an example, listing 22.21 redefines the layout to consolidate the body and sections into a single HTML table.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.21 Using a table in the _Layout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            <b class=\"fm-bold\">&lt;thead&gt;<\/b>\n                <b class=\"fm-bold\">&lt;tr&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;th class=\"bg-primary text-white text-center\"<\/b>\n                            <b class=\"fm-bold\">colspan=\"2\"&gt;<\/b>\n                        <b class=\"fm-bold\">@RenderSection(\"Header\")<\/b>\n                    <b class=\"fm-bold\">&lt;\/th&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/thead&gt;<\/b>\n            &lt;tbody&gt;\n                @RenderBody()\n            &lt;\/tbody&gt;\n            <b class=\"fm-bold\">&lt;tfoot&gt;<\/b>\n                <b class=\"fm-bold\">&lt;tr&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;th class=\"bg-primary text-white text-center\"<\/b> \n                            <b class=\"fm-bold\">colspan=\"2\"&gt;<\/b>\n                        <b class=\"fm-bold\">@RenderSection(\"Footer\")<\/b>\n                    <b class=\"fm-bold\">&lt;\/th&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/tfoot&gt;<\/b>\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">To see the effect of the change to the view, use a browser to request http:\/\/localhost:5000, which will produce the response shown in figure 22.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre216\" src=\"\/images\/proaspnetcore7\/000222.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.11 Changing how sections are displayed in a layout<\/p>\n<\/div>\n<p class=\"fm-head2\">Using optional layout sections<\/p>\n<p class=\"body\">By default, a view must contain all the sections for which there are <code class=\"fm-code-in-text\">RenderSection<\/code> calls in the layout, and an exception will be thrown if the layout requires a section that the view hasn\u2019t defined. Listing 22.22 adds a call to the <code class=\"fm-code-in-text\">RenderSection<\/code> method that requires a section named <code class=\"fm-code-in-text\">Summary<\/code>.<a id=\"calibre_link-2224\"><\/a><a id=\"calibre_link-1229\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.22 Adding a section in the _Layout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;\n                    &lt;th class=\"bg-primary text-white text-center\"\n                            colspan=\"2\"&gt;\n                        @RenderSection(\"Header\")\n                    &lt;\/th&gt;\n                &lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n                @RenderBody()\n            &lt;\/tbody&gt;\n            &lt;tfoot&gt;\n                &lt;tr&gt;\n                    &lt;th class=\"bg-primary text-white text-center\" \n                            colspan=\"2\"&gt;\n                        @RenderSection(\"Footer\")\n                    &lt;\/th&gt;\n                &lt;\/tr&gt;\n            &lt;\/tfoot&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n    @RenderSection(\"Summary\")\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000, and you will see the exception shown in figure 22.12. You may have to restart the <code class=\"fm-code-in-text\">dotnet watch<\/code> command to see this error.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre217\" src=\"\/images\/proaspnetcore7\/000223.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.12 Attempting to render a nonexistent view section<\/p>\n<\/div>\n<p class=\"body\">There are two ways to solve this problem. The first is to create an optional section, which will be rendered only if it is defined by the view. Optional sections are created by passing a second argument to the <code class=\"fm-code-in-text\">RenderSection<\/code> method, as shown in listing 22.23.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.23 An optional section in the _Layout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;\n                    &lt;th class=\"bg-primary text-white text-center\"\n                            colspan=\"2\"&gt;\n                        <b class=\"fm-bold\">@RenderSection(\"Header\", false)<\/b>\n                    &lt;\/th&gt;\n                &lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n                @RenderBody()\n            &lt;\/tbody&gt;\n            &lt;tfoot&gt;\n                &lt;tr&gt;\n                    &lt;th class=\"bg-primary text-white text-center\" \n                            colspan=\"2\"&gt;\n                        <b class=\"fm-bold\">@RenderSection(\"Footer\", false)<\/b>\n                    &lt;\/th&gt;\n                &lt;\/tr&gt;\n            &lt;\/tfoot&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n    <b class=\"fm-bold\">@RenderSection(\"Summary\", false)<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The second argument specifies whether a section is required, and using <code class=\"fm-code-in-text\">false<\/code> prevents an exception when the view doesn\u2019t define the section.<\/p>\n<p class=\"fm-head2\">Testing for layout sections<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">IsSectionDefined<\/code> method is used to determine whether a view defines a specified section and can be used in an <code class=\"fm-code-in-text\">if<\/code> expression to render fallback content, as shown in listing 22.24.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.24 Checking a section in the _Layout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;\n                    &lt;th class=\"bg-primary text-white text-center\"\n                            colspan=\"2\"&gt;\n                        @RenderSection(\"Header\", false)\n                    &lt;\/th&gt;\n                &lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n                @RenderBody()\n            &lt;\/tbody&gt;\n            &lt;tfoot&gt;\n                &lt;tr&gt;\n                    &lt;th class=\"bg-primary text-white text-center\" \n                            colspan=\"2\"&gt;\n                        @RenderSection(\"Footer\", false)\n                    &lt;\/th&gt;\n                &lt;\/tr&gt;\n            &lt;\/tfoot&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n    <b class=\"fm-bold\">@if (IsSectionDefined(\"Summary\")) {<\/b>\n        <b class=\"fm-bold\">@RenderSection(\"Summary\", false)<\/b>\n    <b class=\"fm-bold\">} else {<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"bg-info text-center text-white m-2 p-2\"&gt;<\/b>\n            <b class=\"fm-bold\">This is the default summary<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">}<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">IsSectionDefined<\/code> method is invoked with the name of the section you want to check and returns <code class=\"fm-code-in-text\">true<\/code> if the view defines that section. In the example, I used this helper to render fallback content when the view does not define the <code class=\"fm-code-in-text\">Summary<\/code> section. To see the fallback content, use a browser to request http:\/\/localhost:5000, which produces the response shown in figure 22.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre218\" src=\"\/images\/proaspnetcore7\/000224.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.13 Displaying fallback content for a view section<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-418\">22.5 Using partial views<\/h2>\n<p class=\"body\">You will often need to use the same set of HTML elements and expressions in several different places. <i class=\"fm-italics\">Partial views<\/i> are views that contain fragments of content that will be included in other views to produce complex responses without duplication.<a id=\"calibre_link-2225\"><\/a><a id=\"calibre_link-1236\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-419\">22.5.1 Enabling partial views<\/h3>\n<p class=\"body\">Partial views are applied using a feature called <i class=\"fm-italics\">tag helpers<\/i>, which are described in detail in chapter 25; tag helpers are configured in the view imports file, which was added to the project in chapter 21. To enable the feature required for partial views, add the statement shown in listing 22.25 to the <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> file.<a id=\"calibre_link-2226\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.25 Enabling tag helpers in the _ViewImports.cshtml file in the Views folder<\/p>\n<pre class=\"programlisting\">@using WebApp.Models\n<b class=\"fm-bold\">@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers<\/b><\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-420\">22.5.2 Creating a partial view<\/h3>\n<p class=\"body\">Partial views are just regular CSHTML files, and it is only the way they are used that differentiates them from standard views. If you are using Visual Studio, right-click the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder, select Add &gt; New Item, and use the Razor View template to create a file named <code class=\"fm-code-in-text\">_RowPartial.cshtml<\/code>. Once the file has been created, replace the contents with those shown in listing 22.26. If you are using Visual Studio Code, add a file named <code class=\"fm-code-in-text\">_RowPartial.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder and add to it the content shown in listing 22.26.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Visual Studio provides some tooling support for creating prepopulated partial views, but the simplest way to create a partial view is to create a regular view using the Razor View item template.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.26 The contents of the _RowPartial.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product\n\n&lt;tr&gt;\n    &lt;td&gt;@Model.Name&lt;\/td&gt;\n    &lt;td&gt;@Model.Price&lt;\/td&gt;\n&lt;\/tr&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">model<\/code> expression is used to define the view model type for the partial view, which contains the same mix of expressions and HTML elements as regular views. The content of this partial view creates a table row, using the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Price<\/code> properties of a <code class=\"fm-code-in-text\">Product<\/code> object to populate the table cells.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-421\">22.5.3 Applying a partial view<\/h3>\n<p class=\"body\">Partial views are applied by adding a <code class=\"fm-code-in-text\">partial<\/code> element in another view or layout. In listing 22.27, I have added the element to the <code class=\"fm-code-in-text\">List.cshtml<\/code> file so the partial view is used to generate the rows in the table.<a id=\"calibre_link-2227\"><\/a><a id=\"calibre_link-1237\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.27 Using a partial view in the List.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;Product&gt;\n@{\n    Layout = null;\n    decimal average = Model.Average(p =&gt; p.Price);\n}\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;Products&lt;\/h6&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n                @foreach (Product p in Model) {\n                    <b class=\"fm-bold\">&lt;partial name=\"_RowPartial\" model=\"p\" \/&gt;<\/b>\n                }\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The attributes applied to the <code class=\"fm-code-in-text\">partial<\/code> element control the selection and configuration of the partial view, as described in table 22.2.<\/p>\n<p class=\"fm-table-caption\">Table 22.2 The partial element attributes<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2228\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">name<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies the name of the partial view, which is located using the same search process as regular views.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">model<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies the value that will be used as the view model object for the partial view.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">for<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to define an expression that selects the view model object for the partial view, as explained next.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">view-data<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to provide the partial view with additional data.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">partial<\/code> element in listing 22.27 uses the <code class=\"fm-code-in-text\">name<\/code> attribute to select the <code class=\"fm-code-in-text\">_RowPartial<\/code> view and the <code class=\"fm-code-in-text\">model<\/code> attribute to select the <code class=\"fm-code-in-text\">Product<\/code> object that will be used as the view model object. The <code class=\"fm-code-in-text\">partial<\/code> element is applied within the <code class=\"fm-code-in-text\">@foreach<\/code> expression, which means that it will be used to generate each row in the table, which you can see by using a browser to request http:\/\/localhost:5000\/home\/list to produce the response shown in figure 22.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre219\" src=\"\/images\/proaspnetcore7\/000225.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.14 Using a partial view<\/p>\n<\/div>\n<p class=\"fm-head2\">Selecting the partial view model using an expression<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">for<\/code> attribute is used to set the partial view\u2019s model using an expression that is applied to the view\u2019s model, which is a feature more easily demonstrated than described. Add a partial view named <code class=\"fm-code-in-text\">_CellPartial.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder with the content shown in listing 22.28.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.28 The contents of the _CellPartial.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model string\n\n&lt;td class=\"bg-info text-white\"&gt;@Model&lt;\/td&gt;<\/pre>\n<p class=\"body\">This partial view has a string view model object, which it uses as the contents of a table cell element; the table cell element is styled using the Bootstrap CSS framework. In listing 22.29, I have added a <code class=\"fm-code-in-text\">partial<\/code> element to the <code class=\"fm-code-in-text\">_RowPartial.cshtml<\/code> file that uses the <code class=\"fm-code-in-text\">_CellPartial<\/code> partial view to display the table cell for the name of the <code class=\"fm-code-in-text\">Product<\/code> object.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.29 Using a partial in the _RowPartial.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product\n\n&lt;tr&gt;\n    <b class=\"fm-bold\">&lt;partial name=\"_CellPartial\" for=\"Name\" \/&gt;<\/b>\n    &lt;td&gt;@Model.Price&lt;\/td&gt;\n&lt;\/tr&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">for<\/code> attribute selects the <code class=\"fm-code-in-text\">Name<\/code> property as the model for the <code class=\"fm-code-in-text\">_CellPartial<\/code> partial view. To see the effect, use a browser to request http:\/\/localhost:5000\/home\/list, which will produce the response shown in figure 22.15.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre220\" src=\"\/images\/proaspnetcore7\/000226.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.15 Selecting a model property for use in a partial view<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Using templated delegates<\/p>\n<p class=\"fm-sidebar-text\">Templated delegates are an alternative way of avoiding duplication in a view. Templated delegates are defined in a code block, like this:<a id=\"calibre_link-2229\"><\/a><a id=\"calibre_link-1244\"><\/a><\/p>\n<pre class=\"programlisting\">...\n@{ \n    Func&lt;Product, object&gt; row \n        = @&lt;tr&gt;&lt;td&gt;@item.Name&lt;\/td&gt;&lt;td&gt;@item.Price&lt;\/td&gt;&lt;\/tr&gt;;\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">The template is a function that accepts a <code class=\"fm-code-in-text1\">Product<\/code> input object and returns a dynamic result. Within the template expression, the input object is referred to as <code class=\"fm-code-in-text1\">item<\/code> in expressions. The templated delegate is invoked as a method expression to generate content.<\/p>\n<pre class=\"programlisting\">...\n&lt;tbody&gt;\n    @foreach (Product p in Model) {\n        <b class=\"fm-bold\">@row(p)<\/b>\n    }\n&lt;\/tbody&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">I find this feature awkward and prefer using partial views, although this is a matter of preference and habit rather than any objective problems with the way that templated delegates work.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-422\">22.6 Understanding content-encoding<\/h2>\n<p class=\"body\">Razor Views provide two useful features for encoding content. The HTML content-encoding feature ensures that expression responses don\u2019t change the structure of the response sent to the browser, which is an important security feature. The JSON encoding feature encodes an object as JSON and inserts it into the response, which can be a useful debugging feature and can also be useful when providing data to JavaScript applications. Both encoding features are described in the following sections.<a id=\"calibre_link-2230\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-423\">22.6.1 Understanding HTML encoding<\/h3>\n<p class=\"body\">The Razor View engine encodes expression results to make them safe to include in an HTML document without changing its structure. This is an important feature when dealing with content that is provided by users, who may try to subvert the application or accidentally enter dangerous content. Listing 22.30 adds an action method to the <code class=\"fm-code-in-text\">Home<\/code> controller that passes a fragment of HTML to the <code class=\"fm-code-in-text\">View<\/code> method.<a id=\"calibre_link-2231\"><\/a><a id=\"calibre_link-2232\"><\/a><a id=\"calibre_link-1003\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.30 Adding an action in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        private DataContext context;\n                \n        public HomeController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            ViewBag.AveragePrice =\n                await context.Products.AverageAsync(p =&gt; p.Price);\n            return View(await context.Products.FindAsync(id));\n        }\n                \n        public IActionResult List() {\n            return View(context.Products);\n        }\n                \n        <b class=\"fm-bold\">public IActionResult Html() {<\/b>\n            <b class=\"fm-bold\">return View((object)\"This is a &lt;h3&gt;&lt;i&gt;string&lt;\/i&gt;&lt;\/h3&gt;\");<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The new action passes a string that contains HTML elements. To create the view for the new action method, add a Razor View file named <code class=\"fm-code-in-text\">Html.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder with the content shown in listing 22.31.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Notice that I cast the string passed to the <code class=\"fm-code-in-text1\">View<\/code> method as an object, without which the string is assumed to be the name of a view and not the view model object.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.31 The contents of the Html.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model string\n@{\n    Layout = null;\n}\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        @Model\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Save the file and use a browser to request http:\/\/localhost:5000\/home\/html. The response, which is shown on the left of figure 22.16, shows how the potentially dangerous characters in the view model string have been escaped.<\/p>\n<p class=\"body\">To include the result of an expression without safe encoding, you can invoke the <code class=\"fm-code-in-text\">Html.Raw<\/code> method. The <code class=\"fm-code-in-text\">Html<\/code> property is one of the properties added to the generated view class, described in chapter 21, which returns an object that implements the <code class=\"fm-code-in-text\">IHtmlHelper<\/code> interface, as shown in listing 22.32.<a id=\"calibre_link-2233\"><\/a><a id=\"calibre_link-1212\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.32 Disabling encoding in the Html.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model string\n@{ \n    Layout = null;\n}\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        <b class=\"fm-bold\">@Html.Raw(Model)<\/b>\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Save the changes, and you will see that the view model string is passed on without being encoded and is then interpreted by the browser as part of the HTML document, as shown on the right of figure 22.16.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre221\" src=\"\/images\/proaspnetcore7\/000227.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.16 HTML result encoding<\/p>\n<\/div>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Do not disable safe encoding unless you are entirely confident that no malicious content will be passed to the view. Careless use of this feature presents a security risk to your application and your users.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-424\">22.6.2 Understanding JSON encoding<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Json<\/code> property, which is added to the class generated from the view, as described in chapter 21, can be used to encode an object as JSON. The most common use for JSON data is in RESTful web services, as described in earlier chapters, but I find the Razor JSON encoding feature useful as a debugging aid when I don\u2019t get the output I expect from a view. Listing 22.33 adds a JSON representation of the view model object to the output produced by the <code class=\"fm-code-in-text\">Index.cshtml<\/code> view.<a id=\"calibre_link-2234\"><\/a><a id=\"calibre_link-1225\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 22.33 Using JSON encoding in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{\n    Layout = \"_Layout\";\n    ViewBag.Title = \"Product Table\";\n}\n\n@section Header {\n    Product Information\n}\n\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n&lt;tr&gt;\n    &lt;th&gt;Price&lt;\/th&gt;\n    &lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;\n&lt;\/tr&gt;\n&lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n\n@section Footer {\n    @(((Model?.Price \/ ViewBag.AveragePrice)\n            * 100).ToString(\"F2\"))% of average price\n}\n\n<b class=\"fm-bold\">@section Summary {<\/b>\n    <b class=\"fm-bold\">&lt;div class=\"bg-info text-white m-2 p-2\"&gt;<\/b>\n        <b class=\"fm-bold\">@Json.Serialize(Model)<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n<b class=\"fm-bold\">}<\/b><\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Json<\/code> property returns an implementation of the <code class=\"fm-code-in-text\">IJsonHelper<\/code> interface, whose <code class=\"fm-code-in-text\">Serialize<\/code> method produces a JSON representation of an object. Use a browser to request http:\/\/localhost:5000, and you will see the response shown in figure 22.17, which includes JSON in the <code class=\"fm-code-in-text\">Summary<\/code> section of the view.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre222\" src=\"\/images\/proaspnetcore7\/000228.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 22.17 Encoding an expression result as JSON<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-425\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The view bag is used to pass unstructured data to a view, in addition to the view model.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Temp data is similar to the view bag but is deleted once the data values have been read.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Layouts define common content, such as the header of an HTML document.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The default layout can be specified by creating a view start file.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Layouts can contain sections, which can be optional or mandatory.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Partial views define sections of content that can be reused within views.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-426\">\n<div class=\"calibre1\" id=\"calibre_link-2235\">\n<h1 class=\"tochead\" id=\"calibre_link-2236\"><a id=\"calibre_link-2237\"><\/a><a id=\"calibre_link-2238\"><\/a><a id=\"calibre_link-2239\"><\/a>23 Using Razor Pages<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2240\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Generating HTML content with Razor Pages<\/li>\n<li class=\"co-summary-bullet\">Routing HTTP requests to Razor Pages<\/li>\n<li class=\"co-summary-bullet\">Using single-file Razor Pages and using a separate code-behind class file<\/li>\n<li class=\"co-summary-bullet\">Using layouts and partial views with Razor Pages<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I introduce how to use Razor Pages, which is a simpler approach to generating HTML content, intended to capture some of the enthusiasm for the legacy ASP.NET Web Pages framework. I explain how Razor Pages work, explain how they differ from the controllers and views approach taken by the MVC Framework, and show you how they fit into the wider ASP.NET Core platform.<\/p>\n<p class=\"body\">The process of explaining how Razor Pages work can minimize the differences from the controllers and views described in earlier chapters. You might form the impression that Razor Pages are just MVC-lite and dismiss them, which would be a shame. Razor Pages are interesting because of the developer experience and not the way they are implemented.<\/p>\n<p class=\"body\">My advice is to give Razor Pages a chance, especially if you are an experienced MVC developer. Although the technology used will be familiar, the process of creating application features is different and is well-suited to small and tightly focused features that don\u2019t require the scale and complexity of controllers and views. I have been using the MVC Framework since it was first introduced, and I admit to ignoring the early releases of Razor Pages. Now, however, I find myself mixing Razor Pages and the MVC Framework in most projects, much as I did in the SportsStore example in part 1. Table 23.1 puts Razor Pages in context.<\/p>\n<p class=\"fm-table-caption\">Table 23.1 Putting Razor Pages in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2241\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Razor Pages are a simplified way of generating HTML responses.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The simplicity of Razor Pages means you can start getting results sooner than with the MVC Framework, which can require a relatively complex preparation process. Razor Pages are also easier for less experienced web developers to understand because the relationship between the code and content is more obvious.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Razor Pages associate a single view with the class that provides it with features and use a file-based routing system to match URLs.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Razor Pages are less flexible than the MVC Framework, which makes them unsuitable for complex applications. Razor Pages can be used only to generate HTML responses and cannot be used to create RESTful web services.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The MVC Framework\u2019s approach of controllers and views can be used instead of Razor Pages.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 23.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 23.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2242\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Enabling Razor Pages<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use <code class=\"fm-code-in-text1\">AddRazorPages<\/code> and <code class=\"fm-code-in-text1\">MapRazorPages<\/code> to set up the required services and middleware.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">3<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a self-contained endpoint<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create a Razor Page.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">4, 26, 27<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Routing requests to a Razor Page<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the name of the page or specify a route using the <code class=\"fm-code-in-text1\">@page<\/code> directive.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">5&ndash;8<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Providing logic to support the view section of a Razor Page<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a page model class.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">9&ndash;12<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating results that are not rendered using the view section of a Razor Page<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define a handler method that returns an action result.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">13&ndash;15<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Handling multiple HTTP methods<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define handlers in the page model class.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">16&ndash;18<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Avoiding duplication of content<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a layout or a partial view.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">19&ndash;25<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-427\">23.1 Preparing for this chapter<\/h2>\n<p class=\"body\"><a id=\"calibre_link-2243\"><\/a>This chapter uses the WebApp project from chapter 22. Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 23.1 to drop the database.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.1 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-428\">23.1.1 Running the example application<\/h3>\n<p class=\"body\">Once the database has been dropped, use the PowerShell command prompt to run the command shown in listing 23.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.2 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">The database will be seeded as part of the application startup. Once ASP.NET Core is running, use a web browser to request http:\/\/localhost:5000, which will produce the response shown in figure 23.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre223\" src=\"\/images\/proaspnetcore7\/000229.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.1 Running the example application<\/p>\n<\/div>\n<p class=\"body\">The <code class=\"fm-code-in-text\">dotnet watch<\/code> command can be useful with Razor Pages development, but it doesn\u2019t handle the initial configuration of the services and middleware or changes to the routing configuration, which is why I have returned to the <code class=\"fm-code-in-text\">dotnet run<\/code> command in this chapter.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-429\">23.2 Understanding Razor Pages<\/h2>\n<p class=\"body\">As you learn how Razor Pages work, you will see they share functionality with the MVC Framework. In fact, Razor Pages are typically described as a simplification of the MVC Framework&mdash;which is true&mdash;but that doesn\u2019t give any sense of why Razor Pages can be useful.<\/p>\n<p class=\"body\">The MVC Framework solves every problem in the same way: a controller defines action methods that select views to produce responses. It is a solution that works because it is so flexible: the controller can define multiple action methods that respond to different requests, the action method can decide which view will be used as the request is being processed, and the view can depend on private or shared partial views to produce its response.<\/p>\n<p class=\"body\">Not every feature in web applications needs the flexibility of the MVC Framework. For many features, a single action method will be used to handle a wide range of requests, all of which are dealt with using the same view. Razor Pages offer a more focused approach that ties together markup and C# code, sacrificing flexibility for focus.<\/p>\n<p class=\"body\">But Razor Pages have limitations. Razor Pages tend to start out focusing on a single feature but slowly grow out of control as enhancements are made. And, unlike MVC controllers, Razor Pages cannot be used to create web services.<\/p>\n<p class=\"body\">You don\u2019t have to choose just one model because the MVC Framework and Razor Pages coexist, as demonstrated in this chapter. This means that self-contained features can be easily developed with Razor Pages, leaving the more complex aspects of an application to be implemented using the MVC controllers and actions.<\/p>\n<p class=\"body\">In the sections that follow, I show you how to configure and use Razor Pages, and then I explain how they work and demonstrate the common foundation they share with MVC controllers and actions.<a id=\"calibre_link-2244\"><\/a><a id=\"calibre_link-1097\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-430\">23.2.1 Configuring Razor Pages<\/h3>\n<p class=\"body\">To prepare the application for Razor Pages, statements must be added to the <code class=\"fm-code-in-text\">Program.cs<\/code> file to set up services and configure the endpoint routing system, as shown in listing 23.3.<a id=\"calibre_link-2245\"><\/a><a id=\"calibre_link-2246\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.3 Configuring the application in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddControllersWithViews();\n<b class=\"fm-bold\">builder.Services.AddRazorPages();<\/b>\n\nbuilder.Services.AddDistributedMemoryCache();\nbuilder.Services.AddSession(options =&gt; {\n    options.Cookie.IsEssential = true;\n});\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.UseSession();\napp.MapControllers();\napp.MapDefaultControllerRoute();\n<b class=\"fm-bold\">app.MapRazorPages();<\/b>\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddRazorPages<\/code> method sets up the service that is required to use Razor Pages, and the <code class=\"fm-code-in-text\">MapRazorPages<\/code> method creates the routing configuration that matches URLs to pages, which is explained later in the chapter.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-431\">23.2.2 Creating a Razor Page<\/h3>\n<p class=\"body\">Razor Pages are defined in the <code class=\"fm-code-in-text\">Pages<\/code> folder. If you are using Visual Studio, create the <code class=\"fm-code-in-text\">WebApp\/Pages<\/code> folder, right-click it in the Solution Explorer, select Add &gt; New Item from the pop-up menu, and select the Razor Page template, as shown in figure 23.2. Set the Name field to <code class=\"fm-code-in-text\">Index.cshtml<\/code> and click the Add button to create the file and replace the contents of the file with those shown in listing 23.4.<a id=\"calibre_link-2247\"><\/a><a id=\"calibre_link-1098\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre224\" src=\"\/images\/proaspnetcore7\/000230.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.2 Creating a Razor Page<\/p>\n<\/div>\n<p class=\"body\">If you are using Visual Studio Code, create the <code class=\"fm-code-in-text\">WebApp\/Pages<\/code> folder and add to it a new file named <code class=\"fm-code-in-text\">Index.cshtml<\/code> with the content shown in listing 23.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.4 The contents of the Index.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n@model IndexModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using WebApp.Models;\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n        @Model.Product?.Name\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n\n@functions {\n\n    public class IndexModel : PageModel {\n        private DataContext context;\n                \n        public Product? Product { get; set; }\n                \n        public IndexModel(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task OnGetAsync(long id = 1) {\n            Product = await context.Products.FindAsync(id);\n        }\n    }\n}<\/pre>\n<p class=\"body\">Razor Pages use the Razor syntax that I described in chapters 21 and 22, and Razor Pages even use the same CSHTML file extension. But there are some important differences.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@page<\/code> directive must be the first thing in a Razor Page, which ensures that the file is not mistaken for a view associated with a controller. But the most important difference is that the <code class=\"fm-code-in-text\">@functions<\/code> directive is used to define the C# code that supports the Razor content in the same file. I explain how Razor Pages work shortly, but to see the output generated by the Razor Page, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/index, which produces the response shown in figure 23.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre225\" src=\"\/images\/proaspnetcore7\/000231.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.3 Using a Razor Page<\/p>\n<\/div>\n<p class=\"fm-head2\">Understanding the URL routing convention<\/p>\n<p class=\"body\">URL routing for Razor Pages is based on the file name and location, relative to the <code class=\"fm-code-in-text\">Pages<\/code> folder. The Razor Page in listing 23.4 is in a file named <code class=\"fm-code-in-text\">Index.cshtml<\/code>, in the <code class=\"fm-code-in-text\">Pages<\/code> folder, which means that it will handle requests for the <code class=\"fm-code-in-text\">\/index<\/code>. The routing convention can be overridden, as described in the \u201cUnderstanding Razor Pages Routing\u201d section, but, by default, it is the location of the Razor Page file that determines the URLs that it responds to.<a id=\"calibre_link-2248\"><\/a><a id=\"calibre_link-2249\"><\/a><a id=\"calibre_link-1105\"><\/a><\/p>\n<p class=\"fm-head2\">Understanding the page model<\/p>\n<p class=\"body\">In a Razor Page, the <code class=\"fm-code-in-text\">@model<\/code> directive is used to select a <i class=\"fm-italics\">page model<\/i> class, rather than identifying the type of the object provided by an action method. The <code class=\"fm-code-in-text\">@model<\/code> directive in listing 23.4 selects the <code class=\"fm-code-in-text\">IndexModel<\/code> class.<a id=\"calibre_link-2250\"><\/a><\/p>\n<pre class=\"programlisting\">...\n@model <b class=\"fm-bold\">IndexModel<\/b>\n...<\/pre>\n<p class=\"body\">The page model is defined within the <code class=\"fm-code-in-text\">@functions<\/code> directive and is derived from the <code class=\"fm-code-in-text\">PageModel<\/code> class, like this:<\/p>\n<pre class=\"programlisting\">...\n@functions {\n    public class IndexModel: <b class=\"fm-bold\">PageModel<\/b> {\n...<\/pre>\n<p class=\"body\">When the Razor Page is selected to handle an HTTP request, a new instance of the page model class is created, and dependency injection is used to resolve any dependencies that have been declared using constructor parameters, using the features described in chapter 14. The <code class=\"fm-code-in-text\">IndexModel<\/code> class declares a dependency on the <code class=\"fm-code-in-text\">DataContext<\/code> service created in chapter 18, which allows it to access the data in the database.<\/p>\n<pre class=\"programlisting\">...\npublic IndexModel(<b class=\"fm-bold\">DataContext ctx<\/b>) {\n    context = ctx;\n}\n...<\/pre>\n<p class=\"body\">After the page model object has been created, a handler method is invoked. The name of the handler method is <code class=\"fm-code-in-text\">On<\/code>, followed by the HTTP method for the request so that the <code class=\"fm-code-in-text\">OnGet<\/code> method is invoked when the Razor Page is selected to handle an HTTP GET request. Handler methods can be asynchronous, in which case a GET request will invoke the <code class=\"fm-code-in-text\">OnGetAsync<\/code> method, which is the method implemented by the <code class=\"fm-code-in-text\">IndexModel<\/code> class.<\/p>\n<pre class=\"programlisting\">...\npublic async Task <b class=\"fm-bold\">OnGetAsync<\/b>(long id = 1) {\n    Product = await context.Products.FindAsync(id);\n}\n...<\/pre>\n<p class=\"body\">Values for the handler method parameters are obtained from the HTTP request using the model binding process, which is described in detail in chapter 28. The <code class=\"fm-code-in-text\">OnGetAsync<\/code> method receives the value for its <code class=\"fm-code-in-text\">id<\/code> parameters from the model binder, which it uses to query the database and assign the result to its <code class=\"fm-code-in-text\">Product<\/code> property.<\/p>\n<p class=\"fm-head2\">Understanding the page view<\/p>\n<p class=\"body\">Razor Pages use the same mix of HTML fragments and code expressions to generate content, which defines the view presented to the user. The page model\u2019s methods and properties are accessible in the Razor Page through the <code class=\"fm-code-in-text\">@Model<\/code> expression. The <code class=\"fm-code-in-text\">Product<\/code> property defined by the <code class=\"fm-code-in-text\">IndexModel<\/code> class is used to set the content of an HTML element, like this:<a id=\"calibre_link-2251\"><\/a><a id=\"calibre_link-1100\"><\/a><\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n    <b class=\"fm-bold\">@Model.Product?.Name<\/b>\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@Model<\/code> expression returns an <code class=\"fm-code-in-text\">IndexModel<\/code> object, and this expression reads the <code class=\"fm-code-in-text\">Name<\/code> property of the object returned by the <code class=\"fm-code-in-text\">Product<\/code> property.<\/p>\n<p class=\"body\">The null conditional operator (<code class=\"fm-code-in-text\">?<\/code>) isn\u2019t required for the <code class=\"fm-code-in-text\">Model<\/code> property because it will always be assigned an instance of the page model class and cannot be <code class=\"fm-code-in-text\">null<\/code>. The properties defined by the page model class can be <code class=\"fm-code-in-text\">null<\/code>, however, which is why I have used the operator for the <code class=\"fm-code-in-text\">Product<\/code> property in the Razor expression:<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n    @Model.<b class=\"fm-bold\">Product?<\/b>.Name\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"fm-head2\">Understanding the generated C# class<\/p>\n<p class=\"body\">Behind the scenes, Razor Pages are transformed into C# classes, just like regular Razor views. Here is a simplified version of the C# class that is produced from the Razor Page in listing 23.4:<a id=\"calibre_link-2252\"><\/a><\/p>\n<pre class=\"programlisting\">namespace AspNetCoreGeneratedDocument {\n\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Threading.Tasks;\n    using Microsoft.AspNetCore.Mvc;\n    using Microsoft.AspNetCore.Mvc.Rendering;\n    using Microsoft.AspNetCore.Mvc.ViewFeatures;\n    using Microsoft.AspNetCore.Mvc.RazorPages;\n    using WebApp.Models;\n        \n    internal sealed class Pages_Index : \n            Microsoft.AspNetCore.Mvc.RazorPages.Page {\n        \n        public async override global::System.Threading.Tasks.Task \n                 ExecuteAsync() {\n            WriteLiteral(\"\\r\\n&lt;!DOCTYPE html&gt;\\r\\n&lt;html&gt;\\r\\n\");\n            __tagHelperExecutionContext = \n                __tagHelperScopeManager.Begin(\"head\", \n                    TagMode.StartTagAndEndTag, \"7d534...\", \n                    async() =&gt; {\n                        WriteLiteral(\"\\r\\n&lt;link href=\\\"\" + \n                            \"\/lib\/bootstrap\/css\/bootstrap.min.css\\\"\" + \n                            \"rel=\\\"stylesheet\\\" \/&gt;\\r\\n\");\n                    });\n            HeadTagHelper = CreateTagHelper&lt;TagHelpers.HeadTagHelper&gt;();\n            __tagHelperExecutionContext.Add(HeadTagHelper);\n            Write(__tagHelperExecutionContext.Output);\n            WriteLiteral(\"\\r\\n\");\n            __tagHelperExecutionContext = \n                __tagHelperScopeManager.Begin(\"body\", \n                    TagMode.StartTagAndEndTag, \"7d534...\", async() =&gt; {\n                WriteLiteral(\"\\r\\n&lt;div class=\\\"bg-primary text-white \" + \n                    \"text-center m-2 p-2\\\"&gt;\");\n                Write(Model.Product?.Name);\n                WriteLiteral(\"&lt;\/div&gt;\\r\\n\");\n            });\n            BodyTagHelper = CreateTagHelper&lt;TagHelpers.BodyTagHelper&gt;();\n            __tagHelperExecutionContext.Add(BodyTagHelper);\n            Write(__tagHelperExecutionContext.Output);\n            WriteLiteral(\"\\r\\n&lt;\/html&gt;\\r\\n\\r\\n\");\n        }\n                \n        public class IndexModel: PageModel {\n            private DataContext context;\n\n            public Product? Product { get; set; }\n\n            public IndexModel(DataContext ctx) {\n                context = ctx;\n            }\n\n            public async Task OnGetAsync(long id = 1) {\n                Product = await context.Products.FindAsync(id);\n            }\n        }\n        \n        public IModelExpressionProvider ModelExpressionProvider \n            { get; private set; }\n        public IUrlHelper Url { get; private set; }\n        public IViewComponentHelper Component { get; private set; }\n        public IJsonHelper Json { get; private set; }\n        public IHtmlHelper&lt;IndexModel&gt; Html { get; private set; }\n        public ViewDataDictionary&lt;IndexModel&gt; ViewData =&gt; \n             (ViewDataDictionary&lt;IndexModel&gt;)PageContext?.ViewData;\n        public IndexModel Model =&gt; ViewData.Model;\n    }\n}<\/pre>\n<p class=\"body\">If you compare this code with the equivalent shown in chapter 21, you can see how Razor Pages rely on the same features used by the MVC Framework. The HTML fragments and view expressions are transformed into calls to the <code class=\"fm-code-in-text\">WriteLiteral<\/code> and <code class=\"fm-code-in-text\">Write<\/code> methods.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The process for seeing the generated C# classes for Razor Pages is the same as for regular Razor Views, as described in chapter 21.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-432\">23.3 Understanding Razor Pages routing<\/h2>\n<p class=\"body\">Razor Pages rely on the location of the CSHTML file for routing so that a request for http:\/\/localhost:5000\/index is handled by the <code class=\"fm-code-in-text\">Pages\/Index.cshtml<\/code> file. Adding a more complex URL structure for an application is done by adding folders whose names represent the segments in the URL you want to support. As an example, create the <code class=\"fm-code-in-text\">WebApp\/Pages\/Suppliers<\/code> folder and add to it a Razor Page named <code class=\"fm-code-in-text\">List.cshtml<\/code> with the contents shown in listing 23.5.<a id=\"calibre_link-2253\"><\/a><a id=\"calibre_link-1110\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.5 The contents of the List.cshtml file in the Pages\/Suppliers folder<\/p>\n<pre class=\"programlisting\">@page\n@model ListModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using WebApp.Models;\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h5 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Suppliers&lt;\/h5&gt;\n    &lt;ul class=\"list-group m-2\"&gt;\n        @foreach (string s in Model.Suppliers) {\n            &lt;li class=\"list-group-item\"&gt;@s&lt;\/li&gt;\n        }\n    &lt;\/ul&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n\n@functions {\n\n    public class ListModel : PageModel {\n        private DataContext context;\n                \n        public IEnumerable&lt;string&gt; Suppliers { get; set; } \n            = Enumerable.Empty&lt;string&gt;();\n                        \n        public ListModel(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public void OnGet() {\n            Suppliers = context.Suppliers.Select(s =&gt; s.Name);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The new page model class defines a <code class=\"fm-code-in-text\">Suppliers<\/code> property that is set to the sequence of <code class=\"fm-code-in-text\">Name<\/code> values for the <code class=\"fm-code-in-text\">Supplier<\/code> objects in the database. The database operation in this example is synchronous, so the page model class defined the <code class=\"fm-code-in-text\">OnGet<\/code> method, rather than <code class=\"fm-code-in-text\">OnGetAsync<\/code>. The supplier names are displayed in a list using an <code class=\"fm-code-in-text\">@foreach<\/code> expression. To use the new Razor Page, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/suppliers\/list, which produces the response shown in figure 23.4. The path segments of the request URL correspond to the folder and file name of the <code class=\"fm-code-in-text\">List.cshtml<\/code> Razor Page.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre226\" src=\"\/images\/proaspnetcore7\/000232.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.4 Using a folder structure to route requests<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding the default URL handling<\/p>\n<p class=\"fm-sidebar-text\">The <code class=\"fm-code-in-text1\">MapRazorPages<\/code> method sets up a route for the default URL for the <code class=\"fm-code-in-text1\">Index.cshtml<\/code> Razor Page, following a similar convention used by the MVC Framework. It is for this reason that the first Razor Page added to a project is usually called <code class=\"fm-code-in-text1\">Index.cshtml<\/code>. However, when the application mixes Razor Pages and the MVC Framework together, the default route defined by Razor Pages takes precedence because it was created with a lower order (route ordering is described in chapter 13). This means a request http:\/\/localhost:5000 is handled by the <code class=\"fm-code-in-text1\">Index.cshtml<\/code> Razor Page in the example project and not the <code class=\"fm-code-in-text1\">Index<\/code> action on the <code class=\"fm-code-in-text1\">Home<\/code> controller.<a id=\"calibre_link-2254\"><\/a><a id=\"calibre_link-1112\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">If you want the MVC framework to handle the default URL, then you can change the order assigned to the Razor Pages routes, like this:<\/p>\n<pre class=\"programlisting\">...\napp.MapRazorPages()<b class=\"fm-bold\">.Add(b =&gt; ((RouteEndpointBuilder)b).Order = 2)<\/b>; \n...<\/pre>\n<p class=\"fm-sidebar-text\">The Razor Pages routes are created with an <code class=\"fm-code-in-text1\">Order<\/code> of <code class=\"fm-code-in-text1\">0<\/code>, which gives them precedence over the MVC routes, which are created with an <code class=\"fm-code-in-text1\">Order<\/code> of <code class=\"fm-code-in-text1\">1<\/code>. Assigning an <code class=\"fm-code-in-text1\">Order<\/code> of 2 gives the MVC framework routes precedence.<\/p>\n<p class=\"fm-sidebar-text\">In my own projects, where I mix Razor Pages and MVC controllers, I tend to rely on the MVC Framework to handle the default URL, and I avoid creating the <code class=\"fm-code-in-text1\">Index.cshtml<\/code> Razor Page to avoid confusion.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-433\">23.3.1 Specifying a routing pattern in a Razor Page<\/h3>\n<p class=\"body\">Using the folder and file structure to perform routing means there are no segment variables for the model binding process to use. Instead, values for the request handler methods are obtained from the URL query string, which you can see by using a browser to request http:\/\/localhost:5000\/index?id=2, which produces the response shown in figure 23.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre227\" src=\"\/images\/proaspnetcore7\/000233.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.5 Using a query string parameter<\/p>\n<\/div>\n<p class=\"body\">The query string provides a parameter named <code class=\"fm-code-in-text\">id<\/code>, which the model binding process uses to satisfy the <code class=\"fm-code-in-text\">id<\/code> parameter defined by the <code class=\"fm-code-in-text\">OnGetAsync<\/code> method in the <code class=\"fm-code-in-text\">Index<\/code> Razor Page.<a id=\"calibre_link-2255\"><\/a><a id=\"calibre_link-2256\"><\/a><a id=\"calibre_link-1104\"><\/a><\/p>\n<pre class=\"programlisting\">...\npublic async Task OnGetAsync(<b class=\"fm-bold\">long id = 1<\/b>) {\n...<\/pre>\n<p class=\"body\">I explain how model binding works in detail in chapter 28, but for now, it is enough to know that the query string parameter in the request URL is used to provide the <code class=\"fm-code-in-text\">id<\/code> argument when the <code class=\"fm-code-in-text\">OnGetAsync<\/code> method is invoked, which is used to query the database for a product.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@page<\/code> directive can be used with a routing pattern, which allows segment variables to be defined, as shown in listing 23.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.6 Defining a segment variable in the Index.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@page \"{id:long?}\"<\/b>\n@model IndexModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using WebApp.Models;\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n        @Model.Product?.Name\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n\n@functions {\n\n    \/\/ ...<i class=\"fm-italics\">statements omitted for brevity<\/i>...\n}<\/pre>\n<p class=\"body\">All the URL pattern features that are described in chapter 13 can be used with the <code class=\"fm-code-in-text\">@page<\/code> directive. The route pattern used in listing 23.6 adds an optional segment variable named <code class=\"fm-code-in-text\">id<\/code>, which is constrained so that it will match only those segments that can be parsed to a <code class=\"fm-code-in-text\">long<\/code> value. To see the change, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/index\/4, which produces the response shown on the left of figure 23.6.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@page<\/code> directive can also be used to override the file-based routing convention for a Razor Page, as shown in listing 23.7.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.7 Changing the route in the List.cshtml file in the Pages\/Suppliers folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@page \"\/lists\/suppliers\"<\/b>\n@model ListModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using WebApp.Models;\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h5 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Suppliers&lt;\/h5&gt;\n    &lt;ul class=\"list-group m-2\"&gt;\n        @foreach (string s in Model.Suppliers) {\n            &lt;li class=\"list-group-item\"&gt;@s&lt;\/li&gt;                \n        }\n    &lt;\/ul&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n\n@functions {\n\n    \/\/ ...statements omitted for brevity...\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1111\"><\/a>The directive changes the route for the <code class=\"fm-code-in-text\">List<\/code> page so that it matches URLs whose path is <code class=\"fm-code-in-text\">\/lists\/suppliers<\/code>. To see the effect of the change, restart ASP.NET Core and request http:\/\/localhost:5000\/lists\/suppliers, which produces the response shown on the right of figure 23.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre228\" src=\"\/images\/proaspnetcore7\/000234.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.6 Changing routes using the @page directive<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-434\">23.3.2 Adding routes for a Razor Page<\/h3>\n<p class=\"body\">Using the <code class=\"fm-code-in-text\">@page<\/code> directive replaces the default file-based route for a Razor Page. If you want to define multiple routes for a page, then configuration statements can be added to the <code class=\"fm-code-in-text\">Program.cs<\/code> file, as shown in listing 23.8.<a id=\"calibre_link-2257\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.8 Adding Razor Page routes in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc.RazorPages;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddDistributedMemoryCache();\nbuilder.Services.AddSession(options =&gt; {\n    options.Cookie.IsEssential = true;\n});\n\n<b class=\"fm-bold\">builder.Services.Configure&lt;RazorPagesOptions&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.Conventions.AddPageRoute(\"\/Index\", \"\/extra\/page\/{id:long?}\");<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.UseSession();\napp.MapControllers();\napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The options pattern is used to add additional routes for a Razor Page using the <code class=\"fm-code-in-text\">RazorPageOptions<\/code> class. The <code class=\"fm-code-in-text\">AddPageRoute<\/code> extension method is called on the <code class=\"fm-code-in-text\">Conventions<\/code> property to add a route for a page. The first argument is the path to the page, without the file extension and relative to the <code class=\"fm-code-in-text\">Pages<\/code> folder. The second argument is the URL pattern to add to the routing configuration. To test the new route, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/extra\/page\/2, which is matched by the URL pattern added in listing 23.8 and produces the response shown on the left of figure 23.7. The route added in listing 23.8 supplements the route defined by the <code class=\"fm-code-in-text\">@page<\/code> attribute, which you can test by requesting http:\/\/localhost:5000\/index\/2, which will produce the response shown on the right of figure 23.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre229\" src=\"\/images\/proaspnetcore7\/000235.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.7 Adding a route for a Razor Page<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-435\">23.4 Understanding the Page model class<\/h2>\n<p class=\"body\">Page models are derived from the <code class=\"fm-code-in-text\">PageModel<\/code> class, which provides the link between the rest of ASP.NET Core and the view part of the Razor Page. The <code class=\"fm-code-in-text\">PageModel<\/code> class provides methods for managing how requests are handled and properties that provide context data, the most useful of which are described in table 23.3. I have listed these properties for completeness, but they are not often required in Razor Page development, which focuses more on selecting the data that is required to render the view part of the page.<a id=\"calibre_link-2258\"><\/a><a id=\"calibre_link-2259\"><\/a><a id=\"calibre_link-2260\"><\/a><a id=\"calibre_link-1095\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 23.3 Selected PageModel properties for context data<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2261\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpContext<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">HttpContext<\/code> object, described in chapter 12.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ModelState<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property provides access to the model binding and validation features described in chapters 28 and 29.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PageContext<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">PageContext<\/code> object that provides access to many of the same properties defined by the <code class=\"fm-code-in-text1\">PageModel<\/code> class, along with additional information about the current page selection.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Request<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">HttpRequest<\/code> object that describes the current HTTP request, as described in chapter 12.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Response<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">HttpResponse<\/code> object that represents the current response, as described in chapter 12.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RouteData<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property provides access to the data matched by the routing system, as described in chapter 13.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">TempData<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property provides access to the temp data feature, which is used to store data until it can be read by a subsequent request. See chapter 22 for details.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">User<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an object that describes the user associated with the request, as described in chapter 38.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-436\">23.4.1 Using a code-behind class file<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@functions<\/code> directive allows the page-behind class and the Razor content to be defined in the same file, which is a development approach used by popular client-side frameworks, such as React or Vue.js.<a id=\"calibre_link-2262\"><\/a><a id=\"calibre_link-2263\"><\/a><\/p>\n<p class=\"body\">Defining code and markup in the same file is convenient but can become difficult to manage for more complex applications. Razor Pages can also be split into separate view and code files, which is similar to the MVC examples in previous chapters and is reminiscent of ASP.NET Web Pages, which defined C# classes in files known as <i class=\"fm-italics\">code-behind files<\/i>. The first step is to remove the page model class from the CSHTML file, as shown in listing 23.9. I have also removed the <code class=\"fm-code-in-text\">@using<\/code> expressions, which are no longer required.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.9 Removing the Page model class in the Index.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"{id:long?}\"\n<b class=\"fm-bold\">@model WebApp.Pages.IndexModel<\/b>\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n        @Model.Product?.Name\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@model<\/code> expression has been modified to specify the namespace of the page model, which wasn\u2019t required previously because the <code class=\"fm-code-in-text\">@functions<\/code> expression defined the <code class=\"fm-code-in-text\">IndexModel<\/code> class within the namespace of the view. When defining the separate page model class, I define the class in the <code class=\"fm-code-in-text\">WebApp.Pages<\/code> namespace. This isn\u2019t a requirement, but it makes the C# class consistent with the rest of the application.<\/p>\n<p class=\"body\">The convention for naming Razor Pages code-behind files is to append the <code class=\"fm-code-in-text\">.cs<\/code> file extension to the name of the view file. If you are using Visual Studio, the code-behind file was created by the Razor Page template when the <code class=\"fm-code-in-text\">Index.cshtml<\/code> file was added to the project. Expand the <code class=\"fm-code-in-text\">Index.cshtml<\/code> item in the Solution Explorer, and you will see the code-behind file, as shown in figure 23.8. Open the file for editing and replace the contents with the statements shown in listing 23.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre230\" src=\"\/images\/proaspnetcore7\/000236.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.8 Revealing the code-behind file in the Visual Studio Solution Explorer<\/p>\n<\/div>\n<p class=\"body\">If you are using Visual Studio Code, add a file named <code class=\"fm-code-in-text\">Index.cshtml.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Pages<\/code> folder with the content shown in listing 23.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.10 The contents of the Index.cshtml.cs file in the Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.RazorPages;\nusing WebApp.Models;\n\nnamespace WebApp.Pages {\n    public class IndexModel : PageModel {\n        private DataContext context;\n                \n        public Product? Product { get; set; }\n                \n        public IndexModel(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task OnGetAsync(long id = 1) {\n            Product = await context.Products.FindAsync(id);\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/index to ensure the code-behind file is used, producing the response shown in figure 23.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre231\" src=\"\/images\/proaspnetcore7\/000237.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.9 Using a code-behind file<\/p>\n<\/div>\n<p class=\"fm-head2\">Adding a view imports file<\/p>\n<p class=\"body\">A view imports file can be used to avoid using the fully qualified name for the page model class in the view file, performing the same role as the one I used in chapter 22 for the MVC Framework. If you are using Visual Studio, use the Razor View Imports template to add a file named <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Pages<\/code> folder, with the content shown in listing 23.11. If you are using Visual Studio Code, add the file directly.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.11 The contents of the _ViewImports.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@namespace WebApp.Pages\n@using WebApp.Models<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@namespace<\/code> directive sets the namespace for the C# class that is generated by a view, and using the directive in the view imports file sets the default namespace for all the Razor Pages in the application, with the effect that the view and its page model class are in the same namespace and the <code class=\"fm-code-in-text\">@model<\/code> directive does not require a fully qualified type, as shown in listing 23.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.12 Removing the namespace in the Index.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"{id:long?}\"\n<b class=\"fm-bold\">@model IndexModel<\/b>\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n        @Model.Product?.Name\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use the browser to request http:\/\/localhost:5000\/index. There is no difference in the response produced by the Razor Page, which is shown in figure 23.9.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-437\">23.4.2 Understanding action results in Razor Pages<\/h3>\n<p class=\"body\">Although it is not obvious, Razor Page handler methods use the same <code class=\"fm-code-in-text\">IActionResult<\/code> interface to control the responses they generate. To make page model classes easier to develop, handler methods have an implied result that displays the view part of the page. Listing 23.13 makes the result explicit.<a id=\"calibre_link-2264\"><\/a><a id=\"calibre_link-2265\"><\/a><a id=\"calibre_link-1093\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.13 Using an explicit result in the Index.cshtml.cs file in the Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.RazorPages;\nusing WebApp.Models;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc;<\/b>\n\nnamespace WebApp.Pages {\n    public class IndexModel : PageModel {\n        private DataContext context;\n                \n        public Product? Product { get; set; }\n                \n        public IndexModel(DataContext ctx) {\n            context = ctx;\n        }\n                \n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; OnGetAsync(long id = 1) {<\/b>\n            Product = await context.Products.FindAsync(id);\n            <b class=\"fm-bold\">return Page();<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Page<\/code> method is inherited from the <code class=\"fm-code-in-text\">PageModel<\/code> class and creates a <code class=\"fm-code-in-text\">PageResult<\/code> object, which tells the framework to render the view part of the page. Unlike the <code class=\"fm-code-in-text\">View<\/code> method used in MVC action methods, the Razor Pages <code class=\"fm-code-in-text\">Page<\/code> method doesn\u2019t accept arguments and always renders the view part of the page that has been selected to handle the request.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">PageModel<\/code> class provides other methods that create different action results to produce different outcomes, as described in table 23.4.<\/p>\n<p class=\"fm-table-caption\">Table 23.4 The PageModel action result methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2266\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Page()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a 200 OK status code and renders the view part of the Razor Page.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">NotFound()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a 404 NOT FOUND status code.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">BadRequest(state)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a 400 BAD REQUEST status code. The method accepts an optional model state object that describes the problem to the client, as demonstrated in chapter 19.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">File(name, type)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a 200 OK response, sets the <code class=\"fm-code-in-text1\">Content-Type<\/code> header to the specified type, and sends the specified file to the client.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Redirect(path)<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectPermanent(path)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by these methods produces 302 FOUND and 301 MOVED PERMANENTLY responses, which redirect the client to the specified URL.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectToAction(name)<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectToActionPermanent(name)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by these methods produces 302 FOUND and 301 MOVED PERMANENTLY responses, which redirect the client to the specified action method. The URL used to redirect the client is produced using the routing features described in chapter 13.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectToPage(name)<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RedirectToPagePermanent(name)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by these methods produce 302 FOUND and 301 MOVED PERMANENTLY responses that redirect the client to another Razor Page. If no name is supplied, the client is redirected to the current page.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">StatusCode(code)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">IActionResult<\/code> returned by this method produces a response with the specific status code.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"fm-head2\">Using an action result<\/p>\n<p class=\"body\">Except for the <code class=\"fm-code-in-text\">Page<\/code> method, the methods in table 23.4 are the same as those available in action methods. However, care must be taken with these methods because sending a status code response is unhelpful in Razor Pages because they are used only when a client expects the content of the view.<\/p>\n<p class=\"body\">Instead of using the <code class=\"fm-code-in-text\">NotFound<\/code> method when requested data cannot be found, for example, a better approach is to redirect the client to another URL that can display an HTML message for the user. The redirection can be to a static HTML file, to another Razor Page, or to an action defined by a controller. Add a Razor Page named <code class=\"fm-code-in-text\">NotFound.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder and add the content shown in listing 23.14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.14 The contents of the NotFound.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/noid\"\n@model NotFoundModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using WebApp.Models;\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    &lt;title&gt;Not Found&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n        No Matching ID\n    &lt;\/div&gt;\n    &lt;ul class=\"list-group m-2\"&gt;\n        @foreach (Product p in Model.Products) {\n                &lt;li class=\"list-group-item\"&gt;\n                    @p.Name (ID: @p.ProductId)\n                &lt;\/li&gt;                \n        }\n    &lt;\/ul&gt;    \n&lt;\/body&gt;\n&lt;\/html&gt;\n\n@functions {\n\n    public class NotFoundModel : PageModel {\n        private DataContext context;\n                \n        public IEnumerable&lt;Product&gt; Products { get; set; }\n            = Enumerable.Empty&lt;Product&gt;();\n                        \n        public NotFoundModel(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public void OnGetAsync(long id = 1) {\n            Products = context.Products;\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@page<\/code> directive overrides the route convention so that this Razor Page will handle the <code class=\"fm-code-in-text\">\/noid<\/code> URL path. The page model class uses an Entity Framework Core context object to query the database and displays a list of the product names and key values that are in the database.<\/p>\n<p class=\"body\">In listing 23.15, I have updated the handle method of the <code class=\"fm-code-in-text\">IndexModel<\/code> class to redirect the user to the <code class=\"fm-code-in-text\">NotFound<\/code> page when a request is received that doesn\u2019t match a <code class=\"fm-code-in-text\">Product<\/code> object in the database.<a id=\"calibre_link-2267\"><\/a><a id=\"calibre_link-1094\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.15 Using a redirection in the Index.cshtml.cs file in the Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.RazorPages;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Pages {\n    public class IndexModel : PageModel {\n        private DataContext context;\n                \n        public Product? Product { get; set; }\n                \n        public IndexModel(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task&lt;IActionResult&gt; OnGetAsync(long id = 1) {\n            Product = await context.Products.FindAsync(id);\n            <b class=\"fm-bold\">if (Product == null) {<\/b>\n                <b class=\"fm-bold\">return RedirectToPage(\"NotFound\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">RedirectToPage<\/code> method produces an action result that redirects the client to a different Razor Page. The name of the target page is specified without the file extension, and any folder structure is specified relative to the <code class=\"fm-code-in-text\">Pages<\/code> folder. To test the redirection, restart ASP.NET Core and request http:\/\/localhost:5000\/index\/500, which provides a value of 500 for the <code class=\"fm-code-in-text\">id<\/code> segment variable and does not match anything in the database. The browser will be redirected and produce the result shown in figure 23.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre232\" src=\"\/images\/proaspnetcore7\/000238.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.10 Redirecting to a different Razor Page<\/p>\n<\/div>\n<p class=\"body\">Notice that the routing system is used to produce the URL to which the client is redirected, which uses the routing pattern specified with the <code class=\"fm-code-in-text\">@page<\/code> directive. In this example, the argument to the <code class=\"fm-code-in-text\">RedirectToPage<\/code> method was <code class=\"fm-code-in-text\">NotFound<\/code>, but this has been translated into a redirection to the <code class=\"fm-code-in-text\">\/noid<\/code> path specified by the <code class=\"fm-code-in-text\">@page<\/code> directive in listing 23.14.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-438\">23.4.3 Handling multiple HTTP methods<\/h3>\n<p class=\"body\">Razor Pages can define handler methods that respond to different HTTP methods. The most common combination is to support the GET and POST methods that allow users to view and edit data. To demonstrate, add a Razor Page called <code class=\"fm-code-in-text\">Editor.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder and add the content shown in listing 23.16.<a id=\"calibre_link-2268\"><\/a><a id=\"calibre_link-2269\"><\/a><a id=\"calibre_link-1101\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> I have kept this example as simple as possible, but there are excellent ASP.NET Core features for creating HTML forms and for receiving data when it is submitted, as described in chapter 31.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.16 The contents of the Editor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"{id:long}\"\n@model EditorModel\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;Editor&lt;\/div&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Product?.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;@Model.Product?.Price&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n        &lt;form method=\"post\"&gt;\n            @Html.AntiForgeryToken()\n            &lt;div class=\"form-group\"&gt;\n                &lt;label&gt;Price&lt;\/label&gt;\n                &lt;input name=\"price\" class=\"form-control\"\n                       value=\"@Model.Product?.Price\" \/&gt;\n            &lt;\/div&gt;\n            &lt;button class=\"btn btn-primary mt-2\" type=\"submit\"&gt;\n                Submit\n            &lt;\/button&gt;\n        &lt;\/form&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The elements in the Razor Page view create a simple HTML form that presents the user with an input element containing the value of the <code class=\"fm-code-in-text\">Price<\/code> property for a <code class=\"fm-code-in-text\">Product<\/code> object. The <code class=\"fm-code-in-text\">form<\/code> element is defined without an action attribute, which means the browser will send a POST request to the Razor Page\u2019s URL when the user clicks the Submit button.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The <code class=\"fm-code-in-text1\">@Html.AntiForgeryToken()<\/code> expression in listing 23.16 adds a hidden form field to the HTML form that ASP.NET Core uses to guard against cross-site request forgery (CSRF) attacks. I explain how this feature works in chapter 27, but for this chapter, it is enough to know that POST requests that do not contain this form field will be rejected.<\/p>\n<p class=\"body\">If you are using Visual Studio, expand the <code class=\"fm-code-in-text\">Editor.cshtml<\/code> item in the Solution Explorer to reveal the <code class=\"fm-code-in-text\">Editor.cshtml.cs<\/code> class file and replace its contents with the code shown in listing 23.17. If you are using Visual Studio Code, add a file named <code class=\"fm-code-in-text\">Editor.cshtml.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Pages<\/code> folder and use it to define the class shown in listing 23.17.<a id=\"calibre_link-2270\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.17 The contents of the Editor.cshtml.cs file in the Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\nusing WebApp.Models;\n\nnamespace WebApp.Pages {\n    public class EditorModel : PageModel {\n        private DataContext context;\n                \n        public Product? Product { get; set; }\n                \n        public EditorModel(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task OnGetAsync(long id) {\n            Product = await context.Products.FindAsync(id);\n        }\n                \n        public async Task&lt;IActionResult&gt; OnPostAsync(long id, \n                decimal price) {\n            Product? p = await context.Products.FindAsync(id);\n            if (p != null) {\n                p.Price = price;\n            }\n            await context.SaveChangesAsync();\n            return RedirectToPage();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The page model class defines two handler methods, and the name of the method tells the Razor Pages framework which HTTP method each handles. The <code class=\"fm-code-in-text\">OnGetAsync<\/code> method is used to handle GET requests, which it does by locating a <code class=\"fm-code-in-text\">Product<\/code>, whose details are displayed by the view.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnPostAsync<\/code> method is used to handle POST requests, which will be sent by the browser when the user submits the HTML form. The parameters for the <code class=\"fm-code-in-text\">OnPostAsync<\/code> method are obtained from the request so that the <code class=\"fm-code-in-text\">id<\/code> value is obtained from the URL route and the <code class=\"fm-code-in-text\">price<\/code> value is obtained from the form. (The model binding feature that extracts data from forms is described in chapter 28.)<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding the POST Redirection<\/p>\n<p class=\"fm-sidebar-text\">Notice that the last statement in the <code class=\"fm-code-in-text1\">OnPostAsync<\/code> method invokes the <code class=\"fm-code-in-text1\">RedirectToPage<\/code> method without an argument, which redirects the client to the URL for the Razor Page. This may seem odd, but the effect is to tell the browser to send a GET request to the URL it used for the POST request. This type of redirection means that the browser won\u2019t resubmit the POST request if the user reloads the browser, preventing the same action from being accidentally performed more than once.<\/p>\n<\/div>\n<p class=\"body\">To see how the page model class handles different HTTP methods, restart ASP.NET Core and use a browser to navigate to http:\/\/localhost:5000\/editor\/1. Edit the field to set the price to 100 and click the Submit button. The browser will send a POST request that is handled by the <code class=\"fm-code-in-text\">OnPostAsync<\/code> method. The database will be updated, and the browser will be redirected so that the updated data is displayed, as shown in figure 23.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre233\" src=\"\/images\/proaspnetcore7\/000239.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.11 Handling multiple HTTP methods<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-439\">23.4.4 Selecting a handler method<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1107\"><\/a>The page model class can define multiple handler methods, allowing the request to select a method using a <code class=\"fm-code-in-text\">handler<\/code> query string parameter or routing segment variable. To demonstrate this feature, add a Razor Page file named <code class=\"fm-code-in-text\">HandlerSelector.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 23.18.<a id=\"calibre_link-2271\"><\/a><a id=\"calibre_link-2272\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.18 The contents of the HandlerSelector.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n@model HandlerSelectorModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using Microsoft.EntityFrameworkCore\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;Selector&lt;\/div&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.Product?.Name&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;@Model.Product?.Price&lt;\/td&gt;&lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Category&lt;\/th&gt;\n                    &lt;td&gt;@Model.Product?.Category?.Name&lt;\/td&gt;\n                &lt;\/tr&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Supplier&lt;\/th&gt;\n                    &lt;td&gt;@Model.Product?.Supplier?.Name&lt;\/td&gt;\n                &lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n        &lt;a href=\"\/handlerselector\" class=\"btn btn-primary\"&gt;Standard&lt;\/a&gt;\n        &lt;a href=\"\/handlerselector?handler=related\" \n                class=\"btn btn-primary\"&gt;\n            Related\n        &lt;\/a&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n\n@functions {\n\n    public class HandlerSelectorModel : PageModel {\n        private DataContext context;\n                \n        public Product? Product { get; set; }\n                \n        public HandlerSelectorModel(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task OnGetAsync(long id = 1) {\n            Product = await context.Products.FindAsync(id);\n        }\n                \n        public async Task OnGetRelatedAsync(long id = 1) {\n            Product = await context.Products\n                .Include(p =&gt; p.Supplier)\n                .Include(p =&gt; p.Category)\n                .FirstOrDefaultAsync(p =&gt; p.ProductId == id);\n            if (Product != null &amp;&amp; Product.Supplier != null) {\n                Product.Supplier.Products = null;\n            }\n            if (Product != null &amp;&amp; Product.Category != null) {\n                Product.Category.Products = null;\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The page model class in this example defines two handler methods: <code class=\"fm-code-in-text\">OnGetAsync<\/code> and <code class=\"fm-code-in-text\">OnGetRelatedAsync<\/code>. The <code class=\"fm-code-in-text\">OnGetAsync<\/code> method is used by default, which you can see by restarting ASP.NET Core and using a browser to request http:\/\/localhost:5000\/handlerselector. The handler method queries the database and presents the result to the user, as shown on the left of figure 23.12.<\/p>\n<p class=\"body\">One of the anchor elements rendered by the page targets a URL with a handler query string parameter, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;a href=\"<b class=\"fm-bold\">\/handlerselector?handler=related<\/b>\" class=\"btn btn-primary\"&gt;\n    Related&lt;\/a&gt;\n...<\/pre>\n<p class=\"body\">The name of the handler method is specified without the <code class=\"fm-code-in-text\">On[method]<\/code> prefix and without the <code class=\"fm-code-in-text\">Async<\/code> suffix so that the <code class=\"fm-code-in-text\">OnGetRelatedAsync<\/code> method is selected using a handler value of <code class=\"fm-code-in-text\">related<\/code>. This alternative handler method includes related data in its query and presents additional data to the user, as shown on the right of figure 23.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre234\" src=\"\/images\/proaspnetcore7\/000240.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.12 Selecting handler methods<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Using rate limiting and output caching<\/p>\n<p class=\"fm-sidebar-text\">In chapters 19 and 21, I demonstrated the use of the ASP.NET Core attributes for applying rate limits and output caching to controllers. These attributes can also be applied to page model classes, providing Razor Pages with the same features available in the MVC Framework.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-440\">23.5 Understanding the Razor Page view<\/h2>\n<p class=\"body\">The view part of a Razor Page uses the same syntax and has the same features as the views used with controllers. Razor Pages can use the full range of expressions and features such as sessions, temp data, and layouts. Aside from the use of the <code class=\"fm-code-in-text\">@page<\/code> directive and the page model classes, the only differences are a certain amount of duplication to configure features such as layouts and partial views, as described in the sections that follow.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-441\">23.5.1 Creating a layout for Razor Pages<\/h3>\n<p class=\"body\">Layouts for Razor Pages are created in the same way as for controller views but in the <code class=\"fm-code-in-text\">Pages\/Shared<\/code> folder. If you are using Visual Studio, create the <code class=\"fm-code-in-text\">Pages\/Shared<\/code> folder and add to it a file named <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> using the Razor Layout template with the contents shown in listing 23.19. If you are using Visual Studio Code, create the <code class=\"fm-code-in-text\">Pages\/Shared<\/code> folder, create the _<code class=\"fm-code-in-text\">Layout.cshtml<\/code> file in the new folder, and add the content shown in listing 23.19.<a id=\"calibre_link-2273\"><\/a><a id=\"calibre_link-1102\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Layouts can be created in the same folder as the Razor Pages that use them, in which case they will be used in preference to the files in the <code class=\"fm-code-in-text1\">Shared<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.19 The contents of the _Layout.cshtml file in the Pages\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;h5 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;\n        Razor Page\n    &lt;\/h5&gt;\n    @RenderBody()\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The layout doesn\u2019t use any features that are specific to Razor Pages and contains the same elements and expressions used in chapter 22 when I created a layout for the controller views.<\/p>\n<p class=\"body\">Next, use the Razor View Start template to add a file named <code class=\"fm-code-in-text\">_ViewStart.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder. Visual Studio will create the file with the content shown in listing 23.20. If you are using Visual Studio Code, create the <code class=\"fm-code-in-text\">_ViewStart.cshtml<\/code> file and add the content shown in listing 23.20.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.20 The contents of the _ViewStart.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = \"_Layout\";\n}<\/pre>\n<p class=\"body\">The C# classes generated from Razor Pages are derived from the <code class=\"fm-code-in-text\">Page<\/code> class, which provides the <code class=\"fm-code-in-text\">Layout<\/code> property used by the view start file, which has the same purpose as the one used by controller views. In listing 23.21, I have updated the <code class=\"fm-code-in-text\">Index<\/code> page to remove the elements that will be provided by the layout.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.21 Removing elements in the Index.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"{id:long?}\"\n@model IndexModel\n\n&lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n    @Model.Product?.Name\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">Using a view start file applies the layout to all pages that don\u2019t override the value assigned to the <code class=\"fm-code-in-text\">Layout<\/code> property. In listing 23.22, I have added a code block to the <code class=\"fm-code-in-text\">Editor<\/code> page so that it doesn\u2019t use a layout.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.22 Disabling layouts in the Editor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"{id:long}\"\n@model EditorModel\n<b class=\"fm-bold\">@{<\/b> \n    <b class=\"fm-bold\">Layout = null;<\/b>\n<b class=\"fm-bold\">}<\/b>\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n\n    <i class=\"fm-italics\">&lt;! ...elements omitted for brevity ... \/&gt;<\/i>\n        \n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/index, and you will see the effect of the new layout, which is shown on the left of figure 23.13. Use the browser to request http:\/\/localhost:5000\/editor\/1, and you will receive content that is generated without the layout, as shown on the right of figure 23.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre235\" src=\"\/images\/proaspnetcore7\/000241.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.13 Using a layout in Razor Pages<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-442\">23.5.2 Using partial views in Razor Pages<\/h3>\n<p class=\"body\">Razor Pages can use partial views so that common content isn\u2019t duplicated. The example in this section relies on the tag helpers feature, which I describe in detail in chapter 25. For this chapter, add the directive shown in listing 23.23 to the view imports file, which enables the custom HTML element used to apply partial views.<a id=\"calibre_link-2274\"><\/a><a id=\"calibre_link-1108\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.23 Enabling tag helpers in the _ViewImports.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@namespace WebApp.Pages\n@using WebApp.Models\n<b class=\"fm-bold\">@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers<\/b><\/pre>\n<p class=\"body\">Next, add a Razor view named <code class=\"fm-code-in-text\">_ProductPartial.cshtml<\/code> in the <code class=\"fm-code-in-text\">Pages\/Shared<\/code> folder and add the content shown in listing 23.24.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.24 The _ProductPartial.cshtml File in the Pages\/Shared Folder<\/p>\n<pre class=\"programlisting\">@model Product\n\n&lt;div class=\"m-2\"&gt;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;tbody&gt;\n            &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;tr&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;@Model?.Price&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">Notice there is nothing specific to Razor Pages in the partial view. Partial views use the <code class=\"fm-code-in-text\">@model<\/code> directive to receive a view model object and do not use the <code class=\"fm-code-in-text\">@page<\/code> directive or have page models, both of which are specific to Razor Pages. This allows Razor Pages to share partial views with MVC controllers, as described in the sidebar.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding the partial method search path<\/p>\n<p class=\"fm-sidebar-text\">The Razor view engine starts looking for a partial view in the same folder as the Razor Page that uses it. If there is no matching file, then the search continues in each parent directory until the <code class=\"fm-code-in-text1\">Pages<\/code> folder is reached. For a partial view used by a Razor Page defined in the <code class=\"fm-code-in-text1\">Pages\/App\/Data<\/code> folder, for example, the view engine looks in the <code class=\"fm-code-in-text1\">Pages\/App\/Data<\/code> folder, the <code class=\"fm-code-in-text1\">Page\/App<\/code> folder, and then the <code class=\"fm-code-in-text1\">Pages<\/code> folder. If no file is found, the search continues to the <code class=\"fm-code-in-text1\">Pages\/Shared<\/code> folder and, finally, to the <code class=\"fm-code-in-text1\">Views\/Shared<\/code> folder.<\/p>\n<p class=\"fm-sidebar-text\">The last search location allows partial views defined for use with controllers to be used by Razor Pages, which is a useful feature for avoiding duplicate content in applications where MVC controllers and Razor Pages are both used.<a id=\"calibre_link-2275\"><\/a><a id=\"calibre_link-2276\"><\/a><\/p>\n<\/div>\n<p class=\"body\">Partial views are applied using the <code class=\"fm-code-in-text\">partial<\/code> element, as shown in listing 23.25, with the <code class=\"fm-code-in-text\">name<\/code> attribute specifying the name of the view and the <code class=\"fm-code-in-text\">model<\/code> attribute providing the view model.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Partial views receive a view model through their <code class=\"fm-code-in-text1\">@model<\/code> directive and not a page model. It is for this reason that the value of the model attribute is <code class=\"fm-code-in-text1\">Model.Product<\/code> and not just <code class=\"fm-code-in-text1\">Model<\/code>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.25 Using a partial view in the Index.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"{id:long?}\"\n@model IndexModel\n\n&lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;\n    @Model.Product?.Name\n&lt;\/div&gt;\n<b class=\"fm-bold\">&lt;partial name=\"_ProductPartial\" model=\"Model.Product\" \/&gt;<\/b><\/pre>\n<p class=\"body\">When the Razor Page is used to handle a response, the contents of the partial view are incorporated into the response. Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/index, and the response includes the table defined in the partial view, as shown in figure 23.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre236\" src=\"\/images\/proaspnetcore7\/000242.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.14 Using a partial view<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-443\">23.5.3 Creating Razor Pages without page models<\/h3>\n<p class=\"body\">If a Razor Page is simply presenting data to the user, the result can be a page model class that simply declares a constructor dependency to set a property that is consumed in the view. To understand this pattern, add a Razor Page named <code class=\"fm-code-in-text\">Data.cshtml<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Pages<\/code> folder with the content shown in listing 23.26.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.26 The contents of the Data.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n@model DataPageModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n\n&lt;h5 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Categories&lt;\/h5&gt;\n&lt;ul class=\"list-group m-2\"&gt;\n    @foreach (Category c in Model.Categories) {\n        &lt;li class=\"list-group-item\"&gt;@c.Name&lt;\/li&gt;                \n    }\n&lt;\/ul&gt;\n\n@functions {\n\n    public class DataPageModel : PageModel {\n        private DataContext context;\n                \n        public IEnumerable&lt;Category&gt; Categories { get; set; }\n            = Enumerable.Empty&lt;Category&gt;();\n                        \n        public DataPageModel(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public void OnGet() {\n            Categories = context.Categories;\n        }\n    }\n}<\/pre>\n<p class=\"body\">The page model in this example doesn\u2019t transform data, perform calculations, or do anything other than giving the view access to the data through dependency injection. To avoid this pattern, where a page model class is used only to access a service, the <code class=\"fm-code-in-text\">@inject<\/code> directive can be used to obtain the service in the view, without the need for a page model, as shown in listing 23.27.<a id=\"calibre_link-2277\"><\/a><a id=\"calibre_link-2278\"><\/a><a id=\"calibre_link-2279\"><\/a><a id=\"calibre_link-1099\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> The <code class=\"fm-code-in-text1\">@inject<\/code> directive should be used sparingly and only when the page model class adds no value other than to provide access to services. In all other situations, using a page model class is easier to manage and maintain.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 23.27 Accessing a service in the Data.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n<b class=\"fm-bold\">@inject DataContext context;<\/b>\n\n&lt;h5 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Categories&lt;\/h5&gt;\n&lt;ul class=\"list-group m-2\"&gt;\n    <b class=\"fm-bold\">@foreach (Category c in context.Categories) {<\/b>\n        &lt;li class=\"list-group-item\"&gt;@c.Name&lt;\/li&gt;                \n    }\n&lt;\/ul&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@inject<\/code> expression specifies the service type and the name by which the service is accessed. In this example, the service type is <code class=\"fm-code-in-text\">DataContext<\/code>, and the name by which it is accessed is <code class=\"fm-code-in-text\">context<\/code>. Within the view, the <code class=\"fm-code-in-text\">@foreach<\/code> expression generates elements for each object returned by the <code class=\"fm-code-in-text\">DataContext.Categories<\/code> properties. Since there is no page model in this example, I have removed the <code class=\"fm-code-in-text\">@model<\/code> and <code class=\"fm-code-in-text\">@using<\/code> directives. Restart ASP.NET Core and use a browser to navigate to http:\/\/localhost:5000\/data, and you will see the response shown in figure 23.15.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre237\" src=\"\/images\/proaspnetcore7\/000243.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 23.15 Using a Razor Page without a page model<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-444\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Razor Pages combines markup and code to generate HTML responses without the setup required by the MVC Framework.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Razor Pages use the same syntax as regular Razor views, with additional expressions to define the page model.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The page model is usually embedded within the markup using the <code class=\"fm-code-in-text\">@functions<\/code> expression but can be defined in a separate C# class file.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The routes supported by Razor Pages are defined using the <code class=\"fm-code-in-text\">@page<\/code> expression.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Razor Pages can use regular Razor features, such as layouts, view start files, and partial views.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-445\">\n<div class=\"calibre1\" id=\"calibre_link-2280\">\n<h1 class=\"tochead\" id=\"calibre_link-2281\">24 Using view components<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2282\"><\/a><a id=\"calibre_link-2283\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using view components to generate content that is orthogonal to the main purpose of the application<\/li>\n<li class=\"co-summary-bullet\">Applying view components in views<\/li>\n<li class=\"co-summary-bullet\">Passing data to view components from the parent view<\/li>\n<li class=\"co-summary-bullet\">Using partial views to generate HTML content<\/li>\n<li class=\"co-summary-bullet\">Creating classes that are controllers and view components<\/li>\n<\/ul>\n<p class=\"body\">I describe <i class=\"fm-italics\">view components<\/i> in this chapter, which are classes that provide action-style logic to support partial views; this means view components provide complex content to be embedded in views while allowing the C# code that supports it to be easily maintained. Table 24.1 puts view components in context.<\/p>\n<p class=\"fm-table-caption\">Table 24.1 Putting view components in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2284\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">View components are classes that provide application logic to support partial views or to inject small fragments of HTML or JSON data into a parent view.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Without view components, it is hard to create embedded functionality such as shopping baskets or login panels in a way that is easy to maintain.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">View components are typically derived from the <code class=\"fm-code-in-text1\">ViewComponent<\/code> class and are applied in a parent view using the custom <code class=\"fm-code-in-text1\">vc<\/code> HTML element or the <code class=\"fm-code-in-text1\">@await<\/code> <code class=\"fm-code-in-text1\">Component.InvokeAsync<\/code> expression.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">View components are a simple and predictable feature. The main pitfall is not using them and trying to include application logic within views where it is difficult to test and maintain.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">You could put the data access and processing logic directly in a partial view, but the result is difficult to work with and hard to maintain.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 24.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 24.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2285\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a reusable unit of code and content<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define a view component.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">7&ndash;13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a response from a view component<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use one of the <code class=\"fm-code-in-text1\">IViewComponentResult<\/code> implementation classes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14&ndash;18<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Getting context data<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the properties inherited from the base class or use the parameters of the <code class=\"fm-code-in-text1\">Invoke<\/code> or <code class=\"fm-code-in-text1\">InvokeAsync<\/code> method.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">19&ndash;25<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Generating view component responses asynchronously<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Override the <code class=\"fm-code-in-text1\">InvokeAsync<\/code> method.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">26&ndash;28<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Integrating a view component into another endpoint<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create a hybrid controller or Razor Page.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">29&ndash;36<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-446\">24.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the WebApp project from chapter 23. To prepare for this chapter, add a class file named <code class=\"fm-code-in-text\">City.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Models<\/code> folder with the content shown in listing 24.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from<a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.1 The contents of the City.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n\n    public class City {\n        public string? Name { get; set; }\n        public string? Country { get; set; }\n        public int? Population { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Add a class named <code class=\"fm-code-in-text\">CitiesData.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Models<\/code> folder with the content shown in listing 24.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.2 The contents of the CitiesData.cs file in the WebApp\/Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n\n    public class CitiesData {\n        \n        private List&lt;City&gt; cities = new List&lt;City&gt; {\n            new City { \n                Name = \"London\", \n                Country = \"UK\", \n                Population = 8539000\n            },\n            new City { \n                Name = \"New York\", \n                Country = \"USA\", \n                Population = 8406000 \n            },\n            new City { \n                Name = \"San Jose\", \n                Country = \"USA\", \n                Population = 998537 \n            },\n            new City { \n                Name = \"Paris\", \n                Country = \"France\", \n                Population = 2244000 \n            }\n        };\n                \n        public IEnumerable&lt;City&gt; Cities =&gt; cities;\n                \n        public void AddCity(City newCity) {\n            cities.Add(newCity);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">CitiesData<\/code> class provides access to a collection of <code class=\"fm-code-in-text\">City<\/code> objects and provides an <code class=\"fm-code-in-text\">AddCity<\/code> method that adds a new object to the collection. Add the statement shown in listing 24.3 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file to create a service for the <code class=\"fm-code-in-text\">CitiesData<\/code> class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.3 Defining a service in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\n\nbuilder.Services.AddDistributedMemoryCache();\nbuilder.Services.AddSession(options =&gt; {\n    options.Cookie.IsEssential = true;\n});\n\nbuilder.Services.Configure&lt;RazorPagesOptions&gt;(opts =&gt; {\n    opts.Conventions.AddPageRoute(\"\/Index\", \"\/extra\/page\/{id:long?}\");\n});\n\n<b class=\"fm-bold\">builder.Services.AddSingleton&lt;CitiesData&gt;();<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.UseSession();\napp.MapControllers();\napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The new statement uses the <code class=\"fm-code-in-text\">AddSingleton<\/code> method to create a <code class=\"fm-code-in-text\">CitiesData<\/code> service. There is no interface\/implementation separation in this service, which I have created to easily distribute a shared <code class=\"fm-code-in-text\">CitiesData<\/code> object. Add a Razor Page named <code class=\"fm-code-in-text\">Cities.cshtml<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Pages<\/code> folder and add the content shown in listing 24.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.4 The contents of the Cities.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n@inject CitiesData Data\n\n&lt;div class=\"m-2\"&gt;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;tbody&gt;\n            @foreach (City c in Data.Cities) {\n                &lt;tr&gt;\n                    &lt;td&gt;@c.Name&lt;\/td&gt;\n                    &lt;td&gt;@c.Country&lt;\/td&gt;\n                    &lt;td&gt;@c.Population&lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n&lt;\/div&gt;<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-447\">24.1.1 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 24.5 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.5 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-448\">24.1.2 Running the example application<\/h3>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 24.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.6 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">The database will be seeded as part of the application startup. Once ASP.NET Core is running, use a web browser to request http:\/\/localhost:5000\/cities, which will produce the response shown in figure 24.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre238\" src=\"\/images\/proaspnetcore7\/000244.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-449\">24.2 Understanding view components<\/h2>\n<p class=\"body\">Applications commonly need to embed content in views that isn\u2019t related to the main purpose of the application. Common examples include site navigation tools and authentication panels that let the user log in without visiting a separate page.<a id=\"calibre_link-2286\"><\/a><\/p>\n<p class=\"body\">The data for this type of feature isn\u2019t part of the model data passed from the action method or page model to the view. It is for this reason that I have created two sources of data in the example project: I am going to display some content generated using <code class=\"fm-code-in-text\">City<\/code> data, which isn\u2019t easily done in a view that receives data from the Entity Framework Core repository and the <code class=\"fm-code-in-text\">Product<\/code>, <code class=\"fm-code-in-text\">Category<\/code>, and <code class=\"fm-code-in-text\">Supplier<\/code> objects it contains.<\/p>\n<p class=\"body\">Partial views are used to create reusable markup that is required in views, avoiding the need to duplicate the same content in multiple places in the application. Partial views are a useful feature, but they just contain fragments of HTML and Razor directives, and the data they operate on is received from the parent view. If you need to display different data, then you run into a problem. You could access the data you need directly from the partial view, but this breaks the development model and produces an application that is difficult to understand and maintain. Alternatively, you could extend the view models used by the application so that it includes the data you require, but this means you have to change every action method, which makes it hard to isolate the functionality of action methods for effective maintenance and testing.<\/p>\n<p class=\"body\">This is where view components come in. A view component is a C# class that provides a partial view with the data that it needs, independently from the action method or Razor Page. In this regard, a view component can be thought of as a specialized action or page, but one that is used only to provide a partial view with data; it cannot receive HTTP requests, and the content that it provides will always be included in the parent view.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-450\">24.3 Creating and using a view component<\/h2>\n<p class=\"body\">A view component is any class whose name ends with <code class=\"fm-code-in-text\">ViewComponent<\/code> and that defines an <code class=\"fm-code-in-text\">Invoke<\/code> or <code class=\"fm-code-in-text\">InvokeAsync<\/code> method or any class that is derived from the <code class=\"fm-code-in-text\">ViewComponent<\/code> base class or that has been decorated with the <code class=\"fm-code-in-text\">ViewComponent<\/code> attribute. I demonstrate the use of the attribute in the \u201cGetting Context Data\u201d section, but the other examples in this chapter rely on the base class.<\/p>\n<p class=\"body\">View components can be defined anywhere in a project, but the convention is to group them in a folder named <code class=\"fm-code-in-text\">Components<\/code>. Create the <code class=\"fm-code-in-text\">WebApp\/Components<\/code> folder and add to it a class file named <code class=\"fm-code-in-text\">CitySummary.cs<\/code> with the content shown in listing 24.7.<a id=\"calibre_link-2287\"><\/a><a id=\"calibre_link-2288\"><\/a><a id=\"calibre_link-2289\"><\/a><a id=\"calibre_link-1202\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.7 The contents of the CitySummary.cs file in the Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Components {\n\n    public class CitySummary : ViewComponent {\n        private CitiesData data;\n                \n        public CitySummary(CitiesData cdata) {\n            data = cdata;\n        }\n                \n        public string Invoke() {\n            return $\"{data.Cities.Count()} cities, \"\n            + $\"{data.Cities.Sum(c =&gt; c.Population)} people\";\n        }\n    }\n}<\/pre>\n<p class=\"body\">View components can take advantage of dependency injection to receive the services they require. In this example, the view component declares a dependency on the <code class=\"fm-code-in-text\">CitiesData<\/code> class, which is then used in the <code class=\"fm-code-in-text\">Invoke<\/code> method to create a <code class=\"fm-code-in-text\">string<\/code> that contains the number of cities and the population total.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-451\">24.3.1 Applying a view component<\/h3>\n<p class=\"body\">View components can be applied in two different ways. The first technique is to use the <code class=\"fm-code-in-text\">Component<\/code> property that is added to the C# classes generated from views and Razor Pages. This property returns an object that implements the <code class=\"fm-code-in-text\">IViewComponentHelper<\/code> interface, which provides the <code class=\"fm-code-in-text\">InvokeAsync<\/code> method. Listing 24.8 uses this technique to apply the view component in the <code class=\"fm-code-in-text\">Index.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder.<a id=\"calibre_link-2290\"><\/a><a id=\"calibre_link-1203\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.8 Using a view component in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{\n    Layout = \"_Layout\";\n    ViewBag.Title = \"Product Table\";\n}\n\n@section Header {\n    Product Information\n}\n\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n&lt;tr&gt;\n    &lt;th&gt;Price&lt;\/th&gt;\n    &lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;\n&lt;\/tr&gt;\n&lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n\n@section Footer {\n    @(((Model?.Price \/ ViewBag.AveragePrice)\n            * 100).ToString(\"F2\"))% of average price\n}\n\n@section Summary {\n    &lt;div class=\"bg-info text-white m-2 p-2\"&gt;\n        <b class=\"fm-bold\">@await Component.InvokeAsync(\"CitySummary\")<\/b>\n    &lt;\/div&gt;\n}<\/pre>\n<p class=\"body\">View components are applied using the <code class=\"fm-code-in-text\">Component.InvokeAsync<\/code> method, using the name of the view component class as the argument. The syntax for this technique can be confusing. View component classes define either an <code class=\"fm-code-in-text\">Invoke<\/code> or <code class=\"fm-code-in-text\">InvokeAsync<\/code> method, depending on whether their work is performed synchronously or asynchronously. But the <code class=\"fm-code-in-text\">Component.InvokeAsync<\/code> method is always used, even to apply view components that define the <code class=\"fm-code-in-text\">Invoke<\/code> method and whose operations are entirely synchronous.<\/p>\n<p class=\"body\">To add the namespace for the view components to the list that are included in views, I added the statement shown in listing 24.9 to the <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.9 Adding a namespace in the _ViewImports.cshtml file in the Views folder<\/p>\n<pre class=\"programlisting\">@using WebApp.Models\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n<b class=\"fm-bold\">@using WebApp.Components<\/b><\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1, which will produce the result shown in figure 24.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre239\" src=\"\/images\/proaspnetcore7\/000245.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.2 Using a view component<\/p>\n<\/div>\n<p class=\"fm-head2\">Applying view components using a tag helper<\/p>\n<p class=\"body\">Razor Views and Pages can contain tag helpers, which are custom HTML elements that are managed by C# classes. I explain how tag helpers work in detail in chapter 25, but view components can be applied using an HTML element that is implemented as a tag helper. To enable this feature, add the directive shown in listing 24.10 to the <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views<\/code> folder.<a id=\"calibre_link-2291\"><\/a><a id=\"calibre_link-2292\"><\/a><a id=\"calibre_link-2293\"><\/a><a id=\"calibre_link-2294\"><\/a><a id=\"calibre_link-1194\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> View components can be used only in controller views or Razor Pages and cannot be used to handle requests directly.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.10 Adding tag helper in the _ViewImports.cshtml file in the Views folder<\/p>\n<pre class=\"programlisting\">@using WebApp.Models\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n@using WebApp.Components\n<b class=\"fm-bold\">@addTagHelper *, WebApp<\/b><\/pre>\n<p class=\"body\">The new directive adds tag helper support for the example project, which is specified by name, and which is <code class=\"fm-code-in-text\">WebApp<\/code> for this example. In listing 24.11, I have used the custom HTML element to apply the view component.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.11 Applying a component in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{\n    Layout = \"_Layout\";\n    ViewBag.Title = \"Product Table\";\n}\n\n@section Header {\n    Product Information\n}\n\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n&lt;tr&gt;\n    &lt;th&gt;Price&lt;\/th&gt;\n    &lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;\n&lt;\/tr&gt;\n&lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n\n@section Footer {\n    @(((Model?.Price \/ ViewBag.AveragePrice)\n            * 100).ToString(\"F2\"))% of average price\n}\n\n@section Summary {\n    &lt;div class=\"bg-info text-white m-2 p-2\"&gt;\n        <b class=\"fm-bold\">&lt;vc:city-summary \/&gt;<\/b>\n    &lt;\/div&gt;\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-1113\"><\/a>The tag for the custom element is <code class=\"fm-code-in-text\">vc<\/code>, followed by a colon, followed by the name of the view component class, which is transformed into kebab-case. Each capitalized word in the class name is converted to lowercase and separated by a hyphen so that <code class=\"fm-code-in-text\">CitySummary<\/code> becomes <code class=\"fm-code-in-text\">city-summary<\/code>, and the <code class=\"fm-code-in-text\">CitySummary<\/code> view component is applied using the <code class=\"fm-code-in-text\">vc:city-summary<\/code> element.<\/p>\n<p class=\"fm-head2\">Applying view components in razor pages<\/p>\n<p class=\"body\">Razor Pages use view components in the same way, either through the <code class=\"fm-code-in-text\">Component<\/code> property or through the custom HTML element. Since Razor Pages have their own view imports file, a separate <code class=\"fm-code-in-text\">@addTagHelper<\/code> directive is required, as shown in listing 24.12.<a id=\"calibre_link-2295\"><\/a><a id=\"calibre_link-2296\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.12 Adding a directive in the _ViewImports.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@namespace WebApp.Pages\n@using WebApp.Models\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n<b class=\"fm-bold\">@addTagHelper *, WebApp<\/b><\/pre>\n<p class=\"body\">Listing 24.13 applies the <code class=\"fm-code-in-text\">CitySummary<\/code> view component to the <code class=\"fm-code-in-text\">Data<\/code> page.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.13 Using a view component in the Data.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n@inject DataContext context;\n\n&lt;h5 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Categories&lt;\/h5&gt;\n&lt;ul class=\"list-group m-2\"&gt;\n    @foreach (Category c in context.Categories) {\n        &lt;li class=\"list-group-item\"&gt;@c.Name&lt;\/li&gt;                \n    }\n&lt;\/ul&gt;\n\n<b class=\"fm-bold\">&lt;div class=\"bg-info text-white m-2 p-2\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;vc:city-summary \/&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b><\/pre>\n<p class=\"body\"><a id=\"calibre_link-1208\"><\/a>Use a browser to request http:\/\/localhost:5000\/data, and you will see the response shown in figure 24.3, which displays the city data alongside the categories in the database.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre240\" src=\"\/images\/proaspnetcore7\/000246.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.3 Using a view component in a Razor Page<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-452\">24.4 Understanding view component results<\/h2>\n<p class=\"body\">The ability to insert simple string values into a view or page isn\u2019t especially useful, but fortunately, view components are capable of much more. More complex effects can be achieved by having the <code class=\"fm-code-in-text\">Invoke<\/code> or <code class=\"fm-code-in-text\">InvokeAsync<\/code> method return an object that implements the <code class=\"fm-code-in-text\">IViewComponentResult<\/code> interface. There are three built-in classes that implement the <code class=\"fm-code-in-text\">IViewComponentResult<\/code> interface, and they are described in table 24.3, along with the convenience methods for creating them provided by the <code class=\"fm-code-in-text\">ViewComponent<\/code> base class. I describe the use of each result type in the sections that follow.<a id=\"calibre_link-2297\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 24.3 The built-in IViewComponentResult implementation classes<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2298\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ViewViewComponentResult<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This class is used to specify a Razor View, with optional view model data. Instances of this class are created using the <code class=\"fm-code-in-text1\">View<\/code> method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ContentViewComponentResult<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This class is used to specify a text result that will be safely encoded for inclusion in an HTML document. Instances of this class are created using the <code class=\"fm-code-in-text1\">Content<\/code> method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HtmlContentViewComponentResult<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This class is used to specify a fragment of HTML that will be included in the HTML document without further encoding. There is no <code class=\"fm-code-in-text1\">ViewComponent<\/code> method to create this type of result.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">There is special handling for two result types. If a view component returns a <code class=\"fm-code-in-text\">string<\/code>, then it is used to create a <code class=\"fm-code-in-text\">ContentViewComponentResult<\/code> object, which is what I relied on in earlier examples. If a view component returns an <code class=\"fm-code-in-text\">IHtmlContent<\/code> object, then it is used to create an <code class=\"fm-code-in-text\">HtmlContentViewComponentResult<\/code> object.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-453\">24.4.1 Returning a partial view<\/h3>\n<p class=\"body\">The most useful response is the awkwardly named <code class=\"fm-code-in-text\">ViewViewComponentResult<\/code> object, which tells Razor to render a partial view and include the result in the parent view. The <code class=\"fm-code-in-text\">ViewComponent<\/code> base class provides the <code class=\"fm-code-in-text\">View<\/code> method for creating <code class=\"fm-code-in-text\">ViewViewComponentResult<\/code> objects, and four versions of the method are available, described in table 24.4.<a id=\"calibre_link-2299\"><\/a><a id=\"calibre_link-2300\"><\/a><a id=\"calibre_link-2301\"><\/a><a id=\"calibre_link-1210\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 24.4 The ViewComponent.View methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2302\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">View()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using this method selects the default view for the view component and does not provide a view model.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">View(model)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using the method selects the default view and uses the specified object as the view model.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">View(viewName)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using this method selects the specified view and does not provide a view model.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">View(viewName, model)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using this method selects the specified view and uses the specified object as the view model.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">These methods correspond to those provided by the <code class=\"fm-code-in-text\">Controller<\/code> base class and are used in much the same way. To create a view model class that the view component can use, add a class file named <code class=\"fm-code-in-text\">CityViewModel.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Models<\/code> folder and use it to define the class shown in listing 24.14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.14 The contents of the CityViewModel.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n\n    public class CityViewModel {\n        public int? Cities { get; set; }\n        public int? Population { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Listing 24.15 modifies the <code class=\"fm-code-in-text\">Invoke<\/code> method of the <code class=\"fm-code-in-text\">CitySummary<\/code> view component so it uses the <code class=\"fm-code-in-text\">View<\/code> method to select a partial view and provides view data using a <code class=\"fm-code-in-text\">CityViewModel<\/code> object.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.15 Selecting a view in the CitySummary.cs file in the Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Components {\n\n    public class CitySummary : ViewComponent {\n        private CitiesData data;\n                \n        public CitySummary(CitiesData cdata) {\n            data = cdata;\n        }\n                \n        <b class=\"fm-bold\">public IViewComponentResult Invoke() {<\/b>\n            <b class=\"fm-bold\">return View(new CityViewModel {<\/b>\n                <b class=\"fm-bold\">Cities = data.Cities.Count(),<\/b>\n                <b class=\"fm-bold\">Population = data.Cities.Sum(c =&gt; c.Population)<\/b>\n            <b class=\"fm-bold\">});<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">There is no view available for the view component currently, but the error message this produces reveals the locations that are searched. Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1 to see the locations that are searched when the view component is used with a controller. Request http:\/\/localhost:5000\/data to see the locations searched when a view component is used with a Razor Page. Figure 24.4 shows both responses.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre241\" src=\"\/images\/proaspnetcore7\/000247.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.4 The search locations for view component views<\/p>\n<\/div>\n<p class=\"body\">Razor searches for a view named <code class=\"fm-code-in-text\">Default.cshtml<\/code> when a view component invokes the <code class=\"fm-code-in-text\">View<\/code> method without specifying a name. If the view component is used with a controller, then the search locations are as follows:<a id=\"calibre_link-2303\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">\/Views\/[controller]\/Components\/[viewcomponent]\/Default.cshtml<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">\/Views\/Shared\/Components\/[viewcomponent]\/Default.cshtml<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">\/Pages\/Shared\/Components\/[viewcomponent]\/Default.cshtml<\/code><\/p>\n<\/li>\n<\/ul>\n<p class=\"body\">When the <code class=\"fm-code-in-text\">CitySummary<\/code> component is rendered by a view selected through the <code class=\"fm-code-in-text\">Home<\/code> controller, for example, <code class=\"fm-code-in-text\">[controller]<\/code> is <code class=\"fm-code-in-text\">Home<\/code> and <code class=\"fm-code-in-text\">[viewcomponent]<\/code> is <code class=\"fm-code-in-text\">CitySummary<\/code>, which means the first search location is <code class=\"fm-code-in-text\">\/Views\/Home\/Components\/CitySummary\/Default.cshtml<\/code>. If the view component is used with a Razor Page, then the search locations are as follows:<\/p>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">\/Pages\/Components\/[viewcomponent]\/Default.cshtml<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">\/Pages\/Shared\/Components\/[viewcomponent]\/Default.cshtml<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">\/Views\/Shared\/Components\/[viewcomponent]\/Default.cshtml<\/code><\/p>\n<\/li>\n<\/ul>\n<p class=\"body\">If the search paths for Razor Pages do not include the page name but a Razor Page is defined in a subfolder, then the Razor view engine will look for a view in the <code class=\"fm-code-in-text\">Components\/[viewcomponent]<\/code> folder, relative to the location in which the Razor Page is defined, working its way up the folder hierarchy until it finds a view or reaches the <code class=\"fm-code-in-text\">Pages<\/code> folder.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Notice that view components used in Razor Pages will find views defined in the <code class=\"fm-code-in-text1\">Views\/Shared\/Components<\/code> folder and that view components defined in controllers will find views in the <code class=\"fm-code-in-text1\">Pages\/Shared\/Components<\/code> folder. This means you don\u2019t have to duplicate views when a view component is used by controllers and Razor Pages.<\/p>\n<p class=\"body\"><a id=\"calibre_link-1211\"><\/a>Create the <code class=\"fm-code-in-text\">WebApp\/Views\/Shared\/Components\/CitySummary<\/code> folder and add to it a Razor View named <code class=\"fm-code-in-text\">Default.cshtml<\/code> with the content shown in listing 24.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.16 The Default.cshtml file in the Views\/Shared\/Components\/CitySummary folder<\/p>\n<pre class=\"programlisting\">@model CityViewModel\n\n&lt;table class=\"table table-sm table-bordered text-white bg-secondary\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;&lt;th colspan=\"2\"&gt;Cities Summary&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        &lt;tr&gt;\n            &lt;td&gt;Cities:&lt;\/td&gt;\n            &lt;td class=\"text-right\"&gt;\n                @Model?.Cities\n            &lt;\/td&gt;\n        &lt;\/tr&gt;\n        &lt;tr&gt;\n            &lt;td&gt;Population:&lt;\/td&gt;\n            &lt;td class=\"text-right\"&gt;\n                @Model?.Population?.ToString(\"#,###\")\n            &lt;\/td&gt;\n        &lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">Views for view components are similar to partial views and use the <code class=\"fm-code-in-text\">@model<\/code> directive to set the type of the view model object. This view receives a <code class=\"fm-code-in-text\">CityViewModel<\/code> object from its view component, which is used to populate the cells in an HTML table. Restart ASP.NET Core and a browser to request http:\/\/localhost:5000\/home\/index\/1 and http:\/\/localhost:5000\/data, and you will see the view incorporated into the responses, as shown in figure 24.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre242\" src=\"\/images\/proaspnetcore7\/000248.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.5 Using a view with a view component<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-454\">24.4.2 Returning HTML fragments<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ContentViewComponentResult<\/code> class is used to include fragments of HTML in the parent view without using a view. Instances of the <code class=\"fm-code-in-text\">ContentViewComponentResult<\/code> class are created using the <code class=\"fm-code-in-text\">Content<\/code> method inherited from the <code class=\"fm-code-in-text\">ViewComponent<\/code> base class, which accepts a <code class=\"fm-code-in-text\">string<\/code> value. Listing 24.17 demonstrates the use of the <code class=\"fm-code-in-text\">Content<\/code> method.<a id=\"calibre_link-2304\"><\/a><a id=\"calibre_link-1209\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> In addition to the <code class=\"fm-code-in-text1\">Content<\/code> method, the <code class=\"fm-code-in-text1\">Invoke<\/code> method can return a <code class=\"fm-code-in-text1\">string<\/code>, which will be automatically converted to a <code class=\"fm-code-in-text1\">ContentViewComponentResult<\/code>. This is the approach I took in the view component when it was first defined.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.17 Using the content method in the CitySummary.cs file in the Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Components {\n\n    public class CitySummary : ViewComponent {\n        private CitiesData data;\n                \n        public CitySummary(CitiesData cdata) {\n            data = cdata;\n        }\n                \n        public IViewComponentResult Invoke() {\n            <b class=\"fm-bold\">return Content(\"This is a &lt;h3&gt;&lt;i&gt;string&lt;\/i&gt;&lt;\/h3&gt;\");<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The string received by the <code class=\"fm-code-in-text\">Content<\/code> method is encoded to make it safe to include in an HTML document. This is particularly important when dealing with content that has been provided by users or external systems because it prevents JavaScript content from being embedded into the HTML generated by the application.<\/p>\n<p class=\"body\">In this example, the <code class=\"fm-code-in-text\">string<\/code> that I passed to the <code class=\"fm-code-in-text\">Content<\/code> method contains some basic HTML tags. Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1. The response will include the encoded HTML fragment, as shown in figure 24.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre243\" src=\"\/images\/proaspnetcore7\/000249.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.6 Returning an encoded HTML fragment using a view component<\/p>\n<\/div>\n<p class=\"body\">If you look at the HTML that the view component produced, you will see that the angle brackets have been replaced so that the browser doesn\u2019t interpret the content as HTML elements, as follows:<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"bg-info text-white m-2 p-2\"&gt;\n    This is a <b class=\"fm-bold\">&lt;h3&gt;&lt;i&gt;string&lt;\/i&gt;&lt;\/h3&gt;<\/b>\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">You don\u2019t need to encode content if you trust its source and want it to be interpreted as HTML. The <code class=\"fm-code-in-text\">Content<\/code> method always encodes its argument, so you must create the <code class=\"fm-code-in-text\">HtmlContentViewComponentResult<\/code> object directly and provide its constructor with an <code class=\"fm-code-in-text\">HtmlString<\/code> object, which represents a string that you know is safe to display, either because it comes from a source that you trust or because you are confident that it has already been encoded, as shown in listing 24.18.<a id=\"calibre_link-2305\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.18 Returning a fragment in the CitySummary.cs file in the Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc.ViewComponents;<\/b>\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Html;<\/b>\n\nnamespace WebApp.Components {\n\n    public class CitySummary : ViewComponent {\n        private CitiesData data;\n                \n        public CitySummary(CitiesData cdata) {\n            data = cdata;\n        }\n                \n        public IViewComponentResult Invoke() {\n            <b class=\"fm-bold\">return new HtmlContentViewComponentResult(<\/b>\n                <b class=\"fm-bold\">new HtmlString(\"This is a &lt;h3&gt;&lt;i&gt;string&lt;\/i&gt;&lt;\/h3&gt;\"));<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">This technique should be used with caution and only with sources of content that cannot be tampered with and that perform their own encoding. Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1, and you will see the response isn\u2019t encoded and is interpreted as HTML elements, as shown in figure 24.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre244\" src=\"\/images\/proaspnetcore7\/000250.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.7 Returning an unencoded HTML fragment using a view component<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-455\">24.5 Getting context data<\/h2>\n<p class=\"body\">Details about the current request and the parent view are provided to a view component through properties defined by the <code class=\"fm-code-in-text\">ViewComponent<\/code> base class, as described in table 24.5.<a id=\"calibre_link-2306\"><\/a><a id=\"calibre_link-2307\"><\/a><a id=\"calibre_link-2308\"><\/a><a id=\"calibre_link-1204\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 24.5 The ViewComponentContext properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2309\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpContext<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">HttpContext<\/code> object that describes the current request and the response that is being prepared.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Request<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">HttpRequest<\/code> object that describes the current HTTP request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">User<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">IPrincipal<\/code> object that describes the current user, as described in chapters 37 and 38.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RouteData<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">RouteData<\/code> object that describes the routing data for the current request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ViewBag<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the <code class=\"fm-code-in-text1\">dynamic<\/code> view bag object, which can be used to pass data between the view component and the view, as described in chapter 22.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ModelState<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">ModelStateDictionary<\/code>, which provides details of the model binding process, as described in chapter 29.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ViewData<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">ViewDataDictionary<\/code>, which provides access to the view data provided for the view component.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The context data can be used in whatever way helps the view component do its work, including varying the way that data is selected or rendering different content or views. It is hard to devise a representative example of using context data in a view component because the problems it solves are specific to each project. In listing 24.19, I check the route data for the request to determine whether the routing pattern contains a controller segment variable, which indicates a request that will be handled by a controller and view.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.19 Using request data in the CitySummary.cs file in the Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc.ViewComponents;\nusing Microsoft.AspNetCore.Html;\n\nnamespace WebApp.Components {\n\n    public class CitySummary : ViewComponent {\n        private CitiesData data;\n                \n        public CitySummary(CitiesData cdata) {\n            data = cdata;\n        }\n                \n        <b class=\"fm-bold\">public string Invoke() {<\/b>\n            <b class=\"fm-bold\">if (RouteData.Values[\"controller\"] != null) {<\/b>\n                <b class=\"fm-bold\">return \"Controller Request\";<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return \"Razor Page Request\";<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1 and http:\/\/localhost:5000\/data, and you will see that the view component alters its output, as shown in figure 24.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre245\" src=\"\/images\/proaspnetcore7\/000251.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.8 Using context data in a view component<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-456\">24.5.1 Providing context from the parent view using arguments<\/h3>\n<p class=\"body\">Parent views can provide additional context data to view components, providing them with either data or guidance about the content that should be produced. The context data is received through the <code class=\"fm-code-in-text\">Invoke<\/code> or <code class=\"fm-code-in-text\">InvokeAsync<\/code> method, as shown in listing 24.20.<a id=\"calibre_link-2310\"><\/a><a id=\"calibre_link-2311\"><\/a><a id=\"calibre_link-1205\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.20 Receiving a value in the CitySummary.cs file in the Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc.ViewComponents;\nusing Microsoft.AspNetCore.Html;\n\nnamespace WebApp.Components {\n\n    public class CitySummary : ViewComponent {\n        private CitiesData data;\n                \n        public CitySummary(CitiesData cdata) {\n            data = cdata;\n        }\n                \n        <b class=\"fm-bold\">public IViewComponentResult Invoke(string themeName) {<\/b>\n            <b class=\"fm-bold\">ViewBag.Theme = themeName;<\/b>\n            <b class=\"fm-bold\">return View(new CityViewModel {<\/b>\n                <b class=\"fm-bold\">Cities = data.Cities.Count(),<\/b>\n                <b class=\"fm-bold\">Population = data.Cities.Sum(c =&gt; c.Population)<\/b>\n            <b class=\"fm-bold\">});<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Invoke<\/code> method defines a <code class=\"fm-code-in-text\">themeName<\/code> parameter that is passed on to the partial view using the view bag, which was described in chapter 22. Listing 24.21 updates the <code class=\"fm-code-in-text\">Default<\/code> view to use the received value to style the content it produces.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.21 Styling content in the Default.cshtml file in the Views\/Shared\/Components\/CitySummary folder<\/p>\n<pre class=\"programlisting\">@model CityViewModel\n\n<b class=\"fm-bold\">&lt;table class=\"table table-sm table-bordered text-white bg-@ViewBag.Theme\"&gt;<\/b>\n    &lt;thead&gt;\n        &lt;tr&gt;&lt;th colspan=\"2\"&gt;Cities Summary&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        &lt;tr&gt;\n            &lt;td&gt;Cities:&lt;\/td&gt;\n            &lt;td class=\"text-right\"&gt;\n                @Model?.Cities\n            &lt;\/td&gt;\n        &lt;\/tr&gt;\n        &lt;tr&gt;\n            &lt;td&gt;Population:&lt;\/td&gt;\n            &lt;td class=\"text-right\"&gt;\n                @Model?.Population?.ToString(\"#,###\")\n            &lt;\/td&gt;\n        &lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">A value for all parameters defined by a view component\u2019s <code class=\"fm-code-in-text\">Invoke<\/code> or <code class=\"fm-code-in-text\">InvokeAsync<\/code> method must always be provided. Listing 24.22 provides a value for <code class=\"fm-code-in-text\">themeName<\/code> parameter in the view selected by the <code class=\"fm-code-in-text\">Home<\/code> controller.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The view component will not be used if you do not provide values for all the parameters it defines, but no error message is displayed. If you don\u2019t see any content from a view component, then the likely cause is a missing parameter value.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.22 Supplying a value in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{\n    Layout = \"_Layout\";\n    ViewBag.Title = \"Product Table\";\n}\n\n@section Header {\n    Product Information\n}\n\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n&lt;tr&gt;\n    &lt;th&gt;Price&lt;\/th&gt;\n    &lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;\n&lt;\/tr&gt;\n&lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n@section Footer {\n    @(((Model?.Price \/ ViewBag.AveragePrice)\n            * 100).ToString(\"F2\"))% of average price\n}\n\n@section Summary {\n    &lt;div class=\"bg-info text-white m-2 p-2\"&gt;\n        <b class=\"fm-bold\">&lt;vc:city-summary theme-name=\"secondary\" \/&gt;<\/b>\n    &lt;\/div&gt;\n}<\/pre>\n<p class=\"body\">The name of each parameter is expressed an attribute using kebab-case so that the <code class=\"fm-code-in-text\">theme-name<\/code> attribute provides a value for the <code class=\"fm-code-in-text\">themeName<\/code> parameter. Listing 24.23 sets a value in the <code class=\"fm-code-in-text\">Data.cshtml<\/code> Razor Page.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.23 Supplying a value in the Data.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n@inject DataContext context;\n\n&lt;h5 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Categories&lt;\/h5&gt;\n&lt;ul class=\"list-group m-2\"&gt;\n    @foreach (Category c in context.Categories) {\n        &lt;li class=\"list-group-item\"&gt;@c.Name&lt;\/li&gt;                \n    }\n&lt;\/ul&gt;\n\n&lt;div class=\"bg-info text-white m-2 p-2\"&gt;\n    <b class=\"fm-bold\">&lt;vc:city-summary theme-name=\"danger\" \/&gt;<\/b>\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1 and http:\/\/localhost:5000\/data. The view component is provided with different values for the <code class=\"fm-code-in-text\">themeName<\/code> parameter, producing the responses shown in figure 24.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre246\" src=\"\/images\/proaspnetcore7\/000252.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.9 Using context data in a view component<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Providing values using the component helper<\/p>\n<p class=\"fm-sidebar-text\">If you prefer applying view components using the <code class=\"fm-code-in-text1\">Component.InvokeAsync<\/code> helper, then you can provide context using method arguments, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;div cla\"s=\"bg-info text-white m-2 p-2\"&gt;\n    @await Component.InvokeAsync(\"CitySummary\",\n        <b class=\"fm-bold\">new { themeName= \"danger\" }<\/b>)\n&lt;\/div&gt;\n... <\/pre>\n<p class=\"fm-sidebar-text\">The first argument to the <code class=\"fm-code-in-text1\">InvokeAsync<\/code> method is the name of the view component class. The second argument is an object whose names correspond to the parameters defined by the view component.<\/p>\n<\/div>\n<p class=\"fm-head2\">Using a default parameter value<\/p>\n<p class=\"body\">Default values can be defined for the <code class=\"fm-code-in-text\">Invoke<\/code> method parameters, as shown in listing 24.24, which provides a fallback if the parent view doesn\u2019t provide a value.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.24 A default value in the CitySummary.cs file in the Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Mvc.ViewComponents;\nusing Microsoft.AspNetCore.Html;\n\nnamespace WebApp.Components {\n\n    public class CitySummary : ViewComponent {\n        private CitiesData data;\n                \n        public CitySummary(CitiesData cdata) {\n            data = cdata;\n        }\n                \n        <b class=\"fm-bold\">public IViewComponentResult Invoke(string themeName=\"success\") {<\/b>\n            ViewBag.Theme = themeName;\n            return View(new CityViewModel {\n                Cities = data.Cities.Count(),\n                Population = data.Cities.Sum(c =&gt; c.Population)\n            });\n        }\n    }\n}<\/pre>\n<p class=\"body\">The default value is <code class=\"fm-code-in-text\">success<\/code>, and it will be used if the view component is applied without a <code class=\"fm-code-in-text\">theme-name<\/code> attribute, as shown in Listing 24.25.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.25 Omitting the attribute in the Data.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n@inject DataContext context;\n\n&lt;h5 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Categories&lt;\/h5&gt;\n&lt;ul class=\"list-group m-2\"&gt;\n    @foreach (Category c in context.Categories) {\n        &lt;li class=\"list-group-item\"&gt;@c.Name&lt;\/li&gt;                \n    }\n&lt;\/ul&gt;\n\n&lt;div class=\"bg-info text-white m-2 p-2\"&gt;\n    <b class=\"fm-bold\">&lt;vc:city-summary \/&gt;<\/b>\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/data. The default value is used to select the theme, as shown in figure 24.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre247\" src=\"\/images\/proaspnetcore7\/000253.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.10 Using a default value<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-457\">24.5.2 Creating asynchronous view components<\/h3>\n<p class=\"body\">All the examples so far in this chapter have been synchronous view components, which can be recognized because they define the <code class=\"fm-code-in-text\">Invoke<\/code> method. If your view component relies on asynchronous APIs, then you can create an asynchronous view component by defining an <code class=\"fm-code-in-text\">InvokeAsync<\/code> method that returns a <code class=\"fm-code-in-text\">Task<\/code>. When Razor receives the <code class=\"fm-code-in-text\">Task<\/code> from the <code class=\"fm-code-in-text\">InvokeAsync<\/code> method, it will wait for it to complete and then insert the result into the main view. To create a new component, add a class file named <code class=\"fm-code-in-text\">PageSize.cs<\/code> to the <code class=\"fm-code-in-text\">Components<\/code> folder and use it to define the class shown in listing 24.26.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.26 The contents of the PageSize.cs file in the Components folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Components {\n\n    public class PageSize : ViewComponent {\n        \n        public async Task&lt;IViewComponentResult&gt; InvokeAsync() {\n                \n            HttpClient client = new HttpClient();\n            HttpResponseMessage response\n                = await client.GetAsync(\"http:\/\/microsoft.com\");\n            return View(response.Content.Headers.ContentLength);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">InvokeAsync<\/code> method uses the <code class=\"fm-code-in-text\">async<\/code> and <code class=\"fm-code-in-text\">await<\/code> keywords to consume the asynchronous API provided by the <code class=\"fm-code-in-text\">HttpClient<\/code> class and get the length of the content returned by sending a GET request to <code class=\"fm-code-in-text\">microsoft.com<\/code>. The length is passed to the <code class=\"fm-code-in-text\">View<\/code> method, which selects the default partial view associated with the view component.<\/p>\n<p class=\"body\">Create the <code class=\"fm-code-in-text\">Views\/Shared\/Components\/PageSize<\/code> folder and add to it a Razor View named <code class=\"fm-code-in-text\">Default.cshtml<\/code> with the content shown in listing 24.27.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.27 The Default.cshtml file in the Views\/Shared\/Components\/PageSize folder<\/p>\n<pre class=\"programlisting\">@model long\n&lt;div class=\"m-1 p-1 bg-light text-dark\"&gt;Page size: @Model&lt;\/div&gt;<\/pre>\n<p class=\"body\">The final step is to use the component, which I have done in the <code class=\"fm-code-in-text\">Index<\/code> view used by the <code class=\"fm-code-in-text\">Home<\/code> controller, as shown in listing 24.28. No change is required in the way that asynchronous view components are used.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.28 Using an asynchronous component in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{\n    Layout = \"_Layout\";\n    ViewBag.Title = \"Product Table\";\n}\n\n@section Header {\n    Product Information\n}\n\n&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n&lt;tr&gt;\n    &lt;th&gt;Price&lt;\/th&gt;\n    &lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;\n&lt;\/tr&gt;\n&lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n\n@section Footer {\n    @(((Model?.Price \/ ViewBag.AveragePrice)\n            * 100).ToString(\"F2\"))% of average price\n}\n\n@section Summary {\n    &lt;div class=\"bg-info text-white m-2 p-2\"&gt;\n        &lt;vc:city-summary theme-name=\"secondary\" \/&gt;\n        <b class=\"fm-bold\">&lt;vc:page-size \/&gt;<\/b>\n    &lt;\/div&gt;\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1, which will produce a response that includes the size of the Microsoft home page, as shown in figure 24.11. At the time of writing, the response sent by the Microsoft website is a concise message used for requests that don\u2019t include a browser user-agent header, but you may see a different response.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre248\" src=\"\/images\/proaspnetcore7\/000254.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.11 Using an asynchronous component<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-458\">24.6 Creating view components classes<\/h2>\n<p class=\"body\">View components often provide a summary or snapshot of functionality that is handled in-depth by a controller or Razor Page. For a view component that summarizes a shopping basket, for example, there will often be a link that targets a controller that provides a detailed list of the products in the basket and that can be used to check out and complete the purchase.<a id=\"calibre_link-2312\"><\/a><a id=\"calibre_link-2313\"><\/a><a id=\"calibre_link-2314\"><\/a><a id=\"calibre_link-2315\"><\/a><a id=\"calibre_link-1207\"><\/a><\/p>\n<p class=\"body\">In this situation, you can create a class that is a view component as well as a controller or Razor Page. If you are using Visual Studio, expand the <code class=\"fm-code-in-text\">Cities.cshtml<\/code> item in the Solution Explorer to show the <code class=\"fm-code-in-text\">Cities.cshtml.cs<\/code> file and replace its contents with those shown in listing 24.29. If you are using Visual Studio Code, add a file named <code class=\"fm-code-in-text\">Cities.cshtml.cs<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 24.29.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.29 The contents of the Cities.cshtml.cs file in the Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\nusing Microsoft.AspNetCore.Mvc.ViewComponents;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\nusing WebApp.Models;\n\nnamespace WebApp.Pages {\n\n    [ViewComponent(Name = \"CitiesPageHybrid\")]\n    public class CitiesModel : PageModel {\n        \n        public CitiesModel(CitiesData cdata) {\n            Data = cdata;\n        }\n                \n        public CitiesData? Data { get; set; }\n                \n        [ViewComponentContext]\n        public ViewComponentContext Context { get; set; } = new();\n                \n        public IViewComponentResult Invoke() {\n            return new ViewViewComponentResult() {\n                ViewData = new ViewDataDictionary&lt;CityViewModel&gt;(\n                    Context.ViewData,\n                    new CityViewModel {\n                        Cities = Data?.Cities.Count(),\n                        Population = Data?.Cities.Sum(c =&gt; c.Population)\n                    })\n            };\n        }\n    }\n}<\/pre>\n<p class=\"body\">This page model class is decorated with the <code class=\"fm-code-in-text\">ViewComponent<\/code> attribute, which allows it to be used as a view component. The <code class=\"fm-code-in-text\">Name<\/code> argument specifies the name by which the view component will be applied. Since a page model cannot inherit from the <code class=\"fm-code-in-text\">ViewComponent<\/code> base class, a property whose type is <code class=\"fm-code-in-text\">ViewComponentContext<\/code> is decorated with the <code class=\"fm-code-in-text\">ViewComponentContext<\/code> attribute, which signals that it should be assigned an object that defines the properties described in table 24.5 before the <code class=\"fm-code-in-text\">Invoke<\/code> or <code class=\"fm-code-in-text\">InvokeAsync<\/code> method is invoked. The <code class=\"fm-code-in-text\">View<\/code> method isn\u2019t available, so I have to create a <code class=\"fm-code-in-text\">ViewViewComponentResult<\/code> object, which relies on the context object received through the decorated property. Listing 24.30 updates the view part of the page to use the new page model class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.30 Updating the view in the Cities.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n<b class=\"fm-bold\">@model WebApp.Pages.CitiesModel<\/b>\n\n&lt;div class=\"m-2\"&gt;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;tbody&gt;\n            <b class=\"fm-bold\">@foreach (City c in Model.Data?.Cities ??<\/b> \n                     <b class=\"fm-bold\">Enumerable.Empty&lt;City&gt;()) {<\/b>\n                &lt;tr&gt;\n                    &lt;td&gt;@c.Name&lt;\/td&gt;\n                    &lt;td&gt;@c.Country&lt;\/td&gt;\n                    &lt;td&gt;@c.Population&lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">The changes update the directives to use the page model class. To create the view for the hybrid view component, create the <code class=\"fm-code-in-text\">Pages\/Shared\/Components\/CitiesPageHybrid<\/code> folder and add to it a Razor View named <code class=\"fm-code-in-text\">Default.cshtml<\/code> with the content shown in listing 24.31.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.31 The Default.cshtml file in the Pages\/Shared\/Components\/CitiesPageHybrid folder<\/p>\n<pre class=\"programlisting\">@model CityViewModel\n\n&lt;table class=\"table table-sm table-bordered text-white bg-dark\"&gt;\n    &lt;thead&gt;&lt;tr&gt;&lt;th colspan=\"2\"&gt;Hybrid Page Summary&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n    &lt;tbody&gt;\n        &lt;tr&gt;\n            &lt;td&gt;Cities:&lt;\/td&gt;\n            &lt;td class=\"text-right\"&gt;@Model?.Cities&lt;\/td&gt;\n        &lt;\/tr&gt;\n        &lt;tr&gt;\n            &lt;td&gt;Population:&lt;\/td&gt;\n            &lt;td class=\"text-right\"&gt;\n                @Model?.Population?.ToString(\"#,###\")\n            &lt;\/td&gt;\n        &lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">Listing 24.32 applies the view component part of the hybrid class in another page.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.32 Using a view component in the Data.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n@inject DataContext context;\n\n&lt;h5 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Categories&lt;\/h5&gt;\n&lt;ul class=\"list-group m-2\"&gt;\n    @foreach (Category c in context.Categories) {\n        &lt;li class=\"list-group-item\"&gt;@c.Name&lt;\/li&gt;                \n    }\n&lt;\/ul&gt;\n\n&lt;div class=\"bg-info text-white m-2 p-2\"&gt;\n    <b class=\"fm-bold\">&lt;vc:cities-page-hybrid \/&gt;<\/b>\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">Hybrids are applied just like any other view component. Restart ASP.NET Core and request http:\/\/localhost:5000\/cities and http:\/\/localhost:5000\/data. Both URLs are processed by the same class. For the first URL, the class acts as a page model; for the second URL, the class acts as a view component. Figure 24.12 shows the output for both URLs.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre249\" src=\"\/images\/proaspnetcore7\/000255.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.12 A hybrid page model and view component class<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-459\">24.6.1 Creating a hybrid controller class<\/h3>\n<p class=\"body\">The same technique can be applied to controllers. Add a class file named <code class=\"fm-code-in-text\">CitiesController.cs<\/code> to the <code class=\"fm-code-in-text\">Controllers<\/code> folder and add the statements shown in listing 24.33.<a id=\"calibre_link-2316\"><\/a><a id=\"calibre_link-1206\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.33 The contents of the CitiesController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.ViewComponents;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [ViewComponent(Name = \"CitiesControllerHybrid\")]\n    public class CitiesController : Controller {\n        private CitiesData data;\n                \n        public CitiesController(CitiesData cdata) {\n            data = cdata;\n        }\n                \n        public IActionResult Index() {\n            return View(data.Cities);\n        }\n                \n        public IViewComponentResult Invoke() {\n            return new ViewViewComponentResult() {\n                ViewData = new ViewDataDictionary&lt;CityViewModel&gt;(\n                    ViewData,\n                    new CityViewModel {\n                        Cities = data.Cities.Count(),\n                        Population = data.Cities.Sum(c =&gt; c.Population)\n                    })\n            };\n        }\n    }\n}<\/pre>\n<p class=\"body\">A quirk in the way that controllers are instantiated means that a property decorated with the <code class=\"fm-code-in-text\">ViewComponentContext<\/code> attribute isn\u2019t required and the <code class=\"fm-code-in-text\">ViewData<\/code> property inherited from the <code class=\"fm-code-in-text\">Controller<\/code> base class can be used to create the view component result.<\/p>\n<p class=\"body\">To provide a view for the action method, create the <code class=\"fm-code-in-text\">Views\/Cities<\/code> folder and add to it a file named <code class=\"fm-code-in-text\">Index.cshtml<\/code> with the content shown in listing 24.34.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.34 The contents of the Index.cshtml file in the Views\/Cities folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;City&gt;\n@{\n    Layout = \"_ImportantLayout\";\n}\n\n&lt;div class=\"m-2\"&gt;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;tbody&gt;\n            @foreach (City c in Model) {\n                &lt;tr&gt;\n                    &lt;td&gt;@c.Name&lt;\/td&gt;\n                    &lt;td&gt;@c.Country&lt;\/td&gt;\n                    &lt;td&gt;@c.Population&lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">To provide a view for the view component, create the <code class=\"fm-code-in-text\">Views\/Shared\/Components\/CitiesControllerHybrid<\/code> folder and add to it a Razor View named <code class=\"fm-code-in-text\">Default.cshtml<\/code> with the content shown in listing 24.35.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.35 The Default.cshtml file in the Views\/Shared\/Components\/CitiesControllerHybrid folder<\/p>\n<pre class=\"programlisting\">@model CityViewModel\n\n&lt;table class=\"table table-sm table-bordered text-white bg-dark\"&gt;\n    &lt;thead&gt;&lt;tr&gt;&lt;th colspan=\"2\"&gt;Hybrid Controller Summary&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n    &lt;tbody&gt;\n        &lt;tr&gt;\n            &lt;td&gt;Cities:&lt;\/td&gt;\n            &lt;td class=\"text-right\"&gt;@Model.Cities&lt;\/td&gt;\n        &lt;\/tr&gt;\n        &lt;tr&gt;\n            &lt;td&gt;Population:&lt;\/td&gt;\n            &lt;td class=\"text-right\"&gt;\n                @Model.Population?.ToString(\"#,###\")\n            &lt;\/td&gt;\n        &lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">Listing 24.36 applies the hybrid view component in the <code class=\"fm-code-in-text\">Data.cshtml<\/code> Razor Page, replacing the hybrid class created in the previous section.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 24.36 Applying the view component in the Data.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page\n@inject DataContext context;\n\n&lt;h5 class=\"bg-primary text-white text-center m-2 p-2\"&gt;Categories&lt;\/h5&gt;\n&lt;ul class=\"list-group m-2\"&gt;\n    @foreach (Category c in context.Categories) {\n        &lt;li class=\"list-group-item\"&gt;@c.Name&lt;\/li&gt;                \n    }\n&lt;\/ul&gt;\n\n&lt;div class=\"bg-info text-white m-2 p-2\"&gt;\n    <b class=\"fm-bold\">&lt;vc:cities-controller-hybrid  \/&gt;<\/b>\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/cities\/index and http:\/\/localhost:5000\/data. For the first URL, the class in listing 24.36 is used as a controller; for the second URL, the class is used as a view component. Figure 24.13 shows the responses for both URLs.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre250\" src=\"\/images\/proaspnetcore7\/000256.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 24.13 A hybrid controller and view component class<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-460\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">View components are self-contained and generate content that isn\u2019t related to the main purpose of the application.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">View components are C# classes that are derived from <code class=\"fm-code-in-text\">ViewComponent<\/code> and whose <code class=\"fm-code-in-text\">Invoke<\/code> method is called to generate content.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">View components are applied using the <code class=\"fm-code-in-text\">Component<\/code> property, which returns a helper or using the <code class=\"fm-code-in-text\">vc<\/code> tag helper.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">View components can use partial views to generate HTML content.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">View components can receive data from the view in which they are applied and can use standard platform features such as dependency injection.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-461\">\n<div class=\"calibre1\" id=\"calibre_link-2317\">\n<h1 class=\"tochead\" id=\"calibre_link-2318\"><a id=\"calibre_link-2319\"><\/a>25 Using tag helpers<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Transforming HTML elements using tag helpers<\/li>\n<li class=\"co-summary-bullet\">Receiving context data in a tag helper class<\/li>\n<li class=\"co-summary-bullet\">Registering and applying tag helpers<\/li>\n<li class=\"co-summary-bullet\">Selecting elements to transform with tag helper scope<\/li>\n<li class=\"co-summary-bullet\">Creating custom shorthand elements<\/li>\n<li class=\"co-summary-bullet\">Generating content for view model properties<\/li>\n<li class=\"co-summary-bullet\">Creating tag helpers that can be consumed through dependency injection<\/li>\n<\/ul>\n<p class=\"body\">Tag helpers are C# classes that transform HTML elements in a view or page. Common uses for tag helpers include generating URLs for forms using the application\u2019s routing configuration, ensuring that elements of a specific type are styled consistently, and replacing custom shorthand elements with commonly used fragments of content. In this chapter, I describe how tag helpers work and how custom tag helpers are created and applied. In chapter 26, I describe the built-in tag helpers, and in chapter 27, I use tag helpers to explain how HTML forms are created. Table 25.1 puts tag helpers in context.<\/p>\n<p class=\"fm-table-caption\">Table 25.1 Putting tag helpers in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2320\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Tag helpers are classes that manipulate HTML elements, either to change them in some way, to supplement them with additional content, or to replace them entirely with new content.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Tag helpers allow view content to be generated or transformed using C# logic, ensuring that the HTML sent to the client reflects the state of the application.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The HTML elements to which tag helpers are applied are selected based on the name of the class or with the <code class=\"fm-code-in-text1\">HTMLTargetElement<\/code> attribute. When a view is rendered, elements are transformed by tag helpers and included in the HTML sent to the client.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">It can be easy to get carried away and generate complex sections of HTML content using tag helpers, which is something that is more readily achieved using view components, described in chapter 24.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">You don\u2019t have to use tag helpers, but they make it easy to generate complex HTML in ASP.NET Core applications.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 25.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 25.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2321\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a tag helper<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define a class that is derived from the <code class=\"fm-code-in-text1\">TagHelper<\/code> class.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">1&ndash;7<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Controlling the scope of a tag helper<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Alter the range of elements specified by the <code class=\"fm-code-in-text1\">HtmlTargetElement<\/code> attribute.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">8&ndash;11<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating custom HTML elements that are replaced with content<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use shorthand elements.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">12, 13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating elements programmatically<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">TagBuilder<\/code> class.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Controlling where content is inserted<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the prepend and append features.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">15&ndash;18<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Getting context data<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the context object.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">19, 20<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Operating on the view model or page model<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a model expression.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">21&ndash;25<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating coordinating tag helpers<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">Items<\/code> property.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">26, 27<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Suppressing content<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">SuppressOutput<\/code> method.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">28, 29<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Defining tag helper as services<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create tag helper components.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">30&ndash;33<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-462\">25.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the WebApp project from chapter 24. To prepare for this chapter, replace the contents of the <code class=\"fm-code-in-text\">Program.cs<\/code> file with those in listing 25.1, removing some of the configuration statements used in earlier chapters.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.1 The contents of the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddSingleton&lt;CitiesData&gt;();\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapControllers();\napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">Next, replace the contents of the <code class=\"fm-code-in-text\">Index.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder with the content shown in listing 25.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.2 The contents of the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{  \n    Layout = \"_SimpleLayout\";\n}\n\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th colspan=\"2\"&gt;Product Summary&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">The view in listing 25.2 relies on a new layout. Add a Razor View file named <code class=\"fm-code-in-text\">_SimpleLayout.cshtml<\/code> in the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder with the content shown in listing 25.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.3 The contents of the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-463\">25.1.1 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 25.4 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.4 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-464\">25.1.2 Running the example application<\/h3>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 25.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.5 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/home, which will produce the response shown in figure 25.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre251\" src=\"\/images\/proaspnetcore7\/000257.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-465\">25.2 Creating a tag helper<\/h2>\n<p class=\"body\">The best way to understand tag helpers is to create one, which reveals how they operate and how they fit into an ASP.NET Core application. In the sections that follow, I go through the process of creating and applying a tag helper that will set the Bootstrap CSS classes for a <code class=\"fm-code-in-text\">tr<\/code> element so that an element like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;tr bg-color=\"primary\"&gt;\n    &lt;th colspan=\"2\"&gt;Product Summary&lt;\/th&gt;\n&lt;\/tr&gt;\n...<\/pre>\n<p class=\"body\">will be transformed into this:<\/p>\n<pre class=\"programlisting\">...\n&lt;tr <b class=\"fm-bold\">class=\"bg-primary text-white text-center\"<\/b>&gt;\n    &lt;th colspan=\"2\"&gt;Product Summary&lt;\/th&gt;\n&lt;\/tr&gt;\n...<\/pre>\n<p class=\"body\">The tag helper will recognize the <code class=\"fm-code-in-text\">tr-color<\/code> attribute and use its value to set the <code class=\"fm-code-in-text\">class<\/code> attribute on the element sent to the browser. This isn\u2019t the most dramatic&mdash;or useful&mdash;transformation, but it provides a foundation for explaining how tag helpers work.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-466\">25.2.1 Defining the tag helper class<\/h3>\n<p class=\"body\">Tag helpers can be defined anywhere in the project, but it helps to keep them together because they need to be registered before they can be used. Create the <code class=\"fm-code-in-text\">WebApp\/TagHelpers<\/code> folder and add to it a class file named <code class=\"fm-code-in-text\">TrTagHelper.cs<\/code> with the code shown in listing 25.6.<a id=\"calibre_link-2322\"><\/a><a id=\"calibre_link-2323\"><\/a><a id=\"calibre_link-2324\"><\/a><a id=\"calibre_link-1183\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.6 The contents of the TrTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    public class TrTagHelper: TagHelper {\n        \n        public string BgColor { get; set; } = \"dark\";\n        public string TextColor { get; set; } = \"white\";\n                \n        public override void Process(TagHelperContext context, \n                TagHelperOutput output) {\n            output.Attributes.SetAttribute(\"class\", \n                $\"bg-{BgColor} text-center text-{TextColor}\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">Tag helpers are derived from the <code class=\"fm-code-in-text\">TagHelper<\/code> class, which is defined in the <code class=\"fm-code-in-text\">Microsoft.AspNetCore.Razor.TagHelpers<\/code> namespace. The <code class=\"fm-code-in-text\">TagHelper<\/code> class defines a <code class=\"fm-code-in-text\">Process<\/code> method, which is overridden by subclasses to implement the behavior that transforms elements.<\/p>\n<p class=\"body\">The name of the tag helper combines the name of the element it transforms followed by <code class=\"fm-code-in-text\">TagHelper<\/code>. In the case of the example, the class name <code class=\"fm-code-in-text\">TrTagHelper<\/code> indicates this is a tag helper that operates on <code class=\"fm-code-in-text\">tr<\/code> elements. The range of elements to which a tag helper can be applied can be broadened or narrowed using attributes, as described later in this chapter, but the default behavior is defined by the class name.<a id=\"calibre_link-2325\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Asynchronous tag helpers can be created by overriding the <code class=\"fm-code-in-text1\">ProcessAsync<\/code> method instead of the <code class=\"fm-code-in-text1\">Process<\/code> method, but this isn\u2019t required for most helpers, which tend to make small and focused changes to HTML elements. You can see an example of an asynchronous tag helper in the \"Advanced Tag Helper Features\" section.<\/p>\n<p class=\"fm-head2\">Receiving context data<\/p>\n<p class=\"body\">Tag helpers receive information about the element they are transforming through an instance of the <code class=\"fm-code-in-text\">TagHelperContext<\/code> class, which is received as an argument to the <code class=\"fm-code-in-text\">Process<\/code> method and which defines the properties described in table 25.3.<a id=\"calibre_link-2326\"><\/a><a id=\"calibre_link-2327\"><\/a><a id=\"calibre_link-2328\"><\/a><a id=\"calibre_link-1175\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 25.3 The TagHelperContext properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2329\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AllAttributes<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a read-only dictionary of the attributes applied to the element being transformed, indexed by name and by index.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Items<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a dictionary that is used to coordinate between tag helpers, as described in the \u201cCoordinating Between Tag Helpers\u201d section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">UniqueId<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a unique identifier for the element being transformed.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Although you can access details of the element\u2019s attributes through the <code class=\"fm-code-in-text\">AllAttributes<\/code> dictionary, a more convenient approach is to define a property whose name corresponds to the attribute you are interested in, like this:<\/p>\n<pre class=\"programlisting\">...\npublic string BgColor { get; set; } = \"dark\";\npublic string TextColor { get; set; } = \"white\";\n...<\/pre>\n<p class=\"body\">When a tag helper is being used, the properties it defines are inspected and assigned the value of any whose name matches attributes applied to the HTML element. As part of this process, the attribute value will be converted to match the type of the C# property so that <code class=\"fm-code-in-text\">bool<\/code> properties can be used to receive <code class=\"fm-code-in-text\">true<\/code> and <code class=\"fm-code-in-text\">false<\/code> attribute values and so <code class=\"fm-code-in-text\">int<\/code> properties can be used to receive numeric attribute values such as <code class=\"fm-code-in-text\">1<\/code> and <code class=\"fm-code-in-text\">2<\/code>.<\/p>\n<p class=\"body\">Properties for which there are no corresponding HTML element attributes are not set, which means you should check to ensure that you are not dealing with <code class=\"fm-code-in-text\">null<\/code> or provide default values, which is the approach taken in listing 25.6.<a id=\"calibre_link-2330\"><\/a><a id=\"calibre_link-2331\"><\/a><\/p>\n<p class=\"body\">The name of the attribute is automatically converted from the default HTML style, <code class=\"fm-code-in-text\">bg-color<\/code>, to the C# style, <code class=\"fm-code-in-text\">BgColor<\/code>. You can use any attribute prefix except <code class=\"fm-code-in-text\">asp-<\/code> (which Microsoft uses) and <code class=\"fm-code-in-text\">data-<\/code> (which is reserved for custom attributes that are sent to the client). The example tag helper will be configured using <code class=\"fm-code-in-text\">bg-color<\/code> and <code class=\"fm-code-in-text\">text-color<\/code> attributes, which will provide values for the <code class=\"fm-code-in-text\">BgColor<\/code> and <code class=\"fm-code-in-text\">TextColor<\/code> properties and be used to configure the <code class=\"fm-code-in-text\">tr<\/code> element in the <code class=\"fm-code-in-text\">Process<\/code> method, as follows:<\/p>\n<pre class=\"programlisting\">...\noutput.Attributes.SetAttribute(\"class\", \n    $\"bg-{<b class=\"fm-bold\">BgColor<\/b>} text-center text-{<b class=\"fm-bold\">TextColor<\/b>}\");\n...<\/pre>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Using the HTML attribute name for tag helper properties doesn\u2019t always lead to readable or understandable classes. You can break the link between the name of the property and the attribute it represents using the <code class=\"fm-code-in-text1\">HtmlAttributeName<\/code> attribute, which can be used to specify the HTML attribute that the property represents.<a id=\"calibre_link-2332\"><\/a><a id=\"calibre_link-1174\"><\/a><\/p>\n<p class=\"fm-head2\">Producing output<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Process<\/code> method transforms an element by configuring the <code class=\"fm-code-in-text\">TagHelperOutput<\/code> object that is received as an argument. The <code class=\"fm-code-in-text\">TagHelperOuput<\/code> object starts by describing the HTML element as it appears in the view and is modified through the properties and methods described in table 25.4.<a id=\"calibre_link-2333\"><\/a><a id=\"calibre_link-2334\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 25.4 The TagHelperOutput properties and methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2335\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">TagName<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to get or set the tag name for the output element.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Attributes<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a dictionary containing the attributes for the output element.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Content<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">TagHelperContent<\/code> object that is used to set the content of the element.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetChildContentAsync()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This asynchronous method provides access to the content of the element that will be transformed, as demonstrated in the \u201cCreating Shorthand Elements\u201d section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PreElement<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">TagHelperContext<\/code> object that is used to insert content in the view before the output element. See the \u201cPrepending and Appending Content and Elements\u201d section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PostElement<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">TagHelperContext<\/code> object that is used to insert content in the view after the output element. See the \u201cPrepending and Appending Content and Elements\u201d section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PreContent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">TagHelperContext<\/code> object that is used to insert content before the output element\u2019s content. See the \u201cPrepending and Appending Content and Elements\u201d section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PostContent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">TagHelperContext<\/code> object that is used to insert content after the output element\u2019s content. See the \u201cPrepending and Appending Content and Elements\u201d section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">TagMode<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property specifies how the output element will be written, using a value from the <code class=\"fm-code-in-text1\">TagMode<\/code> enumeration. See the \u201cCreating Shorthand Elements\u201d section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SupressOuput()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Calling this method excludes an element from the view. See the \u201cSuppressing the Output Element\u201d section.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">In the <code class=\"fm-code-in-text\">TrTagHelper<\/code> class, I used the <code class=\"fm-code-in-text\">Attributes<\/code> dictionary to add a <code class=\"fm-code-in-text\">class<\/code> attribute to the HTML element that specifies Bootstrap styles, including the value of the <code class=\"fm-code-in-text\">BgColor<\/code> and <code class=\"fm-code-in-text\">TextColor<\/code> properties. The effect is that the background color for <code class=\"fm-code-in-text\">tr<\/code> elements can be specified by setting <code class=\"fm-code-in-text\">bg-color<\/code> and <code class=\"fm-code-in-text\">text-color<\/code> attributes to Bootstrap names, such as <code class=\"fm-code-in-text\">primary<\/code>, <code class=\"fm-code-in-text\">info<\/code>, and <code class=\"fm-code-in-text\">danger<\/code>.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-467\">25.2.2 Registering tag helpers<\/h3>\n<p class=\"body\">Tag helper classes must be registered with the <code class=\"fm-code-in-text\">@addTagHelper<\/code> directive before they can be used. The set of views or pages to which a tag helper can be applied depends on where the <code class=\"fm-code-in-text\">@addTagHelper<\/code> directive is used.<a id=\"calibre_link-2336\"><\/a><a id=\"calibre_link-2337\"><\/a><a id=\"calibre_link-2338\"><\/a><a id=\"calibre_link-2339\"><\/a><a id=\"calibre_link-1109\"><\/a><\/p>\n<p class=\"body\">For a single view or page, the directive appears in the CSHTML file itself. To make a tag helper available more widely, it can be added to the view imports file, which is defined in the <code class=\"fm-code-in-text\">Views<\/code> folder for controllers and the <code class=\"fm-code-in-text\">Pages<\/code> folder for Razor Pages.<\/p>\n<p class=\"body\">I want the tag helpers that I create in this chapter to be available anywhere in the application, which means that the <code class=\"fm-code-in-text\">@addTagHelper<\/code> directive is added to the <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> files in the <code class=\"fm-code-in-text\">Views<\/code> and <code class=\"fm-code-in-text\">Pages<\/code> folders. The <code class=\"fm-code-in-text\">vc<\/code> element used in chapter 24 to apply view components is a tag helper, which is why the directive required to enable tag helpers is already in the <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> file.<\/p>\n<pre class=\"programlisting\">@using WebApp.Models\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n@using WebApp.Components\n<b class=\"fm-bold\">@addTagHelper *, WebApp<\/b><\/pre>\n<p class=\"body\">The first part of the argument specifies the names of the tag helper classes, with support for wildcards, and the second part specifies the name of the assembly in which they are defined. This <code class=\"fm-code-in-text\">@addTagHelper<\/code> directive uses the wildcard to select all namespaces in the <code class=\"fm-code-in-text\">WebApp<\/code> assembly, with the effect that tag helpers defined anywhere in the project can be used in any controller view. There is an identical statement in the Razor Pages <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Pages<\/code> folder.<\/p>\n<pre class=\"programlisting\">@namespace WebApp.Pages\n@using WebApp.Models\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n<b class=\"fm-bold\">@addTagHelper *, WebApp<\/b><\/pre>\n<p class=\"body\">The other <code class=\"fm-code-in-text\">@addTagHelper<\/code> directive enables the built-in tag helpers that Microsoft provides, which are described in chapter 26.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-468\">25.2.3 Using a tag helper<\/h3>\n<p class=\"body\">The final step is to use the tag helper to transform an element. In listing 25.7, I have added the attribute to the <code class=\"fm-code-in-text\">tr<\/code> element, which will apply the tag helper.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.7 Using a tag helper in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{  \n    Layout = \"_SimpleLayout\";\n}\n\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    &lt;thead&gt;\n        <b class=\"fm-bold\">&lt;tr bg-color=\"info\" text-color=\"white\"&gt;<\/b>\n            &lt;th colspan=\"2\"&gt;Product Summary&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home, which produces the response shown in figure 25.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre252\" src=\"\/images\/proaspnetcore7\/000258.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.2 Using a tag helper<\/p>\n<\/div>\n<p class=\"body\">The <code class=\"fm-code-in-text\">tr<\/code> element to which the attributes were applied in listing 25.7 has been transformed, but that isn\u2019t the only change shown in the figure. By default, tag helpers apply to all elements of a specific type, which means that all the <code class=\"fm-code-in-text\">tr<\/code> elements in the view have been transformed using the default values defined in the tag helper class, since no attributes were defined. (The reason that some table rows show no text is because of the Bootstrap <code class=\"fm-code-in-text\">table-striped<\/code> class, which applies different styles to alternate rows.)<\/p>\n<p class=\"body\">In fact, the problem is more serious because the <code class=\"fm-code-in-text\">@addTagHelper<\/code> directives in the view import files mean that the example tag helper is applied to all <code class=\"fm-code-in-text\">tr<\/code> elements used in any view rendered by controllers and Razor Pages. Use a browser to request http:\/\/localhost:5000\/cities, for example, and you will see the <code class=\"fm-code-in-text\">tr<\/code> elements in the response from the <code class=\"fm-code-in-text\">Cities<\/code> Razor Page have also been transformed, as shown in figure 25.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre253\" src=\"\/images\/proaspnetcore7\/000259.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.3 Unexpectedly modifying elements with a tag helper<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-469\">25.2.4 Narrowing the scope of a tag helper<\/h3>\n<p class=\"body\">The range of elements that are transformed by a tag helper can be controlled using the <code class=\"fm-code-in-text\">HtmlTargetElement<\/code> element, as shown in listing 25.8.<a id=\"calibre_link-2340\"><\/a><a id=\"calibre_link-2341\"><\/a><a id=\"calibre_link-1186\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.8 Narrowing scope in the TrTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    <b class=\"fm-bold\">[HtmlTargetElement(\"tr\", Attributes = \"bg-color,text-color\",<\/b> \n        <b class=\"fm-bold\">ParentTag = \"thead\")]<\/b>\n    public class TrTagHelper : TagHelper {\n        \n        public string BgColor { get; set; } = \"dark\";\n        public string TextColor { get; set; } = \"white\";\n                \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n            output.Attributes.SetAttribute(\"class\",\n                $\"bg-{BgColor} text-center text-{TextColor}\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HtmlTargetElement<\/code> attribute describes the elements to which the tag helper applies. The first argument specifies the element type and supports the additional named properties described in table 25.5.<a id=\"calibre_link-2342\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 25.5 The HtmlTargetElement properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2343\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Attributes<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to specify that a tag helper should be applied only to elements that have a given set of attributes, supplied as a comma-separated list. An attribute name that ends with an asterisk will be treated as a prefix so that <code class=\"fm-code-in-text1\">bg-*<\/code> will match <code class=\"fm-code-in-text1\">bg-color<\/code>, <code class=\"fm-code-in-text1\">bg-size<\/code>, and so on.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ParentTag<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to specify that a tag helper should be applied only to elements that are contained within an element of a given type.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">TagStructure<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to specify that a tag helper should be applied only to elements whose tag structure corresponds to the given value from the <code class=\"fm-code-in-text1\">TagStructure<\/code> enumeration, which defines <code class=\"fm-code-in-text1\">Unspecified<\/code>, <code class=\"fm-code-in-text1\">NormalOrSelfClosing<\/code>, and <code class=\"fm-code-in-text1\">WithoutEndTag<\/code>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Attributes<\/code> property supports CSS attribute selector syntax so that <code class=\"fm-code-in-text\">[bg-color]<\/code> matches elements that have a <code class=\"fm-code-in-text\">bg-color<\/code> attribute, <code class=\"fm-code-in-text\">[bg-color=primary]<\/code> matches elements that have a <code class=\"fm-code-in-text\">bg-color<\/code> attribute whose value is <code class=\"fm-code-in-text\">primary<\/code>, and <code class=\"fm-code-in-text\">[bg-color^=p]<\/code> matches elements with a <code class=\"fm-code-in-text\">bg-color<\/code> attribute whose value begins with <code class=\"fm-code-in-text\">p<\/code>. The attribute applied to the tag helper in listing 25.8 matches <code class=\"fm-code-in-text\">tr<\/code> elements with both <code class=\"fm-code-in-text\">bg-color<\/code> and <code class=\"fm-code-in-text\">text-color<\/code> attributes that are children of a <code class=\"fm-code-in-text\">thead<\/code> element. Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1, and you will see the scope of the tag helper has been narrowed, as shown in figure 25.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre254\" src=\"\/images\/proaspnetcore7\/000260.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.4 Narrowing the scope of a tag helper<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-470\">25.2.5 Widening the scope of a tag helper<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HtmlTargetElement<\/code> attribute can also be used to widen the scope of a tag helper so that it matches a broader range of elements. This is done by setting the attribute\u2019s first argument to an asterisk (the <code class=\"fm-code-in-text\">*<\/code> character), which matches any element. Listing 25.9 changes the attribute applied to the example tag helper so that it matches any element that has <code class=\"fm-code-in-text\">bg-color<\/code> and <code class=\"fm-code-in-text\">text-color<\/code> attributes.<a id=\"calibre_link-2344\"><\/a><a id=\"calibre_link-1189\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.9 Widening scope in the TrTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    <b class=\"fm-bold\">[HtmlTargetElement(\"*\", Attributes = \"bg-color,text-color\")]<\/b>\n    public class TrTagHelper : TagHelper {\n        \n        public string BgColor { get; set; } = \"dark\";\n        public string TextColor { get; set; } = \"white\";\n                \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n            output.Attributes.SetAttribute(\"class\",\n                $\"bg-{BgColor} text-center text-{TextColor}\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">Care must be taken when using the asterisk because it is easy to match too widely and select elements that should not be transformed. A safer middle ground is to apply the <code class=\"fm-code-in-text\">HtmlTargetElement<\/code> attribute for each type of element, as shown in listing 25.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.10 Balancing scope in the TrTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    <b class=\"fm-bold\">[HtmlTargetElement(\"tr\", Attributes = \"bg-color,text-color\")]<\/b>\n    <b class=\"fm-bold\">[HtmlTargetElement(\"td\", Attributes = \"bg-color\")]<\/b>\n    public class TrTagHelper : TagHelper {\n        \n        public string BgColor { get; set; } = \"dark\";\n        public string TextColor { get; set; } = \"white\";\n                \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n            output.Attributes.SetAttribute(\"class\",\n                $\"bg-{BgColor} text-center text-{TextColor}\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">Each instance of the attribute can use different selection criteria. This tag helper matches <code class=\"fm-code-in-text\">tr<\/code> elements with <code class=\"fm-code-in-text\">bg-color<\/code> and <code class=\"fm-code-in-text\">text-color<\/code> attributes and matches <code class=\"fm-code-in-text\">td<\/code> elements with <code class=\"fm-code-in-text\">bg-color<\/code> attributes. Listing 25.11 adds an element to be transformed to the <code class=\"fm-code-in-text\">Index<\/code> view to demonstrate the revised scope.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.11 Adding attributes in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{  \n    Layout = \"_SimpleLayout\";\n}\n\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    &lt;thead&gt;\n        &lt;tr bg-color=\"info\" text-color=\"white\"&gt;\n            &lt;th colspan=\"2\"&gt;Product Summary&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;\n            &lt;th&gt;Price&lt;\/th&gt;\n            <b class=\"fm-bold\">&lt;td bg-color=\"dark\"&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;<\/b>\n        &lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1. The response will contain two transformed elements, as shown in figure 25.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre255\" src=\"\/images\/proaspnetcore7\/000261.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.5 Managing the scope of a tag helper<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Ordering tag helper execution<\/p>\n<p class=\"fm-sidebar-text\">If you need to apply multiple tag helpers to an element, you can control the sequence in which they execute by setting the <code class=\"fm-code-in-text1\">Order<\/code> property, which is inherited from the <code class=\"fm-code-in-text1\">TagHelper<\/code> base class. Managing the sequence can help minimize the conflicts between tag helpers, although it is still easy to encounter problems.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-471\">25.3 Advanced tag helper features<\/h2>\n<p class=\"body\">The previous section demonstrated how to create a basic tag helper, but that just scratches the surface of what\u2019s possible. In the sections that follow, I show more advanced uses for tag helpers and the features they provide.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-472\">25.3.1 Creating shorthand elements<\/h3>\n<p class=\"body\">Tag helpers are not restricted to transforming the standard HTML elements and can also be used to replace custom elements with commonly used content. This can be a useful feature for making views more concise and making their intent more obvious. To demonstrate, listing 25.12 replaces the <code class=\"fm-code-in-text\">thead<\/code> element in the <code class=\"fm-code-in-text\">Index<\/code> view with a custom HTML element.<a id=\"calibre_link-2345\"><\/a><a id=\"calibre_link-1190\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.12 Adding a custom element in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{  \n    Layout = \"_SimpleLayout\";\n}\n\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    <b class=\"fm-bold\">&lt;tablehead bg-color=\"dark\"&gt;Product Summary&lt;\/tablehead&gt;<\/b>    \n    &lt;tbody&gt;\n        &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;\n            &lt;th&gt;Price&lt;\/th&gt;\n            &lt;td bg-color=\"dark\"&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;\n        &lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">tablehead<\/code> element isn\u2019t part of the HTML specification and won\u2019t be understood by browsers. Instead, I am going to use this element as shorthand for generating the <code class=\"fm-code-in-text\">thead<\/code> element and its content for the HTML table. Add a class named <code class=\"fm-code-in-text\">TableHeadTagHelper.cs<\/code> to the <code class=\"fm-code-in-text\">TagHelpers<\/code> folder and use it to define the class shown in listing 25.13.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> When dealing with custom elements that are not part of the HTML specification, you must apply the <code class=\"fm-code-in-text1\">HtmlTargetElement<\/code> attribute and specify the element name, as shown in listing 25.13. The convention of applying tag helpers to elements based on the class name works only for standard element names.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.13 The contents of TableHeadTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    [HtmlTargetElement(\"tablehead\")]\n    public class TableHeadTagHelper : TagHelper {\n        \n        public string BgColor { get; set; } = \"light\";\n                \n        public override async Task ProcessAsync(TagHelperContext context,\n                TagHelperOutput output) {\n                                \n            output.TagName = \"thead\";\n            output.TagMode = TagMode.StartTagAndEndTag;\n            output.Attributes.SetAttribute(\"class\",\n                $\"bg-{BgColor} text-white text-center\");\n                                \n            string content = \n                (await output.GetChildContentAsync()).GetContent();\n            output.Content.SetHtmlContent(\n                $\"&lt;tr&gt;&lt;th colspan=\\\"2\\\"&gt;{content}&lt;\/th&gt;&lt;\/tr&gt;\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">This tag helper is asynchronous and overrides the <code class=\"fm-code-in-text\">ProcessAsync<\/code> method so that it can access the existing content of the elements it transforms. The <code class=\"fm-code-in-text\">ProcessAsync<\/code> method uses the properties of the <code class=\"fm-code-in-text\">TagHelperOuput<\/code> object to generate a completely different element: the <code class=\"fm-code-in-text\">TagName<\/code> property is used to specify a <code class=\"fm-code-in-text\">thead<\/code> element, the <code class=\"fm-code-in-text\">TagMode<\/code> property is used to specify that the element is written using start and end tags, the <code class=\"fm-code-in-text\">Attributes.SetAttribute<\/code> method is used to define a <code class=\"fm-code-in-text\">class<\/code> attribute, and the <code class=\"fm-code-in-text\">Content<\/code> property is used to set the element content.<\/p>\n<p class=\"body\">The existing content of the element is obtained through the asynchronous <code class=\"fm-code-in-text\">GetChildContentAsync<\/code> method, which returns a <code class=\"fm-code-in-text\">TagHelperContent<\/code> object. This is the same object that is returned by the <code class=\"fm-code-in-text\">TagHelperOutput.Content<\/code> property and allows the content of the element to be inspected and changed using the same type, through the methods described in table 25.6.<\/p>\n<p class=\"fm-table-caption\">Table 25.6 Useful TagHelperContent methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2346\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetContent()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns the contents of the HTML element as a string.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetContent(text)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sets the content of the output element. The <code class=\"fm-code-in-text1\">string<\/code> argument is encoded so that it is safe for inclusion in an HTML element.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetHtmlContent(html)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sets the content of the output element. The <code class=\"fm-code-in-text1\">string<\/code> argument is assumed to be safely encoded. Use with caution.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Append(text)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method safely encodes the specified <code class=\"fm-code-in-text1\">string<\/code> and adds it to the content of the output element.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AppendHtml(html)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method adds the specified <code class=\"fm-code-in-text1\">string<\/code> to the content of the output element without performing any encoding. Use with caution.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Clear()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method removes the content of the output element.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">In listing 25.13, the existing content of the element is read through the <code class=\"fm-code-in-text\">GetContent<\/code> element and then set using the <code class=\"fm-code-in-text\">SetHtmlContent<\/code> method. The effect is to wrap the existing content in the transformed element in <code class=\"fm-code-in-text\">tr<\/code> and <code class=\"fm-code-in-text\">th<\/code> elements.<\/p>\n<p class=\"body\">Restart ASP.NET Core and navigate to http:\/\/localhost:5000\/home\/index\/1, and you will see the effect of the tag helper, which is shown in figure 25.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre256\" src=\"\/images\/proaspnetcore7\/000262.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.6 Using a shorthand element<\/p>\n<\/div>\n<p class=\"body\">The tag helper transforms this shorthand element:<\/p>\n<pre class=\"programlisting\">...\n&lt;tablehead bg-color=\"dark\"&gt;Product Summary&lt;\/tablehead&gt;\n...<\/pre>\n<p class=\"body\">into these elements:<\/p>\n<pre class=\"programlisting\">...\n&lt;thead class=\"bg-dark text-white text-center\"&gt;\n    &lt;tr&gt;\n        &lt;th colspan=\"2\"&gt;Product Summary&lt;\/th&gt;\n    &lt;\/tr&gt;\n&lt;\/thead&gt;\n...<\/pre>\n<p class=\"body\">Notice that the transformed elements do not include the <code class=\"fm-code-in-text\">bg-color<\/code> attribute. Attributes matched to properties defined by the tag helper are removed from the output element and must be explicitly redefined if they are required.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-473\">25.3.2 Creating elements programmatically<\/h3>\n<p class=\"body\">When generating new HTML elements, you can use standard C# string formatting to create the content you require, which is the approach I took in listing 25.13. This works, but it can be awkward and requires close attention to avoid typos. A more robust approach is to use the <code class=\"fm-code-in-text\">TagBuilder<\/code> class, which is defined in the <code class=\"fm-code-in-text\">Microsoft.AspNetCore.Mvc.Rendering<\/code> namespace and allows elements to be created in a more structured manner. The <code class=\"fm-code-in-text\">TagHelperContent<\/code> methods described in table 25.6 accept <code class=\"fm-code-in-text\">TagBuilder<\/code> objects, which makes it easy to create HTML content in tag helpers, as shown in listing 25.14.<a id=\"calibre_link-2347\"><\/a><a id=\"calibre_link-1192\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.14 HTML elements in the TableHeadTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Razor.TagHelpers;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc.Rendering;<\/b>\n\nnamespace WebApp.TagHelpers {\n\n    [HtmlTargetElement(\"tablehead\")]\n    public class TableHeadTagHelper: TagHelper {\n        \n        public string BgColor { get; set; } = \"light\";\n                \n        public override async Task ProcessAsync(TagHelperContext context, \n                TagHelperOutput output) {\n                                \n            output.TagName = \"thead\";\n            output.TagMode = TagMode.StartTagAndEndTag;\n            output.Attributes.SetAttribute(\"class\", \n                $\"bg-{BgColor} text-white text-center\");\n                                \n            string content =\n                (await output.GetChildContentAsync()).GetContent();\n                                \n            <b class=\"fm-bold\">TagBuilder header = new TagBuilder(\"th\");<\/b>\n            <b class=\"fm-bold\">header.Attributes[\"colspan\"] = \"2\";<\/b>\n            <b class=\"fm-bold\">header.InnerHtml.Append(content);<\/b>\n                        \n            <b class=\"fm-bold\">TagBuilder row = new TagBuilder(\"tr\");<\/b>\n            <b class=\"fm-bold\">row.InnerHtml.AppendHtml(header);<\/b>\n                        \n            <b class=\"fm-bold\">output.Content.SetHtmlContent(row);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">This example creates each new element using a <code class=\"fm-code-in-text\">TagBuilder<\/code> object and composes them to produce the same HTML structure as the string-based version in listing 25.13.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-474\">25.3.3 Prepending and appending content and elements<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">TagHelperOutput<\/code> class provides four properties that make it easy to inject new content into a view so that it surrounds an element or the element\u2019s content, as described in table 25.7. In the sections that follow, I explain how you can insert content around and inside the target element.<\/p>\n<p class=\"fm-table-caption\">Table 25.7 The TagHelperOutput properties for appending context and elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2348\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PreElement<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to insert elements into the view before the target element.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PostElement<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to insert elements into the view after the target element.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PreContent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to insert content into the target element, before any existing content.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PostContent<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to insert content into the target element, after any existing content.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"fm-head2\">Inserting content around the output element<\/p>\n<p class=\"body\">The first <code class=\"fm-code-in-text\">TagHelperOuput<\/code> properties are <code class=\"fm-code-in-text\">PreElement<\/code> and <code class=\"fm-code-in-text\">PostElement<\/code>, which are used to insert elements into the view before and after the output element. To demonstrate the use of these properties, add a class file named <code class=\"fm-code-in-text\">ContentWrapperTagHelper.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/TagHelpers<\/code> folder with the content shown in listing 25.15.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.15 The contents of the WrapperTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    [HtmlTargetElement(\"*\", Attributes = \"[wrap=true]\")]\n    public class ContentWrapperTagHelper: TagHelper {\n        \n        public override void Process(TagHelperContext context, \n                TagHelperOutput output) {\n            TagBuilder elem = new TagBuilder(\"div\");\n            elem.Attributes[\"class\"] = \"bg-primary text-white p-2 m-2\";\n            elem.InnerHtml.AppendHtml(\"Wrapper\");\n                        \n            output.PreElement.AppendHtml(elem);\n            output.PostElement.AppendHtml(elem);\n        }\n    }\n}<\/pre>\n<p class=\"body\">This tag helper transforms elements that have a <code class=\"fm-code-in-text\">wrap<\/code> attribute whose value is <code class=\"fm-code-in-text\">true<\/code>, which it does using the <code class=\"fm-code-in-text\">PreElement<\/code> and <code class=\"fm-code-in-text\">PostElement<\/code> properties to add a <code class=\"fm-code-in-text\">div<\/code> element before and after the output element. Listing 25.16 adds an element to the <code class=\"fm-code-in-text\">Index<\/code> view that is transformed by the tag helper.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.16 Adding an element in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{\n    Layout = \"_SimpleLayout\";\n}\n\n<b class=\"fm-bold\">&lt;div class=\"m-2\" wrap=\"true\"&gt;Inner Content&lt;\/div&gt;<\/b>\n\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    &lt;tablehead bg-color=\"dark\"&gt;Product Summary&lt;\/tablehead&gt;    \n    &lt;tbody&gt;\n        &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;\n            &lt;th&gt;Price&lt;\/th&gt;\n            &lt;td bg-color=\"dark\"&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;\n        &lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1. The response includes the transformed element, as shown in figure 25.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre257\" src=\"\/images\/proaspnetcore7\/000263.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.7 Inserting content around the output element<\/p>\n<\/div>\n<p class=\"body\">If you examine the HTML sent to the browser, you will see that this element:<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"m-2\" wrap=\"true\"&gt;Inner Content&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">has been transformed into these elements:<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"bg-primary text-white p-2 m-2\"&gt;Wrapper&lt;\/div&gt;\n&lt;div class=\"m-2\" wrap=\"true\"&gt;Inner Content&lt;\/div&gt;\n&lt;div class=\"bg-primary text-white p-2 m-2\"&gt;Wrapper&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">Notice that the <code class=\"fm-code-in-text\">wrap<\/code> attribute has been left on the output element. This is because I didn\u2019t define a property in the tag helper class that corresponds to this attribute. If you want to prevent attributes from being included in the output, then define a property for them in the tag helper class, even if you don\u2019t use the attribute value.<\/p>\n<p class=\"fm-head2\">Inserting content inside the output element<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">PreContent<\/code> and <code class=\"fm-code-in-text\">PostContent<\/code> properties are used to insert content inside the output element, surrounding the original content. To demonstrate this feature, add a class file named <code class=\"fm-code-in-text\">HighlightTagHelper.cs<\/code> to the <code class=\"fm-code-in-text\">TagHelpers<\/code> folder and use it to define the tag helper shown in listing 25.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.17 The contents of the HighlightTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    [HtmlTargetElement(\"*\", Attributes = \"[highlight=true]\")]\n    public class HighlightTagHelper: TagHelper {\n        \n        public override void Process(TagHelperContext context, \n                TagHelperOutput output) {\n                                \n            output.PreContent.SetHtmlContent(\"&lt;b&gt;&lt;i&gt;\");\n            output.PostContent.SetHtmlContent(\"&lt;\/i&gt;&lt;\/b&gt;\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">This tag helper inserts <code class=\"fm-code-in-text\">b<\/code> and <code class=\"fm-code-in-text\">i<\/code> elements around the output element\u2019s content. Listing 25.18 adds the wrap attribute to one of the table cells in the <code class=\"fm-code-in-text\">Index<\/code> view.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.18 Adding an attribute in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{  \n    Layout = \"_SimpleLayout\";\n}\n\n&lt;div class=\"m-2\" wrap=\"true\"&gt;Inner Content&lt;\/div&gt;\n\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    &lt;tablehead bg-color=\"dark\"&gt;Product Summary&lt;\/tablehead&gt;    \n    &lt;tbody&gt;\n        <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td highlight=\"true\"&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n        &lt;tr&gt;\n            &lt;th&gt;Price&lt;\/th&gt;\n            &lt;td bg-color=\"dark\"&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;\n        &lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1. The response includes the transformed element, as shown in figure 25.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre257\" src=\"\/images\/proaspnetcore7\/000264.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.8 Inserting content inside an element<\/p>\n<\/div>\n<p class=\"body\">If you examine the HTML sent to the browser, you will see that this element:<\/p>\n<pre class=\"programlisting\">...\n&lt;td highlight=\"true\"&gt;@Model?.Name&lt;\/td&gt;\n...<\/pre>\n<p class=\"body\">has been transformed into these elements:<\/p>\n<pre class=\"programlisting\">...\n&lt;td highlight=\"true\"&gt;&lt;b&gt;&lt;i&gt;Kayak&lt;\/i&gt;&lt;\/b&gt;&lt;\/td&gt;\n...<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-475\">25.3.4 Getting view context data<\/h3>\n<p class=\"body\">A common use for tag helpers is to transform elements so they contain details of the current request or the view model\/page model, which requires access to context data. To create this type of tag helper, add a file named <code class=\"fm-code-in-text\">RouteDataTagHelper.cs<\/code> to the <code class=\"fm-code-in-text\">TagHelpers<\/code> folder, with the content shown in listing 25.19.<a id=\"calibre_link-2349\"><\/a><a id=\"calibre_link-1180\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.19 The contents of the RouteDataTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    [HtmlTargetElement(\"div\", Attributes = \"[route-data=true]\")]\n    public class RouteDataTagHelper : TagHelper {\n        \n        [ViewContext]\n        [HtmlAttributeNotBound]\n        public ViewContext Context { get; set; } = new();\n                \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n                                \n            output.Attributes.SetAttribute(\"class\", \"bg-primary m-2 p-2\");\n                        \n            TagBuilder list = new TagBuilder(\"ul\");\n            list.Attributes[\"class\"] = \"list-group\";\n            RouteValueDictionary rd = Context.RouteData.Values;\n            if (rd.Count &gt; 0) {\n                foreach (var kvp in rd) {\n                    TagBuilder item = new TagBuilder(\"li\");\n                    item.Attributes[\"class\"] = \"list-group-item\";\n                    item.InnerHtml.Append($\"{kvp.Key}: {kvp.Value}\");\n                    list.InnerHtml.AppendHtml(item);\n                }\n                output.Content.AppendHtml(list);\n            } else {\n                output.Content.Append(\"No route data\");\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The tag helper transforms <code class=\"fm-code-in-text\">div<\/code> elements that have a <code class=\"fm-code-in-text\">route-data<\/code> attribute whose value is <code class=\"fm-code-in-text\">true<\/code> and populates the output element with a list of the segment variables obtained by the routing system.<a id=\"calibre_link-2350\"><\/a><a id=\"calibre_link-2351\"><\/a><a id=\"calibre_link-2352\"><\/a><a id=\"calibre_link-1181\"><\/a><\/p>\n<p class=\"body\">To get the route data, I added a property called <code class=\"fm-code-in-text\">Context<\/code> and decorated it with two attributes, like this:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">[ViewContext]<\/b>\n<b class=\"fm-bold\">[HtmlAttributeNotBound]<\/b>\npublic ViewContext Context { get; set; } = new();\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ViewContext<\/code> attribute denotes that the value of this property should be assigned a <code class=\"fm-code-in-text\">ViewContext<\/code> object when a new instance of the tag helper class is created, which provides details of the view that is being rendered, including the routing data, as described in chapter 13.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HtmlAttributeNotBound<\/code> attribute prevents a value from being assigned to this property if there is a matching attribute defined on the <code class=\"fm-code-in-text\">div<\/code> element. This is good practice, especially if you are writing tag helpers for other developers to use.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Tag helpers can declare dependencies on services in their constructors, which are resolved using the dependency injection feature described in chapter 14.<\/p>\n<p class=\"body\">Listing 25.20 adds an element to the <code class=\"fm-code-in-text\">Home<\/code> controller\u2019s <code class=\"fm-code-in-text\">Index<\/code> view that will be transformed by the new tag helper.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.20 Adding an element in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product?\n@{  \n    Layout = \"_SimpleLayout\";\n}\n\n<b class=\"fm-bold\">&lt;div route-data=\"true\"&gt;&lt;\/div&gt;<\/b>\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    &lt;tablehead bg-color=\"dark\"&gt;Product Summary&lt;\/tablehead&gt;    \n    &lt;tbody&gt;\n        &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td highlight=\"true\"&gt;@Model?.Name&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;tr&gt;\n            &lt;th&gt;Price&lt;\/th&gt;\n            &lt;td bg-color=\"dark\"&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;\n        &lt;\/tr&gt;\n        &lt;tr&gt;&lt;th&gt;Category ID&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1. The response will include a list of the segment variables the routing system has matched, as shown in figure 25.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre258\" src=\"\/images\/proaspnetcore7\/000265.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.9 Displaying context data with a tag helper<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-476\">25.3.5 Working with model expressions<\/h3>\n<p class=\"body\">Tag helpers can operate on the view model, tailoring the transformations they perform or the output they create. To see how this feature works, add a class file named <code class=\"fm-code-in-text\">ModelRowTagHelper.cs<\/code> to the <code class=\"fm-code-in-text\">TagHelpers<\/code> folder, with the code shown in listing 25.21.<a id=\"calibre_link-2353\"><\/a><a id=\"calibre_link-2354\"><\/a><a id=\"calibre_link-2355\"><\/a><a id=\"calibre_link-1083\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.21 The contents of the ModelRowTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    [HtmlTargetElement(\"tr\", Attributes = \"for\")]\n    public class ModelRowTagHelper : TagHelper {\n        \n        public string Format { get; set; } = \"\";\n        public ModelExpression? For { get; set; }\n                \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n                                \n            output.TagMode = TagMode.StartTagAndEndTag;\n                        \n            TagBuilder th = new TagBuilder(\"th\");\n            th.InnerHtml.Append(For?.Name ?? String.Empty);\n            output.Content.AppendHtml(th);\n                        \n            TagBuilder td = new TagBuilder(\"td\");\n            if (Format != null &amp;&amp; \n                    For?.Metadata.ModelType == typeof(decimal)) {\n                td.InnerHtml.Append(((decimal)For.Model)\n                    .ToString(Format));\n            } else {\n                td.InnerHtml.Append(For?.Model.ToString() \n                    ?? String.Empty);\n            }\n            output.Content.AppendHtml(td);\n        }\n    }\n}<\/pre>\n<p class=\"body\">This tag helper transforms <code class=\"fm-code-in-text\">tr<\/code> elements that have a <code class=\"fm-code-in-text\">for<\/code> attribute. The important part of this tag helper is the type of the <code class=\"fm-code-in-text\">For<\/code> property, which is used to receive the value of the <code class=\"fm-code-in-text\">for<\/code> attribute.<\/p>\n<pre class=\"programlisting\">...\npublic <b class=\"fm-bold\">ModelExpression?<\/b> For { get; set; }\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ModelExpression<\/code> class is used when you want to operate on part of the view model, which is most easily explained by jumping forward and showing how the tag helper is applied in the view, as shown in listing 25.22.<\/p>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-2356\"><\/a><span class=\"fm-callout-head\">Note<\/span> The <code class=\"fm-code-in-text1\">ModelExpression<\/code> feature can be used only on view models or page models. It cannot be used on variables that are created within a view, such as with a <code class=\"fm-code-in-text1\">@foreach<\/code> expression.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.22 Using the tag helper in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@model Product<\/b>\n@{  \n    Layout = \"_SimpleLayout\";\n}\n\n&lt;div route-data=\"true\"&gt;&lt;\/div&gt;\n\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    &lt;tablehead bg-color=\"dark\"&gt;Product Summary&lt;\/tablehead&gt;    \n    &lt;tbody&gt;\n        <b class=\"fm-bold\">&lt;tr for=\"Name\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;tr for=\"Price\" format=\"c\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;tr for=\"CategoryId\" \/&gt;<\/b>\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">The value of the <code class=\"fm-code-in-text\">for<\/code> attribute is the name of a property defined by the view model class. When the tag helper is created, the type of the <code class=\"fm-code-in-text\">For<\/code> property is detected and assigned a <code class=\"fm-code-in-text\">ModelExpression<\/code> object that describes the selected property.<\/p>\n<p class=\"body\">Notice that I have changed the view model type in listing 25.22. This is important because the <code class=\"fm-code-in-text\">ModelExpression<\/code> feature works on non-nullable types. This is a useful feature, but it presents the problems with null values that I described in chapter 21.<\/p>\n<p class=\"body\">I am not going to describe the <code class=\"fm-code-in-text\">ModelExpression<\/code> class in any detail because any introspection on types leads to endless lists of classes and properties. Further, ASP.NET Core provides a useful set of built-in tag helpers that use the view model to transform elements, as described in chapter 26, which means you don\u2019t need to create your own.<\/p>\n<p class=\"body\">For the example tag helper, I use three basic features that are worth describing. The first is to get the name of the model property so that I can include it in the output element, like this:<\/p>\n<pre class=\"programlisting\">...\nth.InnerHtml.Append(<b class=\"fm-bold\">For?.Name<\/b> ?? String.Empty);\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Name<\/code> property returns the name of the model property. The second feature is to get the type of the model property so that I can determine whether to format the value, like this:<\/p>\n<pre class=\"programlisting\">...\nif (Format != null &amp;&amp; <b class=\"fm-bold\">For?.Metadata.ModelType<\/b> == typeof(decimal)) {\n...<\/pre>\n<p class=\"body\">The third feature is to get the value of the property so that it can be included in the response.<\/p>\n<pre class=\"programlisting\">...\ntd.InnerHtml.Append(<b class=\"fm-bold\">For?.Model<\/b>.ToString() ?? String.Empty);\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/2, and you will see the response shown in figure 25.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre259\" src=\"\/images\/proaspnetcore7\/000266.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.10 Using the view model in a tag helper<\/p>\n<\/div>\n<p class=\"fm-head2\">Working with the page model<\/p>\n<p class=\"body\">Tag helpers with model expressions can be applied in Razor Pages, although the expression that selects the property must account for the way that the <code class=\"fm-code-in-text\">Model<\/code> property returns the page model class. Listing 25.23 applies the tag helper to the <code class=\"fm-code-in-text\">Editor<\/code> Razor Page, whose page model defines a <code class=\"fm-code-in-text\">Product<\/code> property.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.23 Applying a tag helper in the Editor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"{id:long}\"\n@model EditorModel\n@{\n    Layout = null;\n}\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"bg-primary text-white text-center m-2 p-2\"&gt;Editor&lt;\/div&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n            &lt;tbody&gt;\n                <b class=\"fm-bold\">&lt;tr for=\"Product.Name\" \/&gt;<\/b>\n                <b class=\"fm-bold\">&lt;tr for=\"Product.Price\" format=\"c\" \/&gt;<\/b>\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n        &lt;form method=\"post\"&gt;\n            @Html.AntiForgeryToken()\n            &lt;div class=\"form-group\"&gt;\n                &lt;label&gt;Price&lt;\/label&gt;\n                &lt;input name=\"price\" class=\"form-control\"\n                       value=\"@Model.Product?.Price\" \/&gt;\n            &lt;\/div&gt;\n            &lt;button class=\"btn btn-primary mt-2\" type=\"submit\"&gt;\n                Submit\n            &lt;\/button&gt;\n        &lt;\/form&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The value for the <code class=\"fm-code-in-text\">for<\/code> attribute selects the nested properties through the <code class=\"fm-code-in-text\">Product<\/code> property, which provides the tag helper with the <code class=\"fm-code-in-text\">ModelExpression<\/code> it requires.<\/p>\n<p class=\"body\">Model expressions cannot be used with the null conditional operator, which presents a problem for this example because the type of the <code class=\"fm-code-in-text\">Product<\/code> property is <code class=\"fm-code-in-text\">Product?<\/code>. Listing 25.24 changes the property type to <code class=\"fm-code-in-text\">Product<\/code> and assigns a default value. (I demonstrate a different way of resolving this issue in chapter 27.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.24 Changing a property type in the Editor.cshtml.cs file in the Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\nusing WebApp.Models;\n\nnamespace WebApp.Pages {\n    public class EditorModel : PageModel {\n        private DataContext context;\n                \n        <b class=\"fm-bold\">public Product Product { get; set; }<\/b> \n            <b class=\"fm-bold\">= new() { Name = string.Empty };<\/b>\n                        \n        public EditorModel(DataContext ctx) {\n            context = ctx;\n        }\n                \n        public async Task OnGetAsync(long id) {\n            <b class=\"fm-bold\">Product = await context.Products.FindAsync(id)<\/b> \n                <b class=\"fm-bold\">?? new() { Name = string.Empty };<\/b>\n        }\n                \n        public async Task&lt;IActionResult&gt; OnPostAsync(long id, \n                decimal price) {\n            Product? p = await context.Products.FindAsync(id);\n            if (p != null) {\n                p.Price = price;\n            }\n            await context.SaveChangesAsync();\n            return RedirectToPage();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and a browser to request http:\/\/localhost:5000\/editor\/1 to see the response from the page, which is shown on the left of figure 25.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre260\" src=\"\/images\/proaspnetcore7\/000267.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.11 Using a model expression tag helper with a Razor Page<\/p>\n<\/div>\n<p class=\"body\">One consequence of the page model is that the <code class=\"fm-code-in-text\">ModelExpression.Name<\/code> property will return <code class=\"fm-code-in-text\">Product.Name<\/code>, for example, instead of just <code class=\"fm-code-in-text\">Name<\/code>. Listing 25.25 updates the tag helper so that it will display just the last part of the model expression name.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> This example is intended to highlight the effect of the page model on model expressions. Instead of displaying just the last part of the name, a more flexible approach is to add support for another attribute that allows the display value to be overridden as needed.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.25 Handling names in the ModelRowTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    [HtmlTargetElement(\"tr\", Attributes = \"for\")]\n    public class ModelRowTagHelper : TagHelper {\n        \n        public string Format { get; set; } = \"\";\n        public ModelExpression? For { get; set; }\n                \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n                                \n            output.TagMode = TagMode.StartTagAndEndTag;\n                        \n            TagBuilder th = new TagBuilder(\"th\");\n            <b class=\"fm-bold\">th.InnerHtml.Append(For?.Name.Split(\".\").Last()<\/b> \n                <b class=\"fm-bold\">?? String.Empty);<\/b>\n            output.Content.AppendHtml(th);\n                        \n            TagBuilder td = new TagBuilder(\"td\");\n            if (Format != null &amp;&amp; \n                    For?.Metadata.ModelType == typeof(decimal)) {\n                td.InnerHtml.Append(((decimal)For.Model)\n                    .ToString(Format));\n            } else {\n                td.InnerHtml.Append(For?.Model.ToString() \n                    ?? String.Empty);\n            }\n            output.Content.AppendHtml(td);\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/editor\/1; you will see the revised response, which is shown on the right of figure 25.11.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-477\">25.3.6 Coordinating between tag helpers<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">TagHelperContext.Items<\/code> property provides a dictionary used by tag helpers that operate on elements and those that operate on their descendants. To demonstrate the use of the <code class=\"fm-code-in-text\">Items<\/code> collection, add a class file named <code class=\"fm-code-in-text\">CoordinatingTagHelpers.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/TagHelpers<\/code> folder and add the code shown in listing 25.26.<a id=\"calibre_link-2357\"><\/a><a id=\"calibre_link-2358\"><\/a><a id=\"calibre_link-1182\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.26 The CoordinatingTagHelpers.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    [HtmlTargetElement(\"tr\", Attributes = \"theme\")]\n    public class RowTagHelper : TagHelper {\n        \n        public string Theme { get; set; } = String.Empty;\n                \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n            context.Items[\"theme\"] = Theme;\n        }\n    }\n        \n    [HtmlTargetElement(\"th\")]\n    [HtmlTargetElement(\"td\")]\n    public class CellTagHelper : TagHelper {\n        \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n                                \n            if (context.Items.ContainsKey(\"theme\")) {\n                output.Attributes.SetAttribute(\"class\",\n                    $\"bg-{context.Items[\"theme\"]} text-white\");\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The first tag helper operates on <code class=\"fm-code-in-text\">tr<\/code> elements that have a <code class=\"fm-code-in-text\">theme<\/code> attribute. Coordinating tag helpers can transform their own elements, but this example simply adds the value of the <code class=\"fm-code-in-text\">theme<\/code> attribute to the <code class=\"fm-code-in-text\">Items<\/code> dictionary so that it is available to tag helpers that operate on elements contained within the <code class=\"fm-code-in-text\">tr<\/code> element. The second tag helper operates on <code class=\"fm-code-in-text\">th<\/code> and <code class=\"fm-code-in-text\">td<\/code> elements and uses the <code class=\"fm-code-in-text\">theme<\/code> value from the <code class=\"fm-code-in-text\">Items<\/code> dictionary to set the Bootstrap style for its output elements.<\/p>\n<p class=\"body\">Listing 25.27 adds elements to the <code class=\"fm-code-in-text\">Home<\/code> controller\u2019s <code class=\"fm-code-in-text\">Index<\/code> view that apply the coordinating tag helpers.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Notice that I have added the <code class=\"fm-code-in-text1\">th<\/code> and <code class=\"fm-code-in-text1\">td<\/code> elements that are transformed in listing 25.27, instead of relying on a tag helper to generate them. Tag helpers are not applied to elements generated by other tag helpers and affect only the elements defined in the view.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.27 Applying a tag helper in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  \n    Layout = \"_SimpleLayout\";\n}\n\n&lt;div route-data=\"true\"&gt;&lt;\/div&gt;\n\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    &lt;tablehead bg-color=\"dark\"&gt;Product Summary&lt;\/tablehead&gt;    \n    &lt;tbody&gt;\n        <b class=\"fm-bold\">&lt;tr theme=\"primary\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n        <b class=\"fm-bold\">&lt;tr theme=\"secondary\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n        <b class=\"fm-bold\">&lt;tr theme=\"info\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;th&gt;Category&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home, which produces the response shown in figure 25.12. The value of the <code class=\"fm-code-in-text\">theme<\/code> element has been passed from one tag helper to another, and a color theme is applied without needing to define attributes on each of the elements that is transformed.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre261\" src=\"\/images\/proaspnetcore7\/000268.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.12 Coordination between tag helpers<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-478\">25.3.7 Suppressing the output element<\/h3>\n<p class=\"body\">Tag helpers can be used to prevent an element from being included in the HTML response by calling the <code class=\"fm-code-in-text\">SuppressOuput<\/code> method on the <code class=\"fm-code-in-text\">TagHelperOutput<\/code> object that is received as an argument to the <code class=\"fm-code-in-text\">Process<\/code> method. In listing 25.28, I have added an element to the <code class=\"fm-code-in-text\">Home<\/code> controller\u2019s <code class=\"fm-code-in-text\">Index<\/code> view that should be displayed only if the <code class=\"fm-code-in-text\">Price<\/code> property of the view model exceeds a specified value.<a id=\"calibre_link-2359\"><\/a><a id=\"calibre_link-1191\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.28 Adding an element in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  \n    Layout = \"_SimpleLayout\";\n}\n\n<b class=\"fm-bold\">&lt;div show-when-gt=\"500\" for=\"Price\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;h5 class=\"bg-danger text-white text-center p-2\"&gt;<\/b>\n        <b class=\"fm-bold\">Warning: Expensive Item<\/b>\n    <b class=\"fm-bold\">&lt;\/h5&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    &lt;tablehead bg-color=\"dark\"&gt;Product Summary&lt;\/tablehead&gt;    \n    &lt;tbody&gt;\n        &lt;tr theme=\"primary\"&gt;\n            &lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model?.Name&lt;\/td&gt;\n        &lt;\/tr&gt;\n        &lt;tr theme=\"secondary\"&gt;\n            &lt;th&gt;Price&lt;\/th&gt;&lt;td&gt;@Model?.Price.ToString(\"c\")&lt;\/td&gt;\n        &lt;\/tr&gt;\n        &lt;tr theme=\"info\"&gt;\n            &lt;th&gt;Category&lt;\/th&gt;&lt;td&gt;@Model?.CategoryId&lt;\/td&gt;\n        &lt;\/tr&gt;\n    &lt;\/tbody&gt;\n&lt;\/table&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">show-when-gt<\/code> attribute specifies the value above which the <code class=\"fm-code-in-text\">div<\/code> element should be displayed, and the <code class=\"fm-code-in-text\">for<\/code> property selects the model property that will be inspected. To create the tag helper that will manage the elements, including the response, add a class file named <code class=\"fm-code-in-text\">SelectiveTagHelper.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/TagHelpers<\/code> folder with the code shown in listing 25.29.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.29 The contents of the SelectiveTagHelper.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.ViewFeatures;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    [HtmlTargetElement(\"div\", Attributes = \"show-when-gt, for\")]\n    public class SelectiveTagHelper : TagHelper {\n        \n        public decimal ShowWhenGt { get; set; }\n                \n        public ModelExpression? For { get; set; }\n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n                                \n            if (For?.Model.GetType() == typeof(decimal)\n                    &amp;&amp; (decimal)For.Model &lt;= ShowWhenGt) {\n                output.SuppressOutput();\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The tag helper uses the model expression to access the property and calls the <code class=\"fm-code-in-text\">SuppressOutput<\/code> method unless the threshold is exceeded. To see the effect, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/index\/1 and http:\/\/localhost:5000\/home\/index\/5. The value for the <code class=\"fm-code-in-text\">Price<\/code> property of the <code class=\"fm-code-in-text\">Product<\/code> selected by the first URL is less than the threshold, so the element is suppressed. The value for the <code class=\"fm-code-in-text\">Price<\/code> property of the <code class=\"fm-code-in-text\">Product<\/code> selected by the second URL is more than the threshold, so the element is displayed. figure 25.13 shows both responses.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre262\" src=\"\/images\/proaspnetcore7\/000269.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.13 Suppressing output elements<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-479\">25.4 Using tag helper components<\/h2>\n<p class=\"body\"><i class=\"fm-italics\">Tag helper components<\/i> provide an alternative approach to applying tag helpers as services. This feature can be useful when you need to set up tag helpers to support another service or middleware component, which is typically the case for diagnostic tools or functionality that has both a client-side component and a server-side component, such as Blazor, which is described in part 4. In the sections that follow, I show you how to create and apply tag helper components.<a id=\"calibre_link-2360\"><\/a><a id=\"calibre_link-2361\"><\/a><a id=\"calibre_link-1171\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-480\">25.4.1 Creating a tag helper component<\/h3>\n<p class=\"body\">Tag helper components are derived from the <code class=\"fm-code-in-text\">TagHelperComponent<\/code> class, which provides a similar API to the <code class=\"fm-code-in-text\">TagHelper<\/code> base class used in earlier examples. To create a tag helper component, add a class file called <code class=\"fm-code-in-text\">TimeTagHelperComponent.cs<\/code> in the <code class=\"fm-code-in-text\">TagHelpers<\/code> folder with the content shown in listing 25.30.<a id=\"calibre_link-2362\"><\/a><a id=\"calibre_link-2363\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.30 The TimeTagHelperComponent.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    public class TimeTagHelperComponent : TagHelperComponent {\n        \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n                                \n            string timestamp = DateTime.Now.ToLongTimeString();\n                        \n            if (output.TagName == \"body\") {\n                TagBuilder elem = new TagBuilder(\"div\");\n                elem.Attributes.Add(\"class\", \n                    \"bg-info text-white m-2 p-2\");\n                elem.InnerHtml.Append($\"Time: {timestamp}\");\n                output.PreContent.AppendHtml(elem);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">Tag helper components do not specify the elements they transform, and the <code class=\"fm-code-in-text\">Process<\/code> method is invoked for every element for which the tag helper component feature has been configured. By default, tag helper components are applied to transform <code class=\"fm-code-in-text\">head<\/code> and <code class=\"fm-code-in-text\">body<\/code> elements. This means that tag helper component classes must check the <code class=\"fm-code-in-text\">TagName<\/code> property of the output element to ensure they perform only their intended transformations. The tag helper component in listing 25.30 looks for <code class=\"fm-code-in-text\">body<\/code> elements and uses the <code class=\"fm-code-in-text\">PreContent<\/code> property to insert a <code class=\"fm-code-in-text\">div<\/code> element containing a timestamp before the rest of the element\u2019s content.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> I show you how to increase the range of elements handled by tag helper components in the next section.<\/p>\n<p class=\"body\">Tag helper components are registered as services that implement the <code class=\"fm-code-in-text\">ITagHelperComponent<\/code> interface, as shown in listing 25.31.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.31 Registering a component in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Razor.TagHelpers;<\/b>\n<b class=\"fm-bold\">using WebApp.TagHelpers;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddSingleton&lt;CitiesData&gt;();\n<b class=\"fm-bold\">builder.Services.AddTransient&lt;ITagHelperComponent,<\/b> \n    <b class=\"fm-bold\">TimeTagHelperComponent&gt;();<\/b>\n        \nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapControllers();\napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddTransient<\/code> method is used to ensure that each request is handled using its own instance of the tag helper component class. To see the effect of the tag helper component, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home. This response&mdash;and all other HTML responses from the application&mdash;contain the content generated by the tag helper component, as shown in figure 25.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre263\" src=\"\/images\/proaspnetcore7\/000270.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.14 Using a tag helper component<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-481\">25.4.2 Expanding tag helper component element selection<\/h3>\n<p class=\"body\">By default, only the <code class=\"fm-code-in-text\">head<\/code> and <code class=\"fm-code-in-text\">body<\/code> elements are processed by the tag helper components, but additional elements can be selected by creating a class derived from the terribly named <code class=\"fm-code-in-text\">TagHelperComponentTagHelper<\/code> class. Add a class file named <code class=\"fm-code-in-text\">TableFooterTagHelperComponent.cs<\/code> to the <code class=\"fm-code-in-text\">TagHelpers<\/code> folder and use it to define the classes shown in listing 25.32.<a id=\"calibre_link-2364\"><\/a><a id=\"calibre_link-2365\"><\/a><a id=\"calibre_link-1193\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.32 The TableFooterTagHelperComponent.cs file in the TagHelpers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing Microsoft.AspNetCore.Razor.TagHelpers;\n\nnamespace WebApp.TagHelpers {\n\n    [HtmlTargetElement(\"table\")]\n    public class TableFooterSelector : TagHelperComponentTagHelper {\n        \n        public TableFooterSelector(ITagHelperComponentManager mgr,\n            ILoggerFactory log) : base(mgr, log) { }\n    }\n        \n    public class TableFooterTagHelperComponent : TagHelperComponent {\n        \n        public override void Process(TagHelperContext context,\n                TagHelperOutput output) {\n                                \n            if (output.TagName == \"table\") {\n                TagBuilder cell = new TagBuilder(\"td\");\n                cell.Attributes.Add(\"colspan\", \"2\");\n                cell.Attributes.Add(\"class\", \n                    \"bg-dark text-white text-center\");\n                cell.InnerHtml.Append(\"Table Footer\");\n                TagBuilder row = new TagBuilder(\"tr\");\n                row.InnerHtml.AppendHtml(cell);\n                TagBuilder footer = new TagBuilder(\"tfoot\");\n                footer.InnerHtml.AppendHtml(row);\n                output.PostContent.AppendHtml(footer);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">TableFooterSelector<\/code> class is derived from <code class=\"fm-code-in-text\">TagHelperComponentTagHelper<\/code>, and it is decorated with the <code class=\"fm-code-in-text\">HtmlTargetElement<\/code> attribute that expands the range of elements processed by the application\u2019s tag helper components. In this case, the attribute selects <code class=\"fm-code-in-text\">table<\/code> elements.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">TableFooterTagHelperComponent<\/code> class, defined in the same file, is a tag helper component that transforms <code class=\"fm-code-in-text\">table<\/code> elements by adding a <code class=\"fm-code-in-text\">tfoot<\/code> element, which represents a table footer.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Bear in mind that when you create a new <code class=\"fm-code-in-text1\">TagHelperComponentTagHelper<\/code>, all the tag helper components will receive the elements selected by the <code class=\"fm-code-in-text1\">HtmlTargetAttribute<\/code> element.<\/p>\n<p class=\"body\">The tag helper component must be registered as a service to receive elements for transformation, but the tag helper component tag helper (which is one of the worst naming choices I have seen for some years) is discovered and applied automatically. Listing 25.33 adds the tag helper component service.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 25.33 Registering a component in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">...\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddSingleton&lt;CitiesData&gt;();\nbuilder.Services.AddTransient&lt;ITagHelperComponent, \n    TimeTagHelperComponent&gt;();\n<b class=\"fm-bold\">builder.Services.AddTransient&lt;ITagHelperComponent,<\/b> \n    <b class=\"fm-bold\">TableFooterTagHelperComponent&gt;();<\/b>\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request a URL that renders a table, such as http:\/\/localhost:5000\/home or http:\/\/localhost:5000\/cities. Each table will contain a table footer, as shown in figure 25.15.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre264\" src=\"\/images\/proaspnetcore7\/000271.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 25.15 Expanding tag helper component element selection<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-482\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Tag helpers are C# classes that transform HTML elements in a response or replace a shorthand element with standard HTML content.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Tag helpers can be configured using attributes, which are received through a <code class=\"fm-code-in-text\">TagHelperContext<\/code> object.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Tag helpers must be registered in the view imports file using the <code class=\"fm-code-in-text\">@addTagHelper<\/code> directive.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The scope of a tag helper can be controlled with the <code class=\"fm-code-in-text\">HTMLTargetElement<\/code> attribute, which allows the elements that are transformed to be specified precisely.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Tag helpers can use model expressions to generate content for the view model of the view to which they are applied.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Tag helpers can be registered as services for dependency injection, using the tag helper component function.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-483\">\n<div class=\"calibre1\" id=\"calibre_link-2366\">\n<h1 class=\"tochead\" id=\"calibre_link-2367\"><a id=\"calibre_link-2368\"><\/a><a id=\"calibre_link-2369\"><\/a>26 Using the built-in tag helpers<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Creating anchor elements that target actions and Razor Pages<\/li>\n<li class=\"co-summary-bullet\">Managing JavaScript and CSS files<\/li>\n<li class=\"co-summary-bullet\">Working with image elements<\/li>\n<li class=\"co-summary-bullet\">Caching fragments of content<\/li>\n<li class=\"co-summary-bullet\">Generating content based on the hosting environment<\/li>\n<\/ul>\n<p class=\"body\">ASP.NET Core provides a set of built-in tag helpers that apply the most commonly required element transformations. In this chapter, I explain those tag helpers that deal with anchor, <code class=\"fm-code-in-text\">script<\/code>, <code class=\"fm-code-in-text\">link<\/code>, and image elements, as well as features for caching content and selecting content based on the environment. In chapter 27, I describe the tag helpers that support HTML forms. Table 26.1 puts the built-in tag helpers in context.<\/p>\n<p class=\"fm-table-caption\">Table 26.1 Putting the built-in tag helpers in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2370\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The built-in tag helpers perform commonly required transformations on HTML elements.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Using the built-in tag helpers means you don\u2019t have to create custom helpers using the techniques in chapter 25.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The tag helpers are applied using attributes on standard HTML elements or through custom HTML elements.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No, these tag helpers are well-tested and easy to use. Unless you have unusual needs, using these tag helpers is preferable to custom implementation.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">These tag helpers are optional, and their use is not required.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 26.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 26.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2371\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating elements that target endpoints<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the anchor element tag helper attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">7, 8<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Including JavaScript files in a response<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the JavaScript tag helper attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">9&ndash;13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Including CSS files in a response<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the CSS tag helper attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14, 15<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Managing image caching<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the image tag helper attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">16<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Caching sections of a view<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the caching tag helper.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">17&ndash;21<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Varying content based on the application environment<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the environment tag helper.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">22<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-484\">26.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the WebApp project from chapter 25. To prepare for this chapter, comment out the statements that register the tag component helpers, as shown in listing 26.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.1 The contents of the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n<b class=\"fm-bold\">\/\/using Microsoft.AspNetCore.Razor.TagHelpers;<\/b>\n<b class=\"fm-bold\">\/\/using WebApp.TagHelpers;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddSingleton&lt;CitiesData&gt;();\n<b class=\"fm-bold\">\/\/builder.Services.AddTransient&lt;ITagHelperComponent,<\/b> \n<b class=\"fm-bold\">\/\/    TimeTagHelperComponent&gt;();<\/b>\n<b class=\"fm-bold\">\/\/builder.Services.AddTransient&lt;ITagHelperComponent,<\/b> \n<b class=\"fm-bold\">\/\/    TableFooterTagHelperComponent&gt;();<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapControllers();\napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">Next, update the <code class=\"fm-code-in-text\">_RowPartial.cshtml<\/code> partial view in the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder, making the changes shown in listing 26.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.2 Making changes in the _RowPartial.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model Product\n\n<b class=\"fm-bold\">&lt;tr&gt;<\/b>\n    <b class=\"fm-bold\">&lt;td&gt;@Model.Name&lt;\/td&gt;<\/b>\n    <b class=\"fm-bold\">&lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;<\/b>\n    <b class=\"fm-bold\">&lt;td&gt;@Model.CategoryId&lt;\/td&gt;<\/b>\n    <b class=\"fm-bold\">&lt;td&gt;@Model.SupplierId&lt;\/td&gt;<\/b>\n    <b class=\"fm-bold\">&lt;td&gt;&lt;\/td&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/tr&gt;<\/b><\/pre>\n<p class=\"body\">Replace the contents of the <code class=\"fm-code-in-text\">List.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder, which applies a layout and adds additional columns to the table rendered by the view, as shown in listing 26.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.3 Making changes in the List.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;Product&gt;\n<b class=\"fm-bold\">@{ Layout = \"_SimpleLayout\"; }<\/b>\n\n<b class=\"fm-bold\">&lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;Products&lt;\/h6&gt;<\/b>\n<b class=\"fm-bold\">&lt;div class=\"m-2\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;table class=\"table table-sm table-striped table-bordered\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;thead&gt;<\/b>\n            <b class=\"fm-bold\">&lt;tr&gt;<\/b>\n                <b class=\"fm-bold\">&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;<\/b>\n                <b class=\"fm-bold\">&lt;th&gt;Category&lt;\/th&gt;&lt;th&gt;Supplier&lt;\/th&gt;&lt;th&gt;&lt;\/th&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/thead&gt;<\/b>\n        <b class=\"fm-bold\">&lt;tbody&gt;<\/b>\n            <b class=\"fm-bold\">@foreach (Product p in Model) {<\/b>\n                <b class=\"fm-bold\">&lt;partial name=\"_RowPartial\" model=\"p\" \/&gt;<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">&lt;\/tbody&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/table&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b><\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-485\">26.1.1 Adding an image file<\/h3>\n<p class=\"body\">One of the tag helpers described in this chapter provides services for images. I created the <code class=\"fm-code-in-text\">wwwroot\/images<\/code> folder and added an image file called <code class=\"fm-code-in-text\">city.png<\/code>. This is a public domain panorama of the New York City skyline, as shown in figure 26.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre265\" src=\"\/images\/proaspnetcore7\/000272.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 26.1 Adding an image to the project<\/p>\n<\/div>\n<p class=\"body\">This image file is included in the source code for this chapter, which is available in the GitHub repository for this book. You can substitute your own image if you don\u2019t want to download the example project.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-486\">26.1.2 Installing a client-side package<\/h3>\n<p class=\"body\">Some of the examples in this chapter demonstrate the tag helper support for working with JavaScript files, for which I use the jQuery package. Use a PowerShell command prompt to run the command shown in listing 26.4 in the project folder, which contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file<a id=\"calibre_link-2372\"><\/a>.<a id=\"calibre_link-1013\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.4 Installing a package<\/p>\n<pre class=\"programlisting\">libman install jquery@3.6.3 -d wwwroot\/lib\/jquery<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-487\">26.1.3 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 26.5 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.5 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-488\">26.1.4 Running the example application<\/h3>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 26.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.6 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/home\/list, which will display a list of products, as shown in figure 26.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre266\" src=\"\/images\/proaspnetcore7\/000273.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 26.2 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-489\">26.2 Enabling the built-in tag helpers<\/h2>\n<p class=\"body\">The built-in tag helpers are all defined in the <code class=\"fm-code-in-text\">Microsoft.AspNetCore.Mvc.TagHelpers<\/code> namespace and are enabled by adding an <code class=\"fm-code-in-text\">@addTagHelpers<\/code> directive to individual views or pages or, as in the case of the example project, to the view imports file. Here is the required directive from the <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views<\/code> folder, which enables the built-in tag helpers for controller views:<\/p>\n<pre class=\"programlisting\">@using WebApp.Models\n<b class=\"fm-bold\">@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers<\/b>\n@using WebApp.Components\n@addTagHelper *, WebApp<\/pre>\n<p class=\"body\">Here is the corresponding directive in the <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Pages<\/code> folder, which enables the built-in tag helpers for Razor Pages:<\/p>\n<pre class=\"programlisting\">@namespace WebApp.Pages\n@using WebApp.Models\n<b class=\"fm-bold\">@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers<\/b>\n@addTagHelper *, WebApp<\/pre>\n<p class=\"body\">These directives were added to the example project in chapter 24 to enable the view components feature.<a id=\"calibre_link-2373\"><\/a><a id=\"calibre_link-2374\"><\/a><\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-490\">26.3 Transforming anchor elements<\/h2>\n<p class=\"body\">The <code class=\"fm-code-in-text\">a<\/code> element is the basic tool for navigating around an application and sending GET requests to the application. The <code class=\"fm-code-in-text\">AnchorTagHelper<\/code> class is used to transform the <code class=\"fm-code-in-text\">href<\/code> attributes of <code class=\"fm-code-in-text\">a<\/code> elements so they target URLs generated using the routing system, which means that hard-coded URLs are not required and a change in the routing configuration will be automatically reflected in the application\u2019s anchor elements. Table 26.3 describes the attributes the <code class=\"fm-code-in-text\">AnchorTagHelper<\/code> class supports.<a id=\"calibre_link-2375\"><\/a><a id=\"calibre_link-1172\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 26.3 The built-in tag helper attributes for anchor elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2376\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-action<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies the action method that the URL will target.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-controller<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies the controller that the URL will target. If this attribute is omitted, then the URL will target the controller or page that rendered the current view.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-page<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies the Razor Page that the URL will target.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-page-handler<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies the Razor Page handler function that will process the request, as described in chapter 23.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fragment<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the URL fragment (which appears after the <code class=\"fm-code-in-text1\">#<\/code> character).<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-host<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies the name of the host that the URL will target.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-protocol<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies the protocol that the URL will use.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-route<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies the name of the route that will be used to generate the URL.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-route-*<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Attributes whose name begins with <code class=\"fm-code-in-text1\">asp-route-<\/code> are used to specify additional values for the URL so that the <code class=\"fm-code-in-text1\">asp-route-id<\/code> attribute is used to provide a value for the <code class=\"fm-code-in-text1\">id<\/code> segment to the routing system.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-all-route-data<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute provides values used for routing as a single value, rather than using individual attributes.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AnchorTagHelper<\/code> is simple and predictable and makes it easy to generate URLs in <code class=\"fm-code-in-text\">a<\/code> elements that use the application\u2019s routing configuration. Listing 26.7 adds an anchor element that uses attributes from the table to create a URL that targets another action defined by the <code class=\"fm-code-in-text\">Home<\/code> controller.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.7 Transforming an element in the Views\/Home\/_RowPartial.cshtml file<\/p>\n<pre class=\"programlisting\">@model Product\n\n&lt;tr&gt;\n    &lt;td&gt;@Model.Name&lt;\/td&gt;\n    &lt;td&gt;@Model.Price.ToString(\"c\")&lt;\/td&gt;\n    &lt;td&gt;@Model.CategoryId&lt;\/td&gt;\n    &lt;td&gt;@Model.SupplierId&lt;\/td&gt;\n    <b class=\"fm-bold\">&lt;td&gt;<\/b>\n        <b class=\"fm-bold\">&lt;a asp-action=\"index\" asp-controller=\"home\"<\/b> \n            <b class=\"fm-bold\">asp-route-id=\"@Model?.ProductId\"<\/b>\n                <b class=\"fm-bold\">class=\"btn btn-sm btn-info text-white\"&gt;<\/b>\n            <b class=\"fm-bold\">Select<\/b>\n        <b class=\"fm-bold\">&lt;\/a&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/td&gt;<\/b>\n&lt;\/tr&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-action<\/code> and <code class=\"fm-code-in-text\">asp-controller<\/code> attributes specify the name of the action method and the controller that defines it. Values for segment variables are defined using <code class=\"fm-code-in-text\">asp-route-[name]<\/code> attributes, such that the <code class=\"fm-code-in-text\">asp-route-id<\/code> attribute provides a value for the <code class=\"fm-code-in-text\">id<\/code> segment variable that is used to provide an argument for the action method selected by the <code class=\"fm-code-in-text\">asp-action<\/code> attribute.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">class<\/code> attributes added to the anchor elements in listing 26.7 apply Bootstrap CSS Framework styles that give the elements the appearance of buttons. This is not a requirement for using the tag helper.<\/p>\n<p class=\"body\">To see the anchor element transformations, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/list, which will produce the response shown in figure 26.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre267\" src=\"\/images\/proaspnetcore7\/000274.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 26.3 Transforming anchor elements<\/p>\n<\/div>\n<p class=\"body\">If you examine the <code class=\"fm-code-in-text\">Select<\/code> anchor elements, you will see that each <code class=\"fm-code-in-text\">href<\/code> attribute includes the <code class=\"fm-code-in-text\">ProductId<\/code> value of the <code class=\"fm-code-in-text\">Product<\/code> object it relates to, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;a class=\"btn btn-sm btn-info text-white\" href=\"<b class=\"fm-bold\">\/Home\/index\/3<\/b>\"&gt;Select&lt;\/a&gt;\n...<\/pre>\n<p class=\"body\">In this case, the value provided by the <code class=\"fm-code-in-text\">asp-route-id<\/code> attribute means the default URL cannot be used, so the routing system has generated a URL that includes segments for the controller and action name, as well as a segment that will be used to provide a parameter to the action method. Clicking the anchor elements will send an HTTP GET request that targets the <code class=\"fm-code-in-text\">Home<\/code> controller\u2019s <code class=\"fm-code-in-text\">Index<\/code> method.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-491\">26.3.1 Using anchor elements for Razor Pages<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-page<\/code> attribute is used to specify a Razor Page as the target for an anchor element\u2019s <code class=\"fm-code-in-text\">href<\/code> attribute. The path to the page is prefixed with the <code class=\"fm-code-in-text\">\/<\/code> character, and values for route segments defined by the <code class=\"fm-code-in-text\">@page<\/code> directive are defined using <code class=\"fm-code-in-text\">asp-route-[name]<\/code> attributes. Listing 26.8 adds an anchor element that targets the <code class=\"fm-code-in-text\">List<\/code> page defined in the <code class=\"fm-code-in-text\">Pages\/Suppliers<\/code> folder.<a id=\"calibre_link-2377\"><\/a><a id=\"calibre_link-1173\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The <code class=\"fm-code-in-text1\">asp-page-handler<\/code> attribute can be used to specify the name of the page model handler method that will process the request.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.8 Targeting a Razor Page in the List.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;Product&gt;\n@{ Layout = \"_SimpleLayout\"; }\n\n&lt;h6 class=\"bg-secondary text-white text-center m-2 p-2\"&gt;Products&lt;\/h6&gt;\n&lt;div class=\"m-2\"&gt;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;thead&gt;\n            &lt;tr&gt;\n                &lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;\n                &lt;th&gt;Category&lt;\/th&gt;&lt;th&gt;Supplier&lt;\/th&gt;&lt;th&gt;&lt;\/th&gt;\n            &lt;\/tr&gt;\n        &lt;\/thead&gt;\n        &lt;tbody&gt;\n            @foreach (Product p in Model) {\n                &lt;partial name=\"_RowPartial\" model=\"p\" \/&gt;\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n    <b class=\"fm-bold\">&lt;a asp-page=\"\/suppliers\/list\" class=\"btn btn-secondary\"&gt;Suppliers&lt;\/a&gt;<\/b>\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and a browser to request http:\/\/localhost:5000\/home\/list, and you will see the anchor element, which is styled to appear as a button. If you examine the HTML sent to the client, you will see the anchor element has been transformed like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;a class=\"btn btn-secondary\" <b class=\"fm-bold\">href=\"\/lists\/suppliers\"<\/b>&gt;Suppliers&lt;\/a&gt;\n...<\/pre>\n<p class=\"body\">This URL used in the <code class=\"fm-code-in-text\">href<\/code> attribute reflects the <code class=\"fm-code-in-text\">@page<\/code> directive, which has been used to override the default routing convention in this page. Click the element, and the browser will display the Razor Page, as shown in figure 26.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre268\" src=\"\/images\/proaspnetcore7\/000275.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 26.4 Targeting a Razor Page with an anchor element<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Generating URLs (and NOT LINKS)<\/p>\n<p class=\"fm-sidebar-text\">The tag helper generates URLs only in anchor elements. If you need to generate a URL, rather than a link, then you can use the <code class=\"fm-code-in-text1\">Url<\/code> property, which is available in controllers, page models, and views. This property returns an object that implements the <code class=\"fm-code-in-text1\">IUrlHelper<\/code> interface, which provides a set of methods and extension methods that generate URLs. Here is a Razor fragment that generates a URL in a view:<a id=\"calibre_link-2378\"><\/a><a id=\"calibre_link-2379\"><\/a><a id=\"calibre_link-2380\"><\/a><a id=\"calibre_link-2381\"><\/a><a id=\"calibre_link-1106\"><\/a><\/p>\n<pre class=\"programlisting\">...\n&lt;div&gt;@Url.Page(\"\/suppliers\/list\")&lt;\/div&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">This fragment produces a <code class=\"fm-code-in-text1\">div<\/code> element whose content is the URL that targets the <code class=\"fm-code-in-text1\">\/Suppliers\/List<\/code> Razor Page. The same interface is used in controllers or page model classes, such as with this statement:<\/p>\n<pre class=\"programlisting\">...\nstring url = Url.Action(\"List\", \"Home\");\n...<\/pre>\n<p class=\"fm-sidebar-text\">The statement generates a URL that targets the <code class=\"fm-code-in-text1\">List<\/code> action on the <code class=\"fm-code-in-text1\">Home<\/code> controller and assigns it to the <code class=\"fm-code-in-text1\">string<\/code> variable named <code class=\"fm-code-in-text1\">url<\/code>.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-492\">26.4 Using the JavaScript and CSS tag helpers<\/h2>\n<p class=\"body\">ASP.NET Core provides tag helpers that are used to manage JavaScript files and CSS stylesheets through the <code class=\"fm-code-in-text\">script<\/code> and <code class=\"fm-code-in-text\">link<\/code> elements. As you will see in the sections that follow, these tag helpers are powerful and flexible but require close attention to avoid creating unexpected results.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-493\">26.4.1 Managing JavaScript files<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ScriptTagHelper<\/code> class is the built-in tag helper for <code class=\"fm-code-in-text\">script<\/code> elements and is used to manage the inclusion of JavaScript files in views using the attributes described in table 26.4, which I describe in the sections that follow.<a id=\"calibre_link-2382\"><\/a><a id=\"calibre_link-2383\"><\/a><a id=\"calibre_link-1000\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 26.4 The built-in tag helper attributes for script elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2384\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-src-include<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify JavaScript files that will be included in the view.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-src-exclude<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify JavaScript files that will be excluded from the view.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-append-version<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used for cache busting, as described in the \u201cUnderstanding Cache Busting\u201d sidebar.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fallback-src<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify a fallback JavaScript file to use if there is a problem with a content delivery network.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fallback-src-include<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to select JavaScript files that will be used if there is a content delivery network problem.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fallback-src-exclude<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to exclude JavaScript files to present their use when there is a content delivery network problem.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fallback-test<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify a fragment of JavaScript that will be used to determine whether JavaScript code has been correctly loaded from a content delivery network.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"fm-head2\">Selecting JavaScript files<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-src-include<\/code> attribute is used to include JavaScript files in a view using globbing patterns. Globbing patterns support a set of wildcards that are used to match files, and table 26.5 describes the most common globbing patterns.<a id=\"calibre_link-2385\"><\/a><a id=\"calibre_link-2386\"><\/a><a id=\"calibre_link-2387\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 26.5 Common globbing patterns<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2388\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Pattern<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Example<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">?<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">js\/src?.js<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This pattern matches any single character except <code class=\"fm-code-in-text1\">\/<\/code>. The example matches any file contained in the <code class=\"fm-code-in-text1\">js<\/code> directory whose name is <code class=\"fm-code-in-text1\">src<\/code>, followed by any character, followed by <code class=\"fm-code-in-text1\">.js<\/code>, such as <code class=\"fm-code-in-text1\">js\/src1.js<\/code> and <code class=\"fm-code-in-text1\">js\/srcX.js<\/code> but not <code class=\"fm-code-in-text1\">js\/src123.js<\/code> or <code class=\"fm-code-in-text1\">js\/mydir\/src1.js<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">*<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">js\/*.js<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This pattern matches any number of characters except <code class=\"fm-code-in-text1\">\/<\/code>. The example matches any file contained in the <code class=\"fm-code-in-text1\">js<\/code> directory with the <code class=\"fm-code-in-text1\">.js<\/code> file extension, such as <code class=\"fm-code-in-text1\">js\/src1.js<\/code> and <code class=\"fm-code-in-text1\">js\/src123.js<\/code> but not <code class=\"fm-code-in-text1\">js\/mydir\/src1.js<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">**<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">js\/**\/*.js<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This pattern matches any number of characters including <code class=\"fm-code-in-text1\">\/<\/code>. The example matches any file with the <code class=\"fm-code-in-text1\">.js<\/code> extension that is contained within the <code class=\"fm-code-in-text1\">js<\/code> directory or any subdirectory, such as <code class=\"fm-code-in-text1\">\/js\/src1.js<\/code> and <code class=\"fm-code-in-text1\">\/js\/mydir\/src1.js<\/code>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Globbing is a useful way of ensuring that a view includes the JavaScript files that the application requires, even when the exact path to the file changes, which usually happens when the version number is included in the file name or when a package adds additional files.<\/p>\n<p class=\"body\">Listing 26.9 uses the <code class=\"fm-code-in-text\">asp-src-include<\/code> attribute to include all the JavaScript files in the <code class=\"fm-code-in-text\">wwwroot\/lib\/jquery<\/code> folder, which is the location of the jQuery package installed with the command in listing 26.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.9 Selecting files in the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    <b class=\"fm-bold\">&lt;script asp-src-include=\"lib\/jquery\/**\/*.js\"&gt;&lt;\/script&gt;<\/b>\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Patterns are evaluated within the <code class=\"fm-code-in-text\">wwwroot<\/code> folder, and the pattern I used locates any file with the <code class=\"fm-code-in-text\">js<\/code> file extension, regardless of its location within the <code class=\"fm-code-in-text\">wwwroot<\/code> folder; this means that any JavaScript package added to the project will be included in the HTML sent to the client.<\/p>\n<p class=\"body\">Restart ASP.NET Core and a browser to request http:\/\/localhost:5000\/home\/list and examine the HTML sent to the browser. You will see the single <code class=\"fm-code-in-text\">script<\/code> element in the layout has been transformed into a <code class=\"fm-code-in-text\">script<\/code> element for each JavaScript file, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;head&gt;\n  &lt;title&gt;&lt;\/title&gt;\n  &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\"&gt;\n  <b class=\"fm-bold\">&lt;script src=\"\/lib\/jquery\/jquery.js\"&gt;&lt;\/script&gt;<\/b>\n  <b class=\"fm-bold\">&lt;script src=\"\/lib\/jquery\/jquery.min.js\"&gt;&lt;\/script&gt;<\/b>\n  <b class=\"fm-bold\">&lt;script src=\"\/lib\/jquery\/jquery.slim.js\"&gt;&lt;\/script&gt;<\/b>\n  <b class=\"fm-bold\">&lt;script src=\"\/lib\/jquery\/jquery.slim.min.js\"&gt;&lt;\/script&gt;<\/b> \n&lt;\/head&gt;\n...<\/pre>\n<p class=\"body\">If you are using Visual Studio, you may not have realized that the jQuery packages contain so many JavaScript files because Visual Studio hides them in the Solution Explorer. To reveal the full contents of the client-side package folders, you can either expand the individual nested entries in the Solution Explorer window or disable file nesting by clicking the button at the top of the Solution Explorer window, as shown in figure 26.5. (Visual Studio Code does not nest files.)<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre269\" src=\"\/images\/proaspnetcore7\/000276.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 26.5 Disabling file nesting in the Visual Studio Solution Explorer<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding source maps<\/p>\n<p class=\"fm-sidebar-text\">JavaScript files are minified to make them smaller, which means they can be delivered to the client faster and using less bandwidth. The minification process removes all the whitespace from the file and renames functions and variables so that meaningful names such as <code class=\"fm-code-in-text1\">myHelpfullyNamedFunction<\/code> will be represented by a smaller number of characters, such as <code class=\"fm-code-in-text1\">x1<\/code>. When using the browser\u2019s JavaScript debugger to track down problems in your minified code, names like <code class=\"fm-code-in-text1\">x1<\/code> make it almost impossible to follow progress through the code.<a id=\"calibre_link-2389\"><\/a><a id=\"calibre_link-1150\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">The files that have the <code class=\"fm-code-in-text1\">map<\/code> file extension are <i class=\"fm-italics\">source maps<\/i>, which browsers use to help debug minified code by providing a map between the minified code and the developer-readable, unminified source file. When you open the browser\u2019s F12 developer tools, the browser will automatically request source maps and use them to help debug the application\u2019s client-side code.<\/p>\n<\/div>\n<p class=\"fm-head2\">Narrowing the globbing pattern<\/p>\n<p class=\"body\">No application would require all the files selected by the pattern in listing 26.9. Many packages include multiple JavaScript files that contain similar content, often removing less popular features to save bandwidth. The jQuery package includes the <code class=\"fm-code-in-text\">jquery.slim.js<\/code> file, which contains the same code as the <code class=\"fm-code-in-text\">jquery.js<\/code> file but without the features that handle asynchronous HTTP requests and animation effects.<\/p>\n<p class=\"body\">Each of these files has a counterpart with the <code class=\"fm-code-in-text\">min.js<\/code> file extension, which denotes a minified file. Minification reduces the size of a JavaScript file by removing all whitespace and renaming functions and variables to use shorter names.<\/p>\n<p class=\"body\">Only one JavaScript file is required for each package and if you only require the minified versions, which will be the case in most projects, then you can restrict the set of files that the globbing pattern matches, as shown in listing 26.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.10 Selecting files in the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    <b class=\"fm-bold\">&lt;script asp-src-include=\"lib\/jquery\/**\/*.min.js\"&gt;&lt;\/script&gt;<\/b>\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and a browser to request http:\/\/localhost:5000\/home\/list again and examine the HTML sent by the application. You will see that only the minified files have been selected.<\/p>\n<pre class=\"programlisting\">...\n&lt;head&gt;\n  &lt;title&gt;&lt;\/title&gt;\n  &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\"&gt;\n  <b class=\"fm-bold\">&lt;script src=\"\/lib\/jquery\/jquery.min.js\"&gt;&lt;\/script&gt;  &lt;script src=\"\/lib\/jquery\/jquery.slim.min.js\"&gt;&lt;\/script&gt;<\/b>\n&lt;\/head&gt;\n... <\/pre>\n<p class=\"body\">Narrowing the pattern for the JavaScript files has helped, but the browser will still end up with the normal and slim versions of jQuery and the bundled and unbundled versions of the Bootstrap JavaScript files. To narrow the selection further, I can include <code class=\"fm-code-in-text\">slim<\/code> in the pattern, as shown in listing 26.11.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.11 Narrowing the focus in the Views\/Shared\/_SimpleLayout.cshtml file<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    <b class=\"fm-bold\">&lt;script asp-src-include=\"lib\/jquery**\/*slim.min.js\"&gt;&lt;\/script&gt;<\/b>\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use the browser to request http:\/\/localhost:5000\/home\/list and examine the HTML the browser receives. The <code class=\"fm-code-in-text\">script<\/code> element has been transformed like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;head&gt;\n  &lt;title&gt;&lt;\/title&gt;\n  &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\"&gt;\n  <b class=\"fm-bold\">&lt;script src=\"\/lib\/jquery\/jquery.slim.min.js\"&gt;&lt;\/script&gt;<\/b>\n&lt;\/head&gt;\n...<\/pre>\n<p class=\"body\">Only one version of the jQuery file will be sent to the browser while preserving the flexibility for the location of the file.<\/p>\n<p class=\"fm-head3\">Excluding files<\/p>\n<p class=\"body\">Narrowing the pattern for the JavaScript files helps when you want to select a file whose name contains a specific term, such as <code class=\"fm-code-in-text\">slim<\/code>. It isn\u2019t helpful when the file you want doesn\u2019t have that term, such as when you want the full version of the minified file. Fortunately, you can use the <code class=\"fm-code-in-text\">asp-src-exclude<\/code> attribute to remove files from the list matched by the <code class=\"fm-code-in-text\">asp-src-include<\/code> attribute, as shown in listing 26.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.12 Excluding files in the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    <b class=\"fm-bold\">&lt;script asp-src-include=\"\/lib\/jquery\/**\/*.min.js\"<\/b> \n         <b class=\"fm-bold\">asp-src-exclude=\"**.slim.**\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/script&gt;<\/b>\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">If you restart ASP.NET Core and use the browser to request http:\/\/localhost:5000\/home\/list and examine the HTML response, you will see that the <code class=\"fm-code-in-text\">script<\/code> element links only to the full minified version of the jQuery library, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;head&gt;\n    &lt;title&gt;&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\"&gt;\n    <b class=\"fm-bold\">&lt;script src=\"\/lib\/jquery\/jquery.min.js\"&gt;&lt;\/script&gt;<\/b>\n&lt;\/head&gt;\n... <\/pre>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding cache busting<\/p>\n<p class=\"fm-sidebar-text\">Static content, such as images, CSS stylesheets, and JavaScript files, is often cached to stop requests for content that rarely changes from reaching the application servers. Caching can be done in different ways: the browser can be told to cache content by the server, the application can use cache servers to supplement the application servers, or the content can be distributed using a content delivery network. Not all caching will be under your control. Large corporations, for example, often install caches to reduce their bandwidth demands since a substantial percentage of requests tend to go to the same sites or applications.<a id=\"calibre_link-2390\"><\/a><a id=\"calibre_link-2391\"><\/a><a id=\"calibre_link-1176\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">One problem with caching is that clients don\u2019t immediately receive new versions of static files when you deploy them because their requests are still being serviced by previously cached content. Eventually, the cached content will expire, and the new content will be used, but that leaves a period where the dynamic content generated by the application\u2019s controllers is out of step with the static content being delivered by the caches. This can lead to layout problems or unexpected application behavior, depending on the content that has been updated.<\/p>\n<p class=\"fm-sidebar-text\">Addressing this problem is called <i class=\"fm-italics\">cache busting<\/i>. The idea is to allow caches to handle static content but immediately reflect any changes that are made at the server. The tag helper classes support cache busting by adding a query string to the URLs for static content that includes a checksum that acts as a version number. For JavaScript files, for example, the <code class=\"fm-code-in-text1\">ScriptTagHelper<\/code> class supports cache busting through the <code class=\"fm-code-in-text1\">asp-append-version<\/code> attribute, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;script asp-src-include=\"\/lib\/jquery\/**\/*.min.js\" \n    asp-src-exclude=\"**.slim.**\" <b class=\"fm-bold\">asp-append-version=\"true\"<\/b>&gt;\n&lt;\/script&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">Enabling the cache busting feature produces an element like this in the HTML sent to the browser:<\/p>\n<pre class=\"programlisting\">...\n&lt;script src=\"\/lib\/jquery\/jquery.min.js?v=_xUj3OJU5yExlq6GSYGSHk7tPXikyn\"&gt;\n&lt;\/script&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">The same version number will be used by the tag helper until you change the contents of the file, such as by updating a JavaScript library, at which point a different checksum will be calculated. The addition of the version number means that each time you change the file, the client will request a different URL, which caches treat as a request for new content that cannot be satisfied with the previously cached content and pass on to the application server. The content is then cached as normal until the next update, which produces another URL with a different version.<\/p>\n<\/div>\n<p class=\"fm-head2\">Working with content delivery networks<\/p>\n<p class=\"body\">Content delivery networks (CDNs) are used to offload requests for application content to servers that are closer to the user. Rather than requesting a JavaScript file from your servers, the browser requests it from a hostname that resolves to a geographically local server, which reduces the amount of time required to load files and reduces the amount of bandwidth you have to provision for your application. If you have a large, geographically disbursed set of users, then it can make commercial sense to sign up to a CDN, but even the smallest and simplest application can benefit from using the free CDNs operated by major technology companies to deliver common JavaScript packages, such as jQuery.<a id=\"calibre_link-2392\"><\/a><a id=\"calibre_link-2393\"><\/a><a id=\"calibre_link-891\"><\/a><\/p>\n<p class=\"body\">For this chapter, I am going to use CDNJS, which is the same CDN used by the Library Manager tool to install client-side packages in the ASP.NET Core project. You can search for packages at <a class=\"url\" href=\"https:\/\/cdnjs.com\">https:\/\/cdnjs.com<\/a>; for jQuery 3.6.3, which is the package and version installed in listing 26.4, there are six CDNJS URLs, all of which are accessible via HTTPS.<\/p>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">cdnjs.cloudflare.com\/ajax\/libs\/jquery\/3.6.3\/jquery.js<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">cdnjs.cloudflare.com\/ajax\/libs\/jquery\/3.6.3\/jquery.min.js<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">cdnjs.cloudflare.com\/ajax\/libs\/jquery\/3.6.3\/jquery.min.map<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">cdnjs.cloudflare.com\/ajax\/libs\/jquery\/3.6.3\/jquery.slim.js<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">cdnjs.cloudflare.com\/ajax\/libs\/jquery\/3.6.3\/jquery.slim.min.js<\/code><\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\"><code class=\"fm-code-in-text\">cdnjs.cloudflare.com\/ajax\/libs\/jquery\/3.6.3\/jquery.slim.min.map<\/code><\/p>\n<\/li>\n<\/ul>\n<p class=\"body\">These URLs provide the regular JavaScript file, the minified JavaScript file, and the source map for the minified file for both the full and slim versions of jQuery.<\/p>\n<p class=\"body\">The problem with CDNs is that they are not under your organization\u2019s control, and that means they can fail, leaving your application running but unable to work as expected because the CDN content isn\u2019t available. The <code class=\"fm-code-in-text\">ScriptTagHelper<\/code> class provides the ability to fall back to local files when the CDN content cannot be loaded by the client, as shown in listing 26.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.13 CDN fallback in the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    <b class=\"fm-bold\">&lt;script src=<\/b>\n      <b class=\"fm-bold\">\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jquery\/3.6.0\/jquery.min.js\"<\/b>\n         <b class=\"fm-bold\">asp-fallback-src=\"\/lib\/jquery\/jquery.min.js\"<\/b> \n         <b class=\"fm-bold\">asp-fallback-test=\"window.jQuery\"&gt;<\/b>\n    &lt;\/script&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">src<\/code> attribute is used to specify the CDN URL. The <code class=\"fm-code-in-text\">asp-fallback-src<\/code> attribute is used to specify a local file that will be used if the CDN is unable to deliver the file specified by the regular <code class=\"fm-code-in-text\">src<\/code> attribute. To figure out whether the CDN is working, the <code class=\"fm-code-in-text\">asp-fallback-test<\/code> attribute is used to define a fragment of JavaScript that will be evaluated at the browser. If the fragment evaluates as <code class=\"fm-code-in-text\">false<\/code>, then the fallback files will be requested.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">asp-fallback-src-include<\/code> and <code class=\"fm-code-in-text1\">asp-fallback-src-exclude<\/code> attributes can be used to select the local files with globbing patterns. However, given that CDN <code class=\"fm-code-in-text1\">script<\/code> elements select a single file, I recommend using the <code class=\"fm-code-in-text1\">asp-fallback-src<\/code> attribute to select the corresponding local file, as shown in the example.<\/p>\n<p class=\"body\">Restart ASP.NET Core and a browser to request http:\/\/localhost:5000\/home\/list, and you will see that the HTML response contains two <code class=\"fm-code-in-text\">script<\/code> elements, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;head&gt;\n    &lt;title&gt;&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    &lt;script src=\n      \"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jquery\/3.6.0\/jquery.min.js\"&gt;\n    &lt;\/script&gt;\n    &lt;script&gt;\n        (window.jQuery||document.write(\n          \"\\u003Cscript src=\\u0022\/lib\/jquery\/jquery.min.js\\u0022\\\n              u003E\\u003C\/script\\u003E\"));\n    &lt;\/script&gt;\n&lt;\/head&gt;\n...<\/pre>\n<p class=\"body\">The first <code class=\"fm-code-in-text\">script<\/code> element requests the JavaScript file from the CDN. The second <code class=\"fm-code-in-text\">script<\/code> element evaluates the JavaScript fragment specified by the <code class=\"fm-code-in-text\">asp-fallback-test<\/code> attribute, which checks to see whether the first <code class=\"fm-code-in-text\">script<\/code> element has worked. If the fragment evaluates to <code class=\"fm-code-in-text\">true<\/code>, then no action is taken because the CDN worked. If the fragment evaluates to <code class=\"fm-code-in-text\">false<\/code>, a new <code class=\"fm-code-in-text\">script<\/code> element is added to the HTML document that instructs the browser to load the JavaScript file from the fallback URL.<\/p>\n<p class=\"body\">It is important to test your fallback settings because you won\u2019t find out if they fail until the CDN has stopped working and your users cannot access your application. The simplest way to check the fallback is to change the name of the file specified by the <code class=\"fm-code-in-text\">src<\/code> attribute to something that you know doesn\u2019t exist (I append the word <code class=\"fm-code-in-text\">FAIL<\/code> to the file name) and then look at the network requests that the browser makes using the F12 developer tools. You should see an error for the CDN file followed by a request for the fallback file.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> The CDN fallback feature relies on browsers loading and executing the contents of <code class=\"fm-code-in-text1\">script<\/code> elements synchronously and in the order in which they are defined. There are a number of techniques in use to speed up JavaScript loading and execution by making the process asynchronous, but these can lead to the fallback test being performed before the browser has retrieved a file from the CDN and executed its contents, resulting in requests for the fallback files even when the CDN is working perfectly and defeating the use of a CDN in the first place. Do not mix asynchronous script loading with the CDN fallback feature.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-494\">26.4.2 Managing CSS stylesheets<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">LinkTagHelper<\/code> class is the built-in tag helper for <code class=\"fm-code-in-text\">link<\/code> elements and is used to manage the inclusion of CSS style sheets in a view. This tag helper supports the attributes described in table 26.6, which I demonstrate in the following sections.<a id=\"calibre_link-1184\"><\/a><a id=\"calibre_link-2394\"><\/a><a id=\"calibre_link-2395\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 26.6. The built-in tag helper attributes for link elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2396\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-href-include<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to select files for the <code class=\"fm-code-in-text1\">href<\/code> attribute of the output element.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-href-exclude<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to exclude files from the <code class=\"fm-code-in-text1\">href<\/code> attribute of the output element.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-append-version<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to enable cache busting, as described in the \u201cUnderstanding Cache Busting\u201d sidebar.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fallback-href<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify a fallback file if there is a problem with a CDN.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fallback-href-include<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to select files that will be used if there is a CDN problem.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fallback-href-exclude<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to exclude files from the set that will be used when there is a CDN problem.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fallback-href-test-class<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the CSS class that will be used to test the CDN.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fallback-href-test-property<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the CSS property that will be used to test the CDN.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fallback-href-test-value<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the CSS value that will be used to test the CDN.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"fm-head2\">Selecting stylesheets<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">LinkTagHelper<\/code> shares many features with the <code class=\"fm-code-in-text\">ScriptTagHelper<\/code>, including support for globbing patterns to select or exclude CSS files so they do not have to be specified individually. Being able to accurately select CSS files is as important as it is for JavaScript files because stylesheets can come in regular and minified versions and support source maps. The popular Bootstrap package, which I have been using to style HTML elements throughout this book, includes its CSS stylesheets in the <code class=\"fm-code-in-text\">wwwroot\/lib\/bootstrap\/css<\/code> folder. These will be visible in Visual Studio Code, but you will have to expand each item in the Solution Explorer or disable nesting to see them in the Visual Studio Solution Explorer, as shown in figure 26.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre270\" src=\"\/images\/proaspnetcore7\/000277.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 26.6 The Bootstrap CSS files<\/p>\n<\/div>\n<p class=\"body\">The <code class=\"fm-code-in-text\">bootstrap.css<\/code> file is the regular stylesheet, the <code class=\"fm-code-in-text\">bootstrap.min.css<\/code> file is the minified version, and the <code class=\"fm-code-in-text\">bootstrap.css.map<\/code> file is a source map. The other files contain subsets of the CSS features to save bandwidth in applications that don\u2019t use them.<\/p>\n<p class=\"body\">Listing 26.14 replaces the regular <code class=\"fm-code-in-text\">link<\/code> element in the layout with one that uses the <code class=\"fm-code-in-text\">asp-href-include<\/code> and <code class=\"fm-code-in-text\">asp-href-exclude<\/code> attributes. (I removed the <code class=\"fm-code-in-text\">script<\/code> element for jQuery, which is no longer required.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.14 Selecting a stylesheet in the Views\/Shared\/_SimpleLayout.cshtml file<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link asp-href-include=\"\/lib\/bootstrap\/css\/*.min.css\"\n          asp-href-exclude=\n            \"**\/*-reboot*,**\/*-grid*,**\/*-utilities*, **\/*.rtl.*\"\n          rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The same attention to detail is required as when selecting JavaScript files because it is easy to generate <code class=\"fm-code-in-text\">link<\/code> elements for multiple versions of the same file or files that you don\u2019t want. Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/list. Inspect the HTML received by the browser, and you will see that there is one <code class=\"fm-code-in-text\">link<\/code> element, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;head&gt;\n    &lt;title&gt;&lt;\/title&gt;\n    <b class=\"fm-bold\">&lt;link rel=\"stylesheet\" href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" \/&gt;<\/b>\n&lt;\/head&gt;\n...<\/pre>\n<p class=\"fm-head2\">Working with content delivery networks<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">LinkTag<\/code> helper class provides a set of attributes for falling back to local content when a CDN isn\u2019t available, although the process for testing to see whether a stylesheet has loaded is more complex than testing for a JavaScript file. Listing 26.15 uses the CDNJS URL for the Bootstrap CSS stylesheet.(The CDN URL is too long to fit on the printed page, but should be on a single line in the code file)<a id=\"calibre_link-2397\"><\/a>.<a id=\"calibre_link-1188\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.15 Using a CDN for CSS in the Views\/Shared\/_SimpleLayout.cshtml file<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    <b class=\"fm-bold\">&lt;link href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/bootstrap\/5.2.3\/<\/b>\n<b class=\"fm-bold\">css\/bootstrap.min.css\"<\/b>\n        <b class=\"fm-bold\">asp-fallback-href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\"<\/b>\n        <b class=\"fm-bold\">asp-fallback-test-class=\"btn\"<\/b>\n        <b class=\"fm-bold\">asp-fallback-test-property=\"display\"<\/b>\n        <b class=\"fm-bold\">asp-fallback-test-value=\"inline-block\"<\/b>\n        <b class=\"fm-bold\">rel=\"stylesheet\" \/&gt;<\/b>\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">href<\/code> attribute is used to specify the CDN URL, and I have used the <code class=\"fm-code-in-text\">asp-fallback-href<\/code> attribute to select the file that will be used if the CDN is unavailable. Testing whether the CDN works, however, requires the use of three different attributes and an understanding of the CSS classes defined by the CSS stylesheet that is being used.<\/p>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/home\/list and examine the HTML elements in the response. You will see that the <code class=\"fm-code-in-text\">link<\/code> element from the layout has been transformed into three separate elements, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;head&gt;\n        &lt;title&gt;&lt;\/title&gt;\n        <b class=\"fm-bold\">&lt;link href=\"https:\/\/cdnjs.cloudflare.com\/...\/bootstrap.min.css\"<\/b> \n            <b class=\"fm-bold\">rel=\"stylesheet\"\/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;meta name=\"x-stylesheet-fallback-test\" content=\"\" class=\"btn\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;script&gt;<\/b>\n          <b class=\"fm-bold\">!function(a,b,c,d){var e,f=document,<\/b>\n          <b class=\"fm-bold\">g=f.getElementsByTagName(\"SCRIPT\"),<\/b>\n          <b class=\"fm-bold\">h=g[g.length1].previousElementSibling,<\/b>\n          <b class=\"fm-bold\">i=f.defaultView&amp;&amp;f.defaultView.getComputedStyle ?<\/b>            \n          <b class=\"fm-bold\">f.defaultView.getComputedStyle(h) : h.currentStyle;<\/b>\n          <b class=\"fm-bold\">if(i&amp;&amp;i[a]!==b)for(e=0;e&lt;c.length;e++)<\/b>\n              <b class=\"fm-bold\">f.write('&lt;link href=\"'+c[e]+'\" '+d+\"\/&gt;\")}(<\/b>\n                 <b class=\"fm-bold\">\"display\",\"inline-block\",<\/b>\n                 <b class=\"fm-bold\">[\"\/lib\/bootstrap\/css\/bootstrap.min.css\"],<\/b> \n                 <b class=\"fm-bold\">\"rel=\\u0022stylesheet\\u0022 \");<\/b>\n        <b class=\"fm-bold\">&lt;\/script&gt;<\/b>\n&lt;\/head&gt;\n...<\/pre>\n<p class=\"body\">To make the transformation easier to understand, I have formatted the JavaScript code and shortened the URL.<\/p>\n<p class=\"body\">The first element is a regular <code class=\"fm-code-in-text\">link<\/code> whose <code class=\"fm-code-in-text\">href<\/code> attribute specifies the CDN file. The second element is a <code class=\"fm-code-in-text\">meta<\/code> element, which specifies the class from the <code class=\"fm-code-in-text\">asp-fallback-test-class<\/code> attribute in the view. I specified the <code class=\"fm-code-in-text\">btn<\/code> class in the listing, which means that an element like this is added to the HTML sent to the browser:<\/p>\n<pre class=\"programlisting\">&lt;meta name=\"x-stylesheet-fallback-test\" content=\"\" <b class=\"fm-bold\">class=\"btn\"<\/b>&gt;<\/pre>\n<p class=\"body\">The CSS class that you specify must be defined in the stylesheet that will be loaded from the CDN. The <code class=\"fm-code-in-text\">btn<\/code> class that I specified provides the basic formatting for Bootstrap button elements.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-fallback-test-property<\/code> attribute is used to specify a CSS property that is set when the CSS class is applied to an element, and the <code class=\"fm-code-in-text\">asp-fallback-test-value<\/code> attribute is used to specify the value that it will be set to.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">script<\/code> element created by the tag helper contains JavaScript code that adds an element to the specified class and then tests the value of the CSS property to determine whether the CDN stylesheet has been loaded. If not, a <code class=\"fm-code-in-text\">link<\/code> element is created for the fallback file. The Bootstrap <code class=\"fm-code-in-text\">btn<\/code> class sets the <code class=\"fm-code-in-text\">display<\/code> property to <code class=\"fm-code-in-text\">inline-block<\/code>, and this provides the test to see whether the browser has been able to load the Bootstrap stylesheet from the CDN.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The easiest way to figure out how to test for third-party packages like Bootstrap is to use the browser\u2019s F12 developer tools. To determine the test in listing 26.15, I assigned an element to the <code class=\"fm-code-in-text1\">btn<\/code> class and then inspected it in the browser, looking at the individual CSS properties that the class changes. I find this easier than trying to read through long and complex style sheets.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-495\">26.5 Working with image elements<\/h2>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ImageTagHelper<\/code> class is used to provide cache busting for images through the <code class=\"fm-code-in-text\">src<\/code> attribute of <code class=\"fm-code-in-text\">img<\/code> elements, allowing an application to take advantage of caching while ensuring that modifications to images are reflected immediately. The <code class=\"fm-code-in-text\">ImageTagHelper<\/code> class operates in <code class=\"fm-code-in-text\">img<\/code> elements that define the <code class=\"fm-code-in-text\">asp-append-version<\/code> attribute, which is described in table 26.7 for quick reference.<a id=\"calibre_link-2398\"><\/a><a id=\"calibre_link-2399\"><\/a><a id=\"calibre_link-1187\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 26.7 The built-in tag helper attribute for image elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2400\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-append-version<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to enable cache busting, as described in the \u201cUnderstanding Cache Busting\u201d sidebar.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">In listing 26.16, I have added an <code class=\"fm-code-in-text\">img<\/code> element to the shared layout for the city skyline image that I added to the project at the start of the chapter. I have also reset the <code class=\"fm-code-in-text\">link<\/code> element to use a local file for brevity.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.16 Adding an image in the Views\/Shared\/_SimpleLayout.cshtml file<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    <b class=\"fm-bold\">&lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;<\/b>\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"m-2\"&gt;\n        <b class=\"fm-bold\">&lt;img src=\"\/images\/proaspnetcore7\/city.png\" asp-append-version=\"true\"<\/b> \n            <b class=\"fm-bold\">class=\"m-2\" \/&gt;<\/b>\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and a browser to request http:\/\/localhost:5000\/home\/list, which will produce the response shown in figure 26.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre271\" src=\"\/images\/proaspnetcore7\/000278.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 26.7 Using an image<\/p>\n<\/div>\n<p class=\"body\">Examine the HTML response, and you will see that the URL used to request the image file includes a version checksum, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;img src=\"\/images\/proaspnetcore7\/city.png<b class=\"fm-bold\">?v=KaMNDSZFAJufRcRDpKh0K_IIPNc7E<\/b>\" class=\"m-2\"&gt;\n...<\/pre>\n<p class=\"body\">The addition of the checksum ensures that any changes to the file will pass through any caches, avoiding stale content.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-496\">26.6 Using the data cache<\/h2>\n<p class=\"body\">The <code class=\"fm-code-in-text\">CacheTagHelper<\/code> class allows fragments of content to be cached to speed up rendering of views or pages. The content to be <code class=\"fm-code-in-text\">cached<\/code> is denoted using the <code class=\"fm-code-in-text\">cache<\/code> element, which is configured using the attributes shown in table 26.8.<a id=\"calibre_link-2401\"><\/a><a id=\"calibre_link-1177\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Caching is a useful tool for reusing sections of content so they don\u2019t have to be generated for every request. But using caching effectively requires careful thought and planning. While caching can improve the performance of an application, it can also create odd effects, such as users receiving stale content, multiple caches containing different versions of content, and update deployments that are broken because content cached from the previous version of the application is mixed with content from the new version. Don\u2019t enable caching unless you have a clearly defined performance problem to resolve, and make sure you understand the impact that caching will have.<\/p>\n<p class=\"fm-table-caption\">Table 26.8 The built-in tag helper attributes for cache elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2402\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">enabled<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">bool<\/code> attribute is used to control whether the contents of the <code class=\"fm-code-in-text1\">cache<\/code> element are cached. Omitting this attribute enables caching.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">expires-on<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify an absolute time at which the cached content will expire, expressed as a <code class=\"fm-code-in-text1\">DateTime<\/code> value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">expires-after<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify a relative time at which the cached content will expire, expressed as a <code class=\"fm-code-in-text1\">TimeSpan<\/code> value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">expires-sliding<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the period since it was last used when the cached content will expire, expressed as a <code class=\"fm-code-in-text1\">TimeSpan<\/code> value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">vary-by-header<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the name of a request header that will be used to manage different versions of the cached content.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">vary-by-query<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the name of a query string key that will be used to manage different versions of the cached content.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">vary-by-route<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the name of a routing variable that will be used to manage different versions of the cached content.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">vary-by-cookie<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the name of a cookie that will be used to manage different versions of the cached content.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">vary-by-user<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">bool<\/code> attribute is used to specify whether the name of the authenticated user will be used to manage different versions of the cached content.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">vary-by<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is evaluated to provide a key used to manage different versions of the content.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">priority<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify a relative priority that will be taken into account when the memory cache runs out of space and purges unexpired cached content.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Listing 26.17 replaces the <code class=\"fm-code-in-text\">img<\/code> element from the previous section with content that contains timestamps.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.17 Caching in the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        <b class=\"fm-bold\">&lt;h6 class=\"bg-primary text-white m-2 p-2\"&gt;<\/b>\n            <b class=\"fm-bold\">Uncached timestamp: @DateTime.Now.ToLongTimeString()<\/b>\n        <b class=\"fm-bold\">&lt;\/h6&gt;<\/b>            \n        <b class=\"fm-bold\">&lt;cache&gt;<\/b>\n            <b class=\"fm-bold\">&lt;h6 class=\"bg-primary text-white m-2 p-2\"&gt;<\/b>\n                <b class=\"fm-bold\">Cached timestamp: @DateTime.Now.ToLongTimeString()<\/b>\n            <b class=\"fm-bold\">&lt;\/h6&gt;<\/b>            \n        <b class=\"fm-bold\">&lt;\/cache&gt;<\/b>\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">cache<\/code> element is used to denote a region of content that should be cached and has been applied to one of the <code class=\"fm-code-in-text\">h6<\/code> elements that contains a timestamp. Restart ASP.NET Core and a browser to request http:\/\/localhost:5000\/home\/list, and both timestamps will be the same. Reload the browser, and you will see that the cached content is used for one of the <code class=\"fm-code-in-text\">h6<\/code> elements and the timestamp doesn\u2019t change, as shown in figure 26.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre272\" src=\"\/images\/proaspnetcore7\/000279.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 26.8 Using the caching tag helper<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Using distributed caching for content<\/p>\n<p class=\"fm-sidebar-text\">The cache used by the <code class=\"fm-code-in-text1\">CacheTagHelper<\/code> class is memory-based, which means that its capacity is limited by the available RAM and that each application server maintains a separate cache. Content will be ejected from the cache when there is a shortage of capacity available, and the entire contents are lost when the application is stopped or restarted.<\/p>\n<p class=\"fm-sidebar-text\">The <code class=\"fm-code-in-text1\">distributed-cache<\/code> element can be used to store content in a shared cache, which ensures that all application servers use the same data and that the cache survives restarts. The <code class=\"fm-code-in-text1\">distributed-cache<\/code> element is configured with the same attributes as the cache element, as described in table 26.8. See chapter 17 for details of setting up a distributed cache.<a id=\"calibre_link-2403\"><\/a><\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-497\">26.6.1 Setting cache expiry<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">expires-*<\/code> attributes allow you to specify when cached content will expire, expressed either as an absolute time or as a time relative to the current time, or to specify a duration during which the cached content isn\u2019t requested. In listing 26.18, I have used the <code class=\"fm-code-in-text\">expires-after<\/code> attribute to specify that the content should be cached for 15 seconds.<a id=\"calibre_link-2404\"><\/a><a id=\"calibre_link-1178\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.18 Setting expiry in the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;h6 class=\"bg-primary text-white m-2 p-2\"&gt;\n            Uncached timestamp: @DateTime.Now.ToLongTimeString()\n        &lt;\/h6&gt;            \n        <b class=\"fm-bold\">&lt;cache expires-after=\"@TimeSpan.FromSeconds(15)\"&gt;<\/b>\n            &lt;h6 class=\"bg-primary text-white m-2 p-2\"&gt;\n                Cached timestamp: @DateTime.Now.ToLongTimeString()\n            &lt;\/h6&gt;            \n        &lt;\/cache&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/home\/list and then reload the page. After 15 seconds the cached content will expire, and a new section of content will be created.<\/p>\n<p class=\"fm-head2\">Setting a fixed expiry point<\/p>\n<p class=\"body\">You can specify a fixed time at which cached content will expire using the <code class=\"fm-code-in-text\">expires-on<\/code> attribute, which accepts a <code class=\"fm-code-in-text\">DateTime<\/code> value, as shown in listing 26.19.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.19 Setting expiry in the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;h6 class=\"bg-primary text-white m-2 p-2\"&gt;\n            Uncached timestamp: @DateTime.Now.ToLongTimeString()\n        &lt;\/h6&gt;            \n        <b class=\"fm-bold\">&lt;cache expires-on=\"@DateTime.Parse(\"2100-01-01\")\"&gt;<\/b>\n            &lt;h6 class=\"bg-primary text-white m-2 p-2\"&gt;\n                Cached timestamp: @DateTime.Now.ToLongTimeString()\n            &lt;\/h6&gt;            \n        &lt;\/cache&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">I have specified that that data should be cached until the year 2100. This isn\u2019t a useful caching strategy since the application is likely to be restarted before the next century starts, but it does illustrate how you can specify a fixed point in the future rather than expressing the expiry point relative to the moment when the content is cached.<\/p>\n<p class=\"fm-head2\">Setting a last-used expiry period<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">expires-sliding<\/code> attribute is used to specify a period after which content is expired if it hasn\u2019t been retrieved from the cache. In listing 26.20, I have specified a sliding expiry of 10 seconds.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.20 Sliding expiry in the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;h6 class=\"bg-primary text-white m-2 p-2\"&gt;\n            Uncached timestamp: @DateTime.Now.ToLongTimeString()\n        &lt;\/h6&gt;            \n        <b class=\"fm-bold\">&lt;cache expires-sliding=\"@TimeSpan.FromSeconds(10)\"&gt;<\/b>\n            &lt;h6 class=\"bg-primary text-white m-2 p-2\"&gt;\n                Cached timestamp: @DateTime.Now.ToLongTimeString()\n            &lt;\/h6&gt;            \n        &lt;\/cache&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">You can see the effect of the <code class=\"fm-code-in-text\">express-sliding<\/code> attribute by restarting ASP.NET Core and requesting http:\/\/localhost:5000\/home\/list and periodically reloading the page. If you reload the page within 10 seconds, the cached content will be used. If you wait longer than 10 seconds to reload the page, then the cached content will be discarded, the view component will be used to generate new content, and the process will begin anew.<\/p>\n<p class=\"fm-head2\">Using cache variations<\/p>\n<p class=\"body\">By default, all requests receive the same cached content. The <code class=\"fm-code-in-text\">CacheTagHelper<\/code> class can maintain different versions of cached content and use them to satisfy different types of HTTP requests, specified using one of the attributes whose name begins with <code class=\"fm-code-in-text\">vary-by<\/code>. Listing 26.21 shows the use of the <code class=\"fm-code-in-text\">vary-by-route<\/code> attribute to create cache variations based on the <code class=\"fm-code-in-text\">action<\/code> value matched by the routing system.<a id=\"calibre_link-2405\"><\/a><a id=\"calibre_link-1179\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.21 Variation in the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;h6 class=\"bg-primary text-white m-2 p-2\"&gt;\n            Uncached timestamp: @DateTime.Now.ToLongTimeString()\n        &lt;\/h6&gt;            \n        <b class=\"fm-bold\">&lt;cache expires-sliding=\"@TimeSpan.FromSeconds(10)\"<\/b> \n                <b class=\"fm-bold\">vary-by-route=\"action\"&gt;<\/b>\n            &lt;h6 class=\"bg-primary text-white m-2 p-2\"&gt;\n                Cached timestamp: @DateTime.Now.ToLongTimeString()\n            &lt;\/h6&gt;            \n        &lt;\/cache&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">If you restart ASP.NET Core and use two browser tabs to request http:\/\/localhost:5000\/home\/index and http:\/\/localhost:5000\/home\/list, you will see that each window receives its own cached content with its own expiration, since each request produces a different <code class=\"fm-code-in-text\">action<\/code> routing value.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> If you are using Razor Pages, then you can achieve the same effect using <code class=\"fm-code-in-text1\">page<\/code> as the value matched by the routing system.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-498\">26.7 Using the hosting environment tag helper<\/h2>\n<p class=\"body\">The <code class=\"fm-code-in-text\">EnvironmentTagHelper<\/code> class is applied to the custom <code class=\"fm-code-in-text\">environment<\/code> element and determines whether a region of content is included in the HTML sent to the browser-based on the hosting environment, which I described in chapters 15 and 16. The <code class=\"fm-code-in-text\">environment<\/code> element relies on the <code class=\"fm-code-in-text\">names<\/code> attribute, which I have described in table 26.9.<a id=\"calibre_link-2406\"><\/a><a id=\"calibre_link-1185\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 26.9 The built-in tag helper attribute for environment elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2407\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">names<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify a comma-separated list of hosting environment names for which the content contained within the <code class=\"fm-code-in-text1\">environment<\/code> element will be included in the HTML sent to the client.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">In listing 26.22, I have added <code class=\"fm-code-in-text\">environment<\/code> elements to the shared layout including different content in the view for the development and production hosting environments.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 26.22 Using environment in the Views\/Shared\/_SimpleLayout.cshtml file<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        <b class=\"fm-bold\">&lt;environment names=\"development\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;h2 class=\"bg-info text-white m-2 p-2\"&gt;<\/b>\n                <b class=\"fm-bold\">This is Development<\/b>\n            <b class=\"fm-bold\">&lt;\/h2&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/environment&gt;<\/b>\n        <b class=\"fm-bold\">&lt;environment names=\"production\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;h2 class=\"bg-danger text-white m-2 p-2\"&gt;<\/b>\n                <b class=\"fm-bold\">This is Production<\/b>\n            <b class=\"fm-bold\">&lt;\/h2&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/environment&gt;<\/b>\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">environment<\/code> element checks the current hosting environment name and either includes the content it contains or omits it (the <code class=\"fm-code-in-text\">environment<\/code> element itself is always omitted from the HTML sent to the client). Figure 26.9 shows the output for the development and production environments. (See chapter 15 for details of how to set the environment.)<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre273\" src=\"\/images\/proaspnetcore7\/000280.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 26.9 Managing content using the hosting environment<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-499\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The built-in tag helpers are enabled using the view imports files.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">AnchorTagHelper<\/code> class transforms anchor elements to target action methods or Razor Pages.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">ScriptTagHelper<\/code> and <code class=\"fm-code-in-text\">LinkTagHelper<\/code> classes transform <code class=\"fm-code-in-text\">script<\/code> and <code class=\"fm-code-in-text\">link<\/code> elements, using local files or those provided by a content delivery network.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">ImageTagHelper<\/code> class transforms <code class=\"fm-code-in-text\">img<\/code> elements, introducing cache-busting values to file names.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">CacheTagHelper<\/code> class is used to cache fragments of content.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">EnvironmentTagHelper<\/code> class incorporates content into the response for specific hosting environments.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-500\">\n<div class=\"calibre1\" id=\"calibre_link-2408\">\n<h1 class=\"tochead\" id=\"calibre_link-2409\"><a id=\"calibre_link-2410\"><\/a><a id=\"calibre_link-2411\"><\/a><a id=\"calibre_link-2412\"><\/a>27 Using the forms tag helpers<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using ASP.NET Core tag helpers to transform form elements<\/li>\n<li class=\"co-summary-bullet\">Transforming input elements and formatting their contents<\/li>\n<li class=\"co-summary-bullet\">Generating label elements from model properties<\/li>\n<li class=\"co-summary-bullet\">Generating select and option elements<\/li>\n<li class=\"co-summary-bullet\">Protecting forms against cross-site request forgery<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I describe the built-in tag helpers that are used to create HTML forms. These tag helpers ensure forms are submitted to the correct action or page handler method and that elements accurately represent specific model properties. Table 27.1 puts the form tag helpers in context.<\/p>\n<p class=\"fm-table-caption\">Table 27.1 Putting form tag helpers in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2413\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">These built-in tag helpers transform HTML form elements.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">These tag helpers ensure that HTML forms reflect the application\u2019s routing configuration and data model.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Tag helpers are applied to HTML elements using <code class=\"fm-code-in-text1\">asp-*<\/code> attributes.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">These tag helpers are reliable and predictable and present no serious issues.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">You don\u2019t have to use tag helpers and can define forms without them if you prefer.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 27.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 27.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2414\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Specifying how a form will be submitted<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the form tag helper attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">10&ndash;13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Transforming <code class=\"fm-code-in-text1\">input<\/code> elements<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the input tag helper attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14&ndash;22<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Transforming <code class=\"fm-code-in-text1\">label<\/code> elements<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the label tag helper attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">23<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Populating <code class=\"fm-code-in-text1\">select<\/code> elements<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the select tag helper attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">24&ndash;26<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Transforming text areas<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the text area tag helper attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">27<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Protecting against cross-site request forgery<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Enable the anti-forgery feature.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">28&ndash;32<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-501\">27.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the WebApp project from chapter 26. To prepare for this chapter, replace the contents of the <code class=\"fm-code-in-text\">_SimpleLayout.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder with those shown in listing 27.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.1 The contents of the _SimpleLayout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">This chapter uses controller views and Razor Pages to present similar content. To differentiate more readily between controllers and pages, add the route shown in listing 27.2 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.2 Adding a route in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddSingleton&lt;CitiesData&gt;();\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n<b class=\"fm-bold\">\/\/app.MapControllers();<\/b>\n<b class=\"fm-bold\">\/\/app.MapDefaultControllerRoute();<\/b>\n<b class=\"fm-bold\">app.MapControllerRoute(\"forms\",<\/b>\n    <b class=\"fm-bold\">\"controllers\/{controller=Home}\/{action=Index}\/{id?}\");<\/b>\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The new route introduces a static path segment that makes it obvious that a URL targets a controller.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-502\">27.1.1 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 27.3 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.3 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-503\">27.1.2 Running the example application<\/h3>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 27.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.4 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/controllers\/home\/list, which will display a list of products, as shown in figure 27.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre274\" src=\"\/images\/proaspnetcore7\/000281.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 27.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-504\">27.2 Understanding the form handling pattern<\/h2>\n<p class=\"body\">Most HTML forms exist within a well-defined pattern, shown in figure 27.2. First, the browser sends an HTTP GET request, which results in an HTML response containing a form, making it possible for the user to provide the application with data. The user clicks a button that submits the form data with an HTTP POST request, which allows the application to receive and process the user\u2019s data. Once the data has been processed, a response is sent that redirects the browser to a URL that confirms the user\u2019s actions.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre275\" src=\"\/images\/proaspnetcore7\/000282.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 27.2 The HTML Post\/Redirect\/Get pattern<\/p>\n<\/div>\n<p class=\"body\">This is known as the Post\/Redirect\/Get pattern, and the redirection is important because it means the user can click the browser\u2019s reload button without sending another POST request, which can lead to inadvertently repeating an operation.<\/p>\n<p class=\"body\">In the sections that follow, I show how to follow the pattern with controllers and Razor Pages. I start with a basic implementation of the pattern and then demonstrate improvements using tag helpers and, in chapter 28, the model binding feature.<a id=\"calibre_link-2415\"><\/a><a id=\"calibre_link-2416\"><\/a><a id=\"calibre_link-2417\"><\/a><a id=\"calibre_link-2418\"><\/a><a id=\"calibre_link-994\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-505\">27.2.1 Creating a controller to handle forms<\/h3>\n<p class=\"body\">Controllers that handle forms are created by combining features described in earlier chapters. Add a class file named <code class=\"fm-code-in-text\">FormController.cs<\/code> to the <code class=\"fm-code-in-text\">Controllers<\/code> folder with the code shown in listing 27.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.5 The contents of the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            return View(\"Form\", await context.Products.FindAsync(id)\n                ?? new () { Name = string.Empty });\n        }\n                \n        [HttpPost]\n        public IActionResult SubmitForm() {\n            foreach (string key in \n                    Request.Form.Keys.Where(k =&gt; !k.StartsWith(\"_\"))) {\n                TempData[key] = string.Join(\", \", \n                    (string?)Request.Form[key]);\n            }\n            return RedirectToAction(nameof(Results));\n        }\n                \n        public IActionResult Results() {\n            return View();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Index<\/code> action method selects a view named <code class=\"fm-code-in-text\">Form<\/code>, which will render an HTML form to the user. When the user submits the form, it will be received by the <code class=\"fm-code-in-text\">SubmitForm<\/code> action, which has been decorated with the <code class=\"fm-code-in-text\">HttpPost<\/code> attribute so that it can only receive HTTP POST requests. This action method processes the HTML form data available through the <code class=\"fm-code-in-text\">HttpRequest.Form<\/code> property so that it can be stored using the temp data feature. The temp data feature can be used to pass data from one request to another but can be used only to store simple data types. Each form data value is presented as a string array, which I convert to a single comma-separated string for storage. The browser is redirected to the <code class=\"fm-code-in-text\">Results<\/code> action method, which selects the default view.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Only form data values whose name doesn\u2019t begin with an underscore are displayed. I explain why in the \u201cUsing the Anti-forgery Feature\u201d section, later in this chapter.<\/p>\n<p class=\"body\">To provide the controller with views, create the <code class=\"fm-code-in-text\">Views\/Form<\/code> folder and add to it a Razor View file named <code class=\"fm-code-in-text\">Form.cshtml<\/code> with the content shown in listing 27.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.6 The contents of the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form action=\"\/controllers\/form\/submitform\" method=\"post\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        &lt;input class=\"form-control\" name=\"Name\" value=\"@Model.Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">This view contains a simple HTML form that is configured to submit its data to the <code class=\"fm-code-in-text\">SubmitForm<\/code> action method using a POST request. The form contains an <code class=\"fm-code-in-text\">input<\/code> element whose value is set using a Razor expression. Next, add a Razor View named <code class=\"fm-code-in-text\">Results.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Form<\/code> folder with the content shown in listing 27.7.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.7 The contents of the Results.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@{ Layout = \"_SimpleLayout\"; }\n\n&lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n    &lt;thead&gt;\n        &lt;tr class=\"bg-primary text-white text-center\"&gt;\n            &lt;th colspan=\"2\"&gt;Form Data&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (string key in TempData.Keys) {\n            &lt;tr&gt;\n                &lt;th&gt;@key&lt;\/th&gt;\n                &lt;td&gt;@TempData[key]&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }        \n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n&lt;a class=\"btn btn-primary\" asp-action=\"Index\"&gt;Return&lt;\/a&gt;<\/pre>\n<p class=\"body\">This view displays the form data back to the user. I\u2019ll show you how to process form data in more useful ways in chapter 31, but for this chapter the focus is on creating the forms, and seeing the data contained in the form is enough to get started.<\/p>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/controllers\/form to see the HTML form. Enter a value into the text field and click Submit to send a POST request, which will be handled by the <code class=\"fm-code-in-text\">SubmitForm<\/code> action. The form data will be stored as temp data, and the browser will be redirected, producing the response shown in figure 27.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre276\" src=\"\/images\/proaspnetcore7\/000283.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 27.3 Using a controller to render and process an HTML form<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-506\">27.2.2 Creating a Razor Page to handle forms<\/h3>\n<p class=\"body\">The same pattern can be implemented using Razor Pages. One page is required to render and process the form data, and a second page displays the results. Add a Razor Page named <code class=\"fm-code-in-text\">FormHandler.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the contents shown in listing 27.8.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.8 The contents of the FormHandler.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/form\/{id:long?}\"\n@model FormHandlerModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n\n&lt;div class=\"m-2\"&gt;\n    &lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n    &lt;form action=\"\/pages\/form\" method=\"post\"&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Name&lt;\/label&gt;\n            &lt;input class=\"form-control\" name=\"Name\"\n                   value=\"@Model.Product?.Name\" \/&gt;\n        &lt;\/div&gt;\n        &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n    &lt;\/form&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    [IgnoreAntiforgeryToken]\n    public class FormHandlerModel : PageModel {\n        private DataContext context;\n                \n        public FormHandlerModel(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public Product? Product { get; set; }\n\n        public async Task OnGetAsync(long id = 1) {\n            Product = await context.Products.FindAsync(id);\n        }\n                \n        public IActionResult OnPost() {\n            foreach (string key in Request.Form.Keys\n                    .Where(k =&gt; !k.StartsWith(\"_\"))) {\n                TempData[key] = string.Join(\", \", \n                    (string?)Request.Form[key]);\n            }\n            return RedirectToPage(\"FormResults\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnGetAsync<\/code> handler methods retrieves a <code class=\"fm-code-in-text\">Product<\/code> from the database, which is used by the view to set the value for the <code class=\"fm-code-in-text\">input<\/code> element in the HTML form. The form is configured to send an HTTP POST request that will be processed by the <code class=\"fm-code-in-text\">OnPost<\/code> handler method. The form data is stored as temp data, and the browser is sent a redirection to a form named <code class=\"fm-code-in-text\">FormResults<\/code>. To create the page to which the browser will be redirected, add a Razor Page named <code class=\"fm-code-in-text\">FormResults.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 27.9.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The page model class in listing 27.8 is decorated with the <code class=\"fm-code-in-text1\">IgnoreAntiforgeryToken<\/code> attribute, which is described in the \u201cUsing the Anti-forgery Feature\u201d section.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.9 The contents of the FormResults.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/results\"\n\n&lt;div class=\"m-2\"&gt;\n    &lt;table class=\"table table-striped table-bordered table-sm\"&gt;\n        &lt;thead&gt;\n            &lt;tr class=\"bg-primary text-white text-center\"&gt;\n                &lt;th colspan=\"2\"&gt;Form Data&lt;\/th&gt;\n            &lt;\/tr&gt;\n        &lt;\/thead&gt;\n        &lt;tbody&gt;\n            @foreach (string key in TempData.Keys) {\n                &lt;tr&gt;\n                    &lt;th&gt;@key&lt;\/th&gt;\n                    &lt;td&gt;@TempData[key]&lt;\/td&gt;\n                &lt;\/tr&gt;\n            }        \n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n    &lt;a class=\"btn btn-primary\" asp-page=\"FormHandler\"&gt;Return&lt;\/a&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">No code is required for this page, which accesses temp data directly and displays it in a table. Restart ASP.NET Core and use a browser to navigate to http:\/\/localhost:5000\/pages\/form, enter a value into the text field, and click the Submit button. The form data will be processed by the <code class=\"fm-code-in-text\">OnPost<\/code> method defined in listing 27.9, and the browser will be redirected to <code class=\"fm-code-in-text\">\/pages\/results<\/code>, which displays the form data, as shown in figure 27.4.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> If you receive a <code class=\"fm-code-in-text1\">RuntimeBinderException<\/code> exception that tells you that <code class=\"fm-code-in-text1\">System.Dynamic.DynamicObject<\/code> does not contain a definition for <code class=\"fm-code-in-text1\">Title<\/code>, then you need to clear your browser\u2019s cookies, start ASP.NET Core, and try again.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre277\" src=\"\/images\/proaspnetcore7\/000284.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 27.4 Using Razor Pages to render and process an HTML form<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-507\">27.3 Using tag helpers to improve HTML forms<\/h2>\n<p class=\"body\">The examples in the previous section show the basic mechanisms for dealing with HTML forms, but ASP.NET Core includes tag helpers that transform form elements. In the sections that follow, I describe the tag helpers and demonstrate their use.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-508\">27.3.1 Working with form elements<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">FormTagHelper<\/code> class is the built-in tag helper for <code class=\"fm-code-in-text\">form<\/code> elements and is used to manage the configuration of HTML forms so that they target the right action or page handler without the need to hard-code URLs. This tag helper supports the attributes described in table 27.3.<a id=\"calibre_link-2419\"><\/a><a id=\"calibre_link-984\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 27.3 The built-in tag helper attributes for form elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2420\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-controller<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the <code class=\"fm-code-in-text1\">controller<\/code> value to the routing system for the <code class=\"fm-code-in-text1\">action<\/code> attribute URL. If omitted, then the controller rendering the view will be used.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-action<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the action method for the <code class=\"fm-code-in-text1\">action<\/code> value to the routing system for the <code class=\"fm-code-in-text1\">action<\/code> attribute URL. If omitted, then the action rendering the view will be used.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-page<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the name of a Razor Page.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-page-handler<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the name of the handler method that will be used to process the request. You can see an example of this attribute in the SportsStore application in chapter 9.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-route-*<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Attributes whose name begins with <code class=\"fm-code-in-text1\">asp-route-<\/code> are used to specify additional values for the action attribute URL so that the <code class=\"fm-code-in-text1\">asp-route-id<\/code> attribute is used to provide a value for the <code class=\"fm-code-in-text1\">id<\/code> segment to the routing system.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-route<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the name of the route that will be used to generate the URL for the <code class=\"fm-code-in-text1\">action<\/code> attribute.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-antiforgery<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute controls whether anti-forgery information is added to the view, as described in the \u201cUsing the Anti-forgery Feature\u201d section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-fragment<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies a fragment for the generated URL.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"fm-head2\">Setting the form target<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">FormTagHelper<\/code> transforms <code class=\"fm-code-in-text\">form<\/code> elements so they target an action method or Razor Page without the need for hard-coded URLs. The attributes supported by this tag helper work in the same way as for anchor elements, described in chapter 26, and use attributes to provide values that help generate URLs through the ASP.NET Core routing system. Listing 27.10 modifies the <code class=\"fm-code-in-text\">form<\/code> element in the <code class=\"fm-code-in-text\">Form<\/code> view to apply the tag helper.<a id=\"calibre_link-2421\"><\/a><a id=\"calibre_link-2422\"><\/a><a id=\"calibre_link-2423\"><\/a><a id=\"calibre_link-2424\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> If a <code class=\"fm-code-in-text1\">form<\/code> element is defined without a <code class=\"fm-code-in-text1\">method<\/code> attribute, then the tag helper will add one with the <code class=\"fm-code-in-text1\">post<\/code> value, meaning that the form will be submitted using an HTTP POST request. This can lead to surprising results if you omitted the <code class=\"fm-code-in-text1\">method<\/code> attribute because you expect the browser to follow the HTML5 specification and send the form using an HTTP GET request. It is a good idea to always specify the <code class=\"fm-code-in-text1\">method<\/code> attribute so that it is obvious how the form should be submitted.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.10 Using a tag helper in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n<b class=\"fm-bold\">&lt;form asp-action=\"submitform\" method=\"post\"&gt;<\/b>\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        &lt;input class=\"form-control\" name=\"Name\" value=\"@Model.Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-action<\/code> attribute is used to specify the name of the action that will receive the HTTP request. The routing system is used to generate the URLs, just as for the anchor elements described in chapter 26. The <code class=\"fm-code-in-text\">asp-controller<\/code> attribute has not been used in listing 27.10, which means the controller that rendered the view will be used in the URL.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-page<\/code> attribute is also used to select a Razor Page as the target for the form, as shown in listing 27.11.<a id=\"calibre_link-2425\"><\/a><a id=\"calibre_link-985\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.11 Setting the form target in the FormHandler.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"m-2\"&gt;\n    &lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n    <b class=\"fm-bold\">&lt;form asp-page=\"FormHandler\" method=\"post\"&gt;<\/b>\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Name&lt;\/label&gt;\n            &lt;input class=\"form-control\" name=\"Name\" \n                value=\"@Model.Product?.Name\" \/&gt;\n        &lt;\/div&gt;\n        &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n    &lt;\/form&gt;\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core, use a browser to navigate to http:\/\/localhost:5000\/controllers\/form, and examine the HTML received by the browser; you will see that the tag helper has added the <code class=\"fm-code-in-text\">action<\/code> attribute to the <code class=\"fm-code-in-text\">form<\/code> element like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;form method=\"post\" <b class=\"fm-bold\">action=\"\/controllers\/Form\/submitform\"<\/b>&gt;\n...<\/pre>\n<p class=\"body\">The routing system is used to generate a URL that will target the specified action method, which means that changes to the routing configuration will be reflected automatically in the form URL. Request http:\/\/localhost:5000\/pages\/form, and you will see that the <code class=\"fm-code-in-text\">form<\/code> element has been transformed to target the page URL, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;form method=\"post\" <b class=\"fm-bold\">action=\"\/pages\/form<\/b>\"&gt;\n...<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-509\">27.3.2 Transforming form buttons<\/h3>\n<p class=\"body\">The buttons that send forms can be defined outside of the <code class=\"fm-code-in-text\">form<\/code> element. In these situations, the button has a <code class=\"fm-code-in-text\">form<\/code> attribute whose value corresponds to the <code class=\"fm-code-in-text\">id<\/code> attribute of the form element it relates to and a <code class=\"fm-code-in-text\">formaction<\/code> attribute that specifies the target URL for the form.<\/p>\n<p class=\"body\">The tag helper will generate the <code class=\"fm-code-in-text\">formaction<\/code> attribute through the <code class=\"fm-code-in-text\">asp-action<\/code>, <code class=\"fm-code-in-text\">asp-controller<\/code>, or <code class=\"fm-code-in-text\">asp-page<\/code> attributes, as shown in listing 27.12.<a id=\"calibre_link-2426\"><\/a><a id=\"calibre_link-2427\"><\/a><a id=\"calibre_link-2428\"><\/a><a id=\"calibre_link-971\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.12 Transforming a button in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n<b class=\"fm-bold\">&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;<\/b>\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        &lt;input class=\"form-control\" name=\"Name\" value=\"@Model.Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n<b class=\"fm-bold\">&lt;button form=\"htmlform\" asp-action=\"submitform\"<\/b> \n        <b class=\"fm-bold\">class=\"btn btn-primary mt-2\"&gt;<\/b>\n    <b class=\"fm-bold\">Submit (Outside Form)<\/b>\n<b class=\"fm-bold\">&lt;\/button&gt;<\/b><\/pre>\n<p class=\"body\">The value of the <code class=\"fm-code-in-text\">id<\/code> attribute added to the <code class=\"fm-code-in-text\">form<\/code> element is used by the <code class=\"fm-code-in-text\">button<\/code> as the value of the <code class=\"fm-code-in-text\">form<\/code> attribute, which tells the browser which form to submit when the button is clicked. The attributes described in table 27.3 are used to identify the target for the form, and the tag helper will use the routing system to generate a URL when the view is rendered. Listing 27.13 applies the same technique to the Razor Page.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.13 Transforming a button in the FormHandler.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"m-2\"&gt;\n    &lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n        \n    <b class=\"fm-bold\">&lt;form asp-page=\"FormHandler\" method=\"post\" id=\"htmlform\"&gt;<\/b>\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Name&lt;\/label&gt;\n            &lt;input class=\"form-control\" name=\"Name\" \n                value=\"@Model.Product?.Name\" \/&gt;\n        &lt;\/div&gt;\n        &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n    &lt;\/form&gt;\n        \n    <b class=\"fm-bold\">&lt;button form=\"htmlform\" asp-page=\"FormHandler\"<\/b> \n            <b class=\"fm-bold\">class=\"btn btn-primary mt-2\"&gt;<\/b>\n        <b class=\"fm-bold\">Submit (Outside Form)<\/b>\n    <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core, use a browser to request http:\/\/localhost:5000\/controllers\/form or http:\/\/localhost:5000\/pages\/form, and inspect the HTML sent to the browser. You will see the <code class=\"fm-code-in-text\">button<\/code> element outside of the form has been transformed like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;button form=\"htmlform\" class=\"btn btn-primary mt-2\" \n        <b class=\"fm-bold\">formaction=\"\/controller\/Form\/submitform\"<\/b>&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;\n...<\/pre>\n<p class=\"body\">Clicking the button submits the form, just as for a button that is defined within the form element, as shown in figure 27.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre278\" src=\"\/images\/proaspnetcore7\/000285.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 27.5 Defining a button outside of a form element<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-510\">27.4 Working with input elements<\/h2>\n<p class=\"body\"><a id=\"calibre_link-986\"><\/a>The <code class=\"fm-code-in-text\">input<\/code> element is the backbone of HTML forms and provides the main means by which a user can provide an application with unstructured data. The <code class=\"fm-code-in-text\">InputTagHelper<\/code> class is used to transform <code class=\"fm-code-in-text\">input<\/code> elements so they reflect the data type and format of the view model property they are used to gather, using the attributes described in table 27.4.<a id=\"calibre_link-2429\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 27.4 The built-in tag helper attributes for input elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2430\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-for<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the view model property that the <code class=\"fm-code-in-text1\">input<\/code> element represents.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-format<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify a format used for the value of the view model property that the <code class=\"fm-code-in-text1\">input<\/code> element represents.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-for<\/code> attribute is set to the name of a view model property, which is then used to set the <code class=\"fm-code-in-text\">name<\/code>, <code class=\"fm-code-in-text\">id<\/code>, <code class=\"fm-code-in-text\">type<\/code>, and <code class=\"fm-code-in-text\">value<\/code> attributes of the <code class=\"fm-code-in-text\">input<\/code> element. Listing 27.14 modifies the <code class=\"fm-code-in-text\">input<\/code> element in the controller view to use the <code class=\"fm-code-in-text\">asp-for<\/code> attribute.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.14 Configuring an input element in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;<\/b>\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;button form=\"htmlform\" asp-action=\"submitform\" \n        class=\"btn btn-primary mt-2\"&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;<\/pre>\n<p class=\"body\">This tag helper uses a model expression, described in chapter 25, which is why the value for the <code class=\"fm-code-in-text\">asp-for<\/code> attribute is specified without the <code class=\"fm-code-in-text\">@<\/code> character. If you restart ASP.NET Core and inspect the HTML the application returns when using a browser to request http:\/\/localhost:5000\/controllers\/form, you will see the tag helper has transformed the <code class=\"fm-code-in-text\">input<\/code> element like this:<a id=\"calibre_link-2431\"><\/a><a id=\"calibre_link-2432\"><\/a><a id=\"calibre_link-2433\"><\/a><a id=\"calibre_link-990\"><\/a><\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;Name&lt;\/label&gt;\n    &lt;input class=\"form-control\" <b class=\"fm-bold\">type=\"text\" data-val=\"true\"<\/b> \n        <b class=\"fm-bold\">data-val-required=\"The Name field is required.\" id=\"Name\"<\/b> \n        <b class=\"fm-bold\">name=\"Name\" value=\"Kayak\"<\/b>&gt;\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">The values for the <code class=\"fm-code-in-text\">id<\/code> and <code class=\"fm-code-in-text\">name<\/code> attributes are obtained through the model expression, ensuring that you don\u2019t introduce typos when creating the form. The other attributes are more complex and are described in the sections that follow or in chapter 29, where I explain the ASP.NET Core support for validating data.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Selecting Model Properties in Razor Pages<\/p>\n<p class=\"fm-sidebar-text\">The <code class=\"fm-code-in-text1\">asp-for<\/code> attribute for this and the other tag helpers described in this chapter can be used for Razor Pages, but the value for the <code class=\"fm-code-in-text1\">name<\/code> and <code class=\"fm-code-in-text1\">id<\/code> attributes in the transformed element includes the name of the page model property. For example, this element selects the <code class=\"fm-code-in-text1\">Name<\/code> property through the page model\u2019s <code class=\"fm-code-in-text1\">Product<\/code> property:<\/p>\n<pre class=\"programlisting\">...\n&lt;input class=\"form-control\" <b class=\"fm-bold\">asp-for=\"Product.Name\"<\/b> \/&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">The transformed element will have the following <code class=\"fm-code-in-text1\">id<\/code> and <code class=\"fm-code-in-text1\">name<\/code> attributes:<\/p>\n<pre class=\"programlisting\">...\n&lt;input class=\"form-control\" type=\"text\" <b class=\"fm-bold\">id=\"Product_Name\"<\/b> \n    <b class=\"fm-bold\">name=\"Product.Name\"<\/b> &gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">This difference is important when using the model binding feature to receive form data, as described in chapter 28.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-511\">27.4.1 Transforming the input element type attribute<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">input<\/code> element\u2019s <code class=\"fm-code-in-text\">type<\/code> attribute tells the browser how to display the element and how it should restrict the values the user enters. The <code class=\"fm-code-in-text\">input<\/code> element in listing 27.14 is configured to the <code class=\"fm-code-in-text\">text<\/code> type, which is the default <code class=\"fm-code-in-text\">input<\/code> element type and offers no restrictions. Listing 27.15 adds another <code class=\"fm-code-in-text\">input<\/code> element to the form, which will provide a more useful demonstration of how the <code class=\"fm-code-in-text\">type<\/code> attribute is handled.<a id=\"calibre_link-2434\"><\/a><a id=\"calibre_link-992\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.15 Adding an input element in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;label&gt;Id&lt;\/label&gt;<\/b>\n        <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"ProductId\" \/&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;button form=\"htmlform\" asp-action=\"submitform\" \n        class=\"btn btn-primary mt-2\"&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;<\/pre>\n<p class=\"body\">The new element uses the <code class=\"fm-code-in-text\">asp-for<\/code> attribute to select the view model\u2019s <code class=\"fm-code-in-text\">ProductId<\/code> property. Restart ASP.NET Core and a browser to request http:\/\/localhost:5000\/controllers\/form to see how the tag helper has transformed the element.<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;Id&lt;\/label&gt;\n    <b class=\"fm-bold\">&lt;input class=\"form-control\" type=\"number\" data-val=\"true\"<\/b> \n        <b class=\"fm-bold\">data-val-required=\"The ProductId field is required.\"<\/b> \n        <b class=\"fm-bold\">id=\"ProductId\" name=\"ProductId\" value=\"1\"&gt;<\/b>\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">The value of the <code class=\"fm-code-in-text\">type<\/code> attribute is determined by the type of the view model property specified by the <code class=\"fm-code-in-text\">asp-for<\/code> attribute. The type of the <code class=\"fm-code-in-text\">ProductId<\/code> property is the C# <code class=\"fm-code-in-text\">long<\/code> type, which has led the tag helper to set the <code class=\"fm-code-in-text\">input<\/code> element\u2019s type attribute to <code class=\"fm-code-in-text\">number<\/code>, which restricts the element so it will accept only numeric characters. The <code class=\"fm-code-in-text\">data-val<\/code> and <code class=\"fm-code-in-text\">data-val-required<\/code> attributes are added to the <code class=\"fm-code-in-text\">input<\/code> element to assist with validation, which is described in chapter 29. Table 27.5 describes how different C# types are used to set the <code class=\"fm-code-in-text\">type<\/code> attribute of <code class=\"fm-code-in-text\">input<\/code> elements.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> There is latitude in how the <code class=\"fm-code-in-text1\">type<\/code> attribute is interpreted by browsers. Not all browsers respond to all the <code class=\"fm-code-in-text1\">type<\/code> values that are defined in the HTML specification, and when they do, there are differences in how they are implemented. The <code class=\"fm-code-in-text1\">type<\/code> attribute can be a useful hint for the kind of data that you are expecting in a form, but you should use the model validation feature to ensure that users provide usable data, as described in chapter 29.<\/p>\n<p class=\"fm-table-caption\">Table 27.5 C# property types and the input type elements they generate<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2435\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">C# Type<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">input Element type Attribute<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">byte<\/code>, <code class=\"fm-code-in-text1\">sbyte<\/code>, <code class=\"fm-code-in-text1\">int<\/code>, <code class=\"fm-code-in-text1\">uint<\/code>, <code class=\"fm-code-in-text1\">short<\/code>, <code class=\"fm-code-in-text1\">ushort<\/code>, <code class=\"fm-code-in-text1\">long<\/code>, <code class=\"fm-code-in-text1\">ulong<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">number<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">float<\/code>, <code class=\"fm-code-in-text1\">double<\/code>, <code class=\"fm-code-in-text1\">decimal<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">text<\/code>, with additional attributes for model validation, as described in chapter 29<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">bool<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">checkbox<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">string<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">text<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">DateTime<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">datetime<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">float<\/code>, <code class=\"fm-code-in-text\">double<\/code>, and <code class=\"fm-code-in-text\">decimal<\/code> types produce <code class=\"fm-code-in-text\">input<\/code> elements whose <code class=\"fm-code-in-text\">type<\/code> is <code class=\"fm-code-in-text\">text<\/code> because not all browsers allow the full range of characters that can be used to express legal values of this type. To provide feedback to the user, the tag helper adds attributes to the <code class=\"fm-code-in-text\">input<\/code> element that are used with the validation features described in chapter 29.<\/p>\n<p class=\"body\">You can override the default mappings shown in table 27.5 by explicitly defining the <code class=\"fm-code-in-text\">type<\/code> attribute on <code class=\"fm-code-in-text\">input<\/code> elements. The tag helper won\u2019t override the value you define, which allows you to specify a <code class=\"fm-code-in-text\">type<\/code> attribute value.<\/p>\n<p class=\"body\">The drawback of this approach is that you must remember to set the <code class=\"fm-code-in-text\">type<\/code> attribute in all the views where <code class=\"fm-code-in-text\">input<\/code> elements are generated for a given model property. A more elegant&mdash;and reliable approach&mdash;is to apply one of the attributes described in table 27.6 to the property in the C# model class.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The tag helper will set the <code class=\"fm-code-in-text1\">type<\/code> attribute of <code class=\"fm-code-in-text1\">input<\/code> elements to <code class=\"fm-code-in-text1\">text<\/code> if the model property isn\u2019t one of the types in table 27.5 and has not been decorated with an attribute.<\/p>\n<p class=\"fm-table-caption\">Table 27.6 The input type elements attributes<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2436\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Attribute<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">input Element type Attribute<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[HiddenInput]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">hidden<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[Text]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">text<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[Phone]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">tel<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[Url]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">url<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[EmailAddress]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">email<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[DataType(DataType.Password)]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">password<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[DataType(DataType.Time)]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">time<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[DataType(DataType.Date)]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">date<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-512\">27.4.2 Formatting input element values<\/h3>\n<p class=\"body\">When the action method provides the view with a view model object, the tag helper uses the value of the property given to the <code class=\"fm-code-in-text\">asp-for<\/code> attribute to set the <code class=\"fm-code-in-text\">input<\/code> element\u2019s <code class=\"fm-code-in-text\">value<\/code> attribute. The <code class=\"fm-code-in-text\">asp-format<\/code> attribute is used to specify how that data value is formatted. To demonstrate the default formatting, listing 27.16 adds a new <code class=\"fm-code-in-text\">input<\/code> element to the <code class=\"fm-code-in-text\">Form<\/code> view.<a id=\"calibre_link-2437\"><\/a><a id=\"calibre_link-989\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.16 Adding an element in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Id&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"ProductId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;label&gt;Price&lt;\/label&gt;<\/b>\n        <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;button form=\"htmlform\" asp-action=\"submitform\" \n        class=\"btn btn-primary mt-2\"&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core, use a browser to navigate to http:\/\/localhost:5000\/controllers\/form\/index\/5, and examine the HTML the browser receives. By default, the <code class=\"fm-code-in-text\">value<\/code> of the <code class=\"fm-code-in-text\">input<\/code> element is set using the value of the model property, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;input class=\"form-control\" type=\"text\" data-val=\"true\" \n    data-val-number=\"The field Price must be a number.\" \n    data-val-required=\"The Price field is required.\" \n    id=\"Price\" name=\"Price\" <b class=\"fm-bold\">value=\"79500.00\"<\/b>&gt;\n...<\/pre>\n<p class=\"body\">This format, with two decimal places, is how the value is stored in the database. In chapter 26, I used the <code class=\"fm-code-in-text\">Column<\/code> attribute to select a SQL type to store <code class=\"fm-code-in-text\">Price<\/code> values, like this:<a id=\"calibre_link-2438\"><\/a><a id=\"calibre_link-987\"><\/a><\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">[Column(TypeName = \"decimal(8, 2)\")]<\/b>\npublic decimal Price { get; set; }\n...<\/pre>\n<p class=\"body\">This type specifies a maximum precision of eight digits, two of which will appear after the decimal place. This allows a maximum value of 999,999.99, which is enough to represent prices for most online stores. The <code class=\"fm-code-in-text\">asp-format<\/code> attribute accepts a format string that will be passed to the standard C# string formatting system, as shown in listing 27.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.17 Formatting a data value in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Id&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"ProductId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Price&lt;\/label&gt;\n        <b class=\"fm-bold\">&lt;input class=\"form-control\"<\/b> \n            <b class=\"fm-bold\">asp-for=\"Price\" asp-format=\"{0:#,###.00}\" \/&gt;<\/b>\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;button form=\"htmlform\" asp-action=\"submitform\" \n        class=\"btn btn-primary mt-2\"&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;<\/pre>\n<p class=\"body\">The attribute value is used verbatim, which means you must include the curly brace characters and the <code class=\"fm-code-in-text\">0:<\/code> reference, as well as the format you require. Refresh the browser, and you will see that the value for the <code class=\"fm-code-in-text\">input<\/code> element has been formatted, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;input class=\"form-control\" type=\"text\" data-val=\"true\" \n    data-val-number=\"The field Price must be a number.\" \n    data-val-required=\"The Price field is required.\" \n    id=\"Price\" name=\"Price\" <b class=\"fm-bold\">value=\"79,500.00\"<\/b>&gt;\n... <\/pre>\n<p class=\"body\">This feature should be used with caution because you must ensure that the rest of the application is configured to support the format you use and that the format you create contains only legal characters for the <code class=\"fm-code-in-text\">input<\/code> element type.<\/p>\n<p class=\"fm-head2\">Applying formatting via the model class<\/p>\n<p class=\"body\">If you always want to use the same formatting for a model property, then you can decorate the C# class with the <code class=\"fm-code-in-text\">DisplayFormat<\/code> attribute, which is defined in the <code class=\"fm-code-in-text\">System.ComponentModel.DataAnnotations<\/code> namespace. The <code class=\"fm-code-in-text\">DisplayFormat<\/code> attribute requires two arguments to format a data value: the <code class=\"fm-code-in-text\">DataFormatString<\/code> argument specifies the formatting string, and setting the <code class=\"fm-code-in-text\">ApplyFormatInEditMode<\/code> to <code class=\"fm-code-in-text\">true<\/code> specifies that formatting should be used when values are being applied to elements used for editing, including the <code class=\"fm-code-in-text\">input<\/code> element. Listing 27.18 applies the attribute to the <code class=\"fm-code-in-text\">Price<\/code> property of the <code class=\"fm-code-in-text\">Product<\/code> class, specifying a different formatting string from earlier examples.<a id=\"calibre_link-2439\"><\/a><a id=\"calibre_link-988\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.18 Applying a formatting attribute to the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Text.Json.Serialization;\n\nnamespace WebApp.Models {\n    public class Product {\n        \n        public long ProductId { get; set; }\n                \n        public required string Name { get; set; }\n        [Column(TypeName = \"decimal(8, 2)\")]\n        <b class=\"fm-bold\">[DisplayFormat(DataFormatString = \"{0:c2}\",<\/b> \n            <b class=\"fm-bold\">ApplyFormatInEditMode = true)]<\/b>\n        public decimal Price { get; set; }\n                \n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        public long SupplierId { get; set; }\n                \n        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-format<\/code> attribute takes precedence over the <code class=\"fm-code-in-text\">DisplayFormat<\/code> attribute, so I have removed the attribute from the view, as shown in listing 27.19.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.19 Removing an attribute in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Id&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"ProductId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Price&lt;\/label&gt;\n        <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;<\/b>\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;button form=\"htmlform\" asp-action=\"submitform\" \n        class=\"btn btn-primary mt-2\"&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/controllers\/form\/index\/5, and you will see that the formatting string defined by the attribute has been applied, as shown in figure 27.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre279\" src=\"\/images\/proaspnetcore7\/000286.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 27.6 Formatting data values<\/p>\n<\/div>\n<p class=\"body\">I chose this format to demonstrate the way the formatting attribute works, but as noted previously, care must be taken to ensure that the application is able to process the formatted values using the model binding and validation features described in chapters 28 and 29.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-513\">27.4.3 Displaying values from related data in input elements<\/h3>\n<p class=\"body\">When using Entity Framework Core, you will often need to display data values that are obtained from related data, which is easily done using the <code class=\"fm-code-in-text\">asp-for<\/code> attribute because a model expression allows the nested navigation properties to be selected. First, listing 27.20 includes related data in the view model object provided to the view.<a id=\"calibre_link-2440\"><\/a><a id=\"calibre_link-991\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.20 Including related data in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n<b class=\"fm-bold\">using Microsoft.EntityFrameworkCore;<\/b>\n\nnamespace WebApp.Controllers {\n\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            <b class=\"fm-bold\">return View(\"Form\", await context.Products<\/b>\n                <b class=\"fm-bold\">.Include(p =&gt; p.Category)<\/b>\n                <b class=\"fm-bold\">.Include(p =&gt; p.Supplier)<\/b>\n                <b class=\"fm-bold\">.FirstAsync(p =&gt; p.ProductId == id)<\/b>\n            <b class=\"fm-bold\">?? new() { Name = string.Empty });<\/b>\n        }\n                \n        [HttpPost]\n        public IActionResult SubmitForm() {\n            foreach (string key in\n                    Request.Form.Keys.Where(k =&gt; !k.StartsWith(\"_\"))) {\n                TempData[key] = string.Join(\", \",\n                    (string?)Request.Form[key]);\n            }\n            return RedirectToAction(nameof(Results));\n        }\n                \n        public IActionResult Results() {\n            return View();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Notice that I don\u2019t need to worry about dealing with circular references in the related data because the view model object isn\u2019t serialized. The circular reference issue is important only for web service controllers. In listing 27.21, I have updated the <code class=\"fm-code-in-text\">Form<\/code> view to include <code class=\"fm-code-in-text\">input<\/code> elements that use the <code class=\"fm-code-in-text\">asp-for<\/code> attribute to select related data.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.21 Displaying related data in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Id&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"ProductId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Price&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;label&gt;Category&lt;\/label&gt;<\/b>\n        <b class=\"fm-bold\">@{ #pragma warning disable CS8602 }<\/b> \n        <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Category.Name\" \/&gt;<\/b>\n        <b class=\"fm-bold\">@{ #pragma warning restore CS8602 }<\/b> \n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;label&gt;Supplier&lt;\/label&gt;<\/b>\n        <b class=\"fm-bold\">@{ #pragma warning disable CS8602 }<\/b> \n        <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Supplier.Name\" \/&gt;<\/b>\n        <b class=\"fm-bold\">@{ #pragma warning restore CS8602 }<\/b> \n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;button form=\"htmlform\" asp-action=\"submitform\" \n        class=\"btn btn-primary mt-2\"&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;<\/pre>\n<p class=\"body\">The value of the <code class=\"fm-code-in-text\">asp-for<\/code> attribute is expressed relative to the view model object and can include nested properties, allowing me to select the <code class=\"fm-code-in-text\">Name<\/code> properties of the related objects that Entity Framework Core has assigned to the <code class=\"fm-code-in-text\">Category<\/code> and <code class=\"fm-code-in-text\">Supplier<\/code> navigation properties.<\/p>\n<p class=\"body\">As I explained in chapter 25, the null conditional operator cannot be used in model expressions. This presents a problem when selecting nullable-related data properties, such as the <code class=\"fm-code-in-text\">Product.Category<\/code> and <code class=\"fm-code-in-text\">Product.Supplier<\/code> properties. In chapter 25, I addressed this limitation by changing the type of a property so that it was not nullable, but this isn\u2019t always possible, especially when a nullable property has been used to indicate a specific condition.<\/p>\n<p class=\"body\">In listing 27.21, I used the <code class=\"fm-code-in-text\">#pragma warning<\/code> expression to disable code analysis for warning <code class=\"fm-code-in-text\">CS8602<\/code>, which is the warning generated when a nullable value isn\u2019t accessed safely. The tag helper is able to deal with <code class=\"fm-code-in-text\">null<\/code> values when processing the <code class=\"fm-code-in-text\">asp-for<\/code> attribute, which means that the warning doesn\u2019t indicate a potential problem.<a id=\"calibre_link-2441\"><\/a><a id=\"calibre_link-2442\"><\/a><a id=\"calibre_link-995\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can elect to simply ignore the warnings the compiler produces, but I prefer to either address the underlying issue or explicitly disable the warning so that it is obvious that the warning has been investigated and deliberately ignored, rather than not noticed.<\/p>\n<p class=\"body\">The same technique is used in Razor Pages, except that the properties are expressed relative to the page model object, as shown in listing 27.22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.22 Displayed related data in the FormHandler.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/form\/{id:long?}\"\n@model FormHandlerModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n<b class=\"fm-bold\">@using Microsoft.EntityFrameworkCore<\/b>\n\n&lt;div class=\"m-2\"&gt;\n    &lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n    &lt;form asp-page=\"FormHandler\" method=\"post\" id=\"htmlform\"&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Name&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Product.Name\" \/&gt;<\/b>\n        &lt;\/div&gt;\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;Price&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Product.Price\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;Category&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">@{ #pragma warning disable CS8602 }<\/b>\n            <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Product.Category.Name\" \/&gt;<\/b>\n            <b class=\"fm-bold\">@{ #pragma warning restore CS8602 }<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;Supplier&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">@{ #pragma warning disable CS8602 }<\/b>\n            <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Product.Supplier.Name\" \/&gt;<\/b>\n            <b class=\"fm-bold\">@{ #pragma warning restore CS8602 }<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n    &lt;\/form&gt;\n    &lt;button form=\"htmlform\" asp-page=\"FormHandler\"\n            class=\"btn btn-primary mt-2\"&gt;\n        Submit (Outside Form)\n    &lt;\/button&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    [IgnoreAntiforgeryToken]\n    public class FormHandlerModel : PageModel {\n        private DataContext context;\n                \n        public FormHandlerModel(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        <b class=\"fm-bold\">public Product Product { get; set; }<\/b> \n            <b class=\"fm-bold\">= new() { Name = string.Empty };<\/b>\n        public async Task OnGetAsync(long id = 1) {\n            <b class=\"fm-bold\">Product = await context.Products<\/b>\n                <b class=\"fm-bold\">.Include(p =&gt; p.Category)<\/b>\n                <b class=\"fm-bold\">.Include(p =&gt; p.Supplier)<\/b>\n                <b class=\"fm-bold\">.FirstAsync(p =&gt; p.ProductId == id);<\/b>\n        }\n                \n        public IActionResult OnPost() {\n            foreach (string key in Request.Form.Keys\n                    .Where(k =&gt; !k.StartsWith(\"_\"))) {\n                TempData[key] = string.Join(\", \", \n                    (string?)Request.Form[key]);\n            }\n            return RedirectToPage(\"FormResults\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">To see the effect, restart ASP.NET Core so the changes to the controller take effect, and use a browser to request http:\/\/localhost:5000\/controllers\/form, which produces the response shown on the left of figure 27.7. Use the browser to request http:\/\/localhost:5000\/pages\/form, and you will see the same features used by the Razor Page, as shown on the right of figure 27.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre280\" src=\"\/images\/proaspnetcore7\/000287.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 27.7 Displaying related data<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-514\">27.5 Working with label elements<\/h2>\n<p class=\"body\">The <code class=\"fm-code-in-text\">LabelTagHelper<\/code> class is used to transform <code class=\"fm-code-in-text\">label<\/code> elements so the <code class=\"fm-code-in-text\">for<\/code> attribute is set consistently with the approach used to transform <code class=\"fm-code-in-text\">input<\/code> elements. Table 27.7 describes the attribute supported by this tag helper.<a id=\"calibre_link-2443\"><\/a><a id=\"calibre_link-2444\"><\/a><a id=\"calibre_link-993\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 27.7 The built-in tag helper attribute for label elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2445\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-for<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the view model property that the <code class=\"fm-code-in-text1\">label<\/code> element describes.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The tag helper sets the content of the <code class=\"fm-code-in-text\">label<\/code> element so that it contains the name of the selected view model property. The tag helper also sets the <code class=\"fm-code-in-text\">for<\/code> attribute, which denotes an association with a specific <code class=\"fm-code-in-text\">input<\/code> element. This aids users who rely on screen readers and allows an <code class=\"fm-code-in-text\">input<\/code> element to gain the focus when its associated <code class=\"fm-code-in-text\">label<\/code> is clicked.<\/p>\n<p class=\"body\">Listing 27.23 applies the <code class=\"fm-code-in-text\">asp-for<\/code> attribute to the <code class=\"fm-code-in-text\">Form<\/code> view to associate each <code class=\"fm-code-in-text\">label<\/code> element with the <code class=\"fm-code-in-text\">input<\/code> element that represents the same view model property.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.23 Transforming label elements in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        <b class=\"fm-bold\">&lt;label asp-for=\"ProductId\"&gt;&lt;\/label&gt;<\/b>\n        &lt;input class=\"form-control\" asp-for=\"ProductId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        <b class=\"fm-bold\">&lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;<\/b>\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        <b class=\"fm-bold\">&lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;<\/b>\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        @{ #pragma warning disable CS8602 } \n        <b class=\"fm-bold\">&lt;label asp-for=\"Category.Name\"&gt;Category&lt;\/label&gt;<\/b>\n        &lt;input class=\"form-control\" asp-for=\"Category.Name\" \/&gt;\n        @{ #pragma warning restore CS8602 } \n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        @{ #pragma warning disable CS8602 } \n        <b class=\"fm-bold\">&lt;label asp-for=\"Supplier.Name\"&gt;Supplier&lt;\/label&gt;<\/b>\n        &lt;input class=\"form-control\" asp-for=\"Supplier.Name\" \/&gt;\n        @{ #pragma warning restore CS8602 } \n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;button form=\"htmlform\" asp-action=\"submitform\" \n        class=\"btn btn-primary mt-2\"&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;<\/pre>\n<p class=\"body\">You can override the content for a <code class=\"fm-code-in-text\">label<\/code> element by defining it yourself, which is what I have done for the related data properties in listing 27.23. The tag helper would have set the content for both these <code class=\"fm-code-in-text\">label<\/code> elements to be <code class=\"fm-code-in-text\">Name<\/code>, which is not a useful description. Defining the element content means the <code class=\"fm-code-in-text\">for<\/code> attribute will be applied, but a more useful name will be displayed to the user. Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/controllers\/form to see the names used for each element, as shown in figure 27.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre281\" src=\"\/images\/proaspnetcore7\/000288.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 27.8 Transforming label elements<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-515\">27.6 Working with select and option elements<\/h2>\n<p class=\"body\">The <code class=\"fm-code-in-text\">select<\/code> and <code class=\"fm-code-in-text\">option<\/code> elements are used to provide the user with a fixed set of choices, rather than the open data entry that is possible with an <code class=\"fm-code-in-text\">input<\/code> element. The <code class=\"fm-code-in-text\">SelectTagHelper<\/code> is responsible for transforming <code class=\"fm-code-in-text\">select<\/code> elements and supports the attributes described in table 27.8.<a id=\"calibre_link-2446\"><\/a><a id=\"calibre_link-2447\"><\/a><a id=\"calibre_link-997\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 27.8 The built-in tag helper attributes for select elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2448\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-for<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the view or page model property that the <code class=\"fm-code-in-text1\">select<\/code> element represents.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-items<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify a source of values for the option elements contained within the <code class=\"fm-code-in-text1\">select<\/code> element.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-for<\/code> attribute sets the value of the <code class=\"fm-code-in-text\">for<\/code> and <code class=\"fm-code-in-text\">id<\/code> attributes to reflect the model property that it receives. In listing 27.24, I have replaced the <code class=\"fm-code-in-text\">input<\/code> element for the category with a <code class=\"fm-code-in-text\">select<\/code> element that presents the user with a fixed range of values.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.24 Using a select element in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"ProductId\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"ProductId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        @{ #pragma warning disable CS8602 } \n        &lt;label asp-for=\"Category.Name\"&gt;Category&lt;\/label&gt;\n        @{ #pragma warning restore CS8602 } \n        <b class=\"fm-bold\">&lt;select class=\"form-control\" asp-for=\"CategoryId\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;option value=\"1\"&gt;Watersports&lt;\/option&gt;<\/b>\n            <b class=\"fm-bold\">&lt;option value=\"2\"&gt;Soccer&lt;\/option&gt;<\/b>\n            <b class=\"fm-bold\">&lt;option value=\"3\"&gt;Chess&lt;\/option&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/select&gt;<\/b>\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        @{ #pragma warning disable CS8602 } \n        &lt;label asp-for=\"Supplier.Name\"&gt;Supplier&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Supplier.Name\" \/&gt;\n        @{ #pragma warning restore CS8602 } \n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;button form=\"htmlform\" asp-action=\"submitform\" \n        class=\"btn btn-primary mt-2\"&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;<\/pre>\n<p class=\"body\">I have manually populated the <code class=\"fm-code-in-text\">select<\/code> element with <code class=\"fm-code-in-text\">option<\/code> elements that provide a range of categories for the user to choose from. If you use a browser to request http:\/\/localhost:5000\/controllers\/form\/index\/5 and examine the HTML response, you will see that the tag helper has transformed the <code class=\"fm-code-in-text\">select<\/code> element like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"Category_Name\"&gt;Category&lt;\/label&gt;\n    <b class=\"fm-bold\">&lt;select class=\"form-control\" data-val=\"true\"<\/b> \n            <b class=\"fm-bold\">data-val-required=\"The CategoryId field is required.\"<\/b> \n            <b class=\"fm-bold\">id=\"CategoryId\" name=\"CategoryId\"&gt;<\/b>\n        &lt;option value=\"1\"&gt;Watersports&lt;\/option&gt;\n        &lt;option value=\"2\" selected=\"selected\"&gt;Soccer&lt;\/option&gt;\n        &lt;option value=\"3\"&gt;Chess&lt;\/option&gt;\n    &lt;\/select&gt;\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">Notice that selected attribute has been added to the <code class=\"fm-code-in-text\">option<\/code> element that corresponds to the view model\u2019s <code class=\"fm-code-in-text\">CategoryId<\/code> value, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;option value=\"2\" <b class=\"fm-bold\">selected=\"selected\"<\/b>&gt;Soccer&lt;\/option&gt;\n...<\/pre>\n<p class=\"body\">The task of selecting an <code class=\"fm-code-in-text\">option<\/code> element is performed by the <code class=\"fm-code-in-text\">OptionTagHelper<\/code> class, which receives instructions from the <code class=\"fm-code-in-text\">SelectTagHelper<\/code> through the <code class=\"fm-code-in-text\">TagHelperContext.Items<\/code> collection, described in chapter 25. The result is that the <code class=\"fm-code-in-text\">select<\/code> element displays the name of the category associated with the <code class=\"fm-code-in-text\">Product<\/code> object\u2019s <code class=\"fm-code-in-text\">CategoryId<\/code> value.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-516\">26.6.1 Populating a select element<\/h3>\n<p class=\"body\">Explicitly defining the <code class=\"fm-code-in-text\">option<\/code> elements for a <code class=\"fm-code-in-text\">select<\/code> element is a useful approach for choices that always have the same possible values but doesn\u2019t help when you need to provide options that are taken from the data model or where you need the same set of options in multiple views and don\u2019t want to manually maintain duplicated content.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">asp-items<\/code> attribute is used to provide the tag helper with a list sequence of <code class=\"fm-code-in-text\">SelectListItem<\/code> objects for which option elements will be generated. Listing 27.25 modifies the <code class=\"fm-code-in-text\">Index<\/code> action of the <code class=\"fm-code-in-text\">Form<\/code> controller to provide the view with a sequence of <code class=\"fm-code-in-text\">SelectListItem<\/code> objects through the view bag.<a id=\"calibre_link-2449\"><\/a><a id=\"calibre_link-2450\"><\/a><a id=\"calibre_link-2451\"><\/a><a id=\"calibre_link-998\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.25 Providing a sequence in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc.Rendering;<\/b>\n\nnamespace WebApp.Controllers {\n\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            <b class=\"fm-bold\">ViewBag.Categories = new SelectList(context.Categories,<\/b> \n                <b class=\"fm-bold\">\"CategoryId\", \"Name\");<\/b>\n            return View(\"Form\", await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstAsync(p =&gt; p.ProductId == id)\n            ?? new() { Name = string.Empty });\n        }\n                \n        [HttpPost]\n        public IActionResult SubmitForm() {\n            foreach (string key in\n                    Request.Form.Keys.Where(k =&gt; !k.StartsWith(\"_\"))) {\n                TempData[key] = string.Join(\", \",\n                    (string?)Request.Form[key]);\n            }\n            return RedirectToAction(nameof(Results));\n        }\n                \n        public IActionResult Results() {\n            return View();\n        }\n    }\n}<\/pre>\n<p class=\"body\"><code class=\"fm-code-in-text\">SelectListItem<\/code> objects can be created directly, but ASP.NET Core provides the <code class=\"fm-code-in-text\">SelectList<\/code> class to adapt existing data sequences. In this case, I pass the sequence of <code class=\"fm-code-in-text\">Category<\/code> objects obtained from the database to the <code class=\"fm-code-in-text\">SelectList<\/code> constructor, along with the names of the properties that should be used as the values and labels for <code class=\"fm-code-in-text\">option<\/code> elements. In listing 27.26, I have updated the <code class=\"fm-code-in-text\">Form<\/code> view to use the <code class=\"fm-code-in-text\">SelectList<\/code>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.26 Using a SelectList in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"ProductId\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"ProductId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        @{ #pragma warning disable CS8602 } \n        &lt;label asp-for=\"Category.Name\"&gt;Category&lt;\/label&gt;\n        @{ #pragma warning restore CS8602 } \n        <b class=\"fm-bold\">&lt;select class=\"form-control\" asp-for=\"CategoryId\"<\/b> \n            <b class=\"fm-bold\">asp-items=\"@ViewBag.Categories\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/select&gt;<\/b>\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        @{ #pragma warning disable CS8602 } \n        &lt;label asp-for=\"Supplier.Name\"&gt;Supplier&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Supplier.Name\" \/&gt;\n        @{ #pragma warning restore CS8602 } \n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;button form=\"htmlform\" asp-action=\"submitform\" \n        class=\"btn btn-primary mt-2\"&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/controllers\/form\/index\/5. There is no visual change to the content presented to the user, but the <code class=\"fm-code-in-text\">option<\/code> elements used to populate the <code class=\"fm-code-in-text\">select<\/code> element have been generated from the database, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"Category_Name\"&gt;Category&lt;\/label&gt;\n    &lt;select class=\"form-control\" data-val=\"true\" \n            data-val-required=\"The CategoryId field is required.\" \n            id=\"CategoryId\" name=\"CategoryId\"&gt;\n        <b class=\"fm-bold\">&lt;option value=\"1\"&gt;Watersports&lt;\/option&gt;<\/b>\n        <b class=\"fm-bold\">&lt;option selected=\"selected\" value=\"2\"&gt;Soccer&lt;\/option&gt;<\/b>\n        <b class=\"fm-bold\">&lt;option value=\"3\"&gt;Chess&lt;\/option&gt;<\/b>\n    &lt;\/select&gt;\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">This approach means that the options presented to the user will automatically reflect new categories added to the database.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-517\">27.7 Working with text areas<\/h2>\n<p class=\"body\">The <code class=\"fm-code-in-text\">textarea<\/code> element is used to solicit a larger amount of text from the user and is typically used for unstructured data, such as notes or observations. The <code class=\"fm-code-in-text\">TextAreaTagHelper<\/code> is responsible for transforming <code class=\"fm-code-in-text\">textarea<\/code> elements and supports the single attribute described in table 27.9.<a id=\"calibre_link-2452\"><\/a><a id=\"calibre_link-999\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 27.9 The built-in tag helper attributes for textarea elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2453\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">asp-for<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify the view model property that the <code class=\"fm-code-in-text1\">textarea<\/code> element represents.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">TextAreaTagHelper<\/code> is relatively simple, and the value provided for the <code class=\"fm-code-in-text\">asp-for<\/code> attribute is used to set the <code class=\"fm-code-in-text\">id<\/code> and <code class=\"fm-code-in-text\">name<\/code> attributes on the <code class=\"fm-code-in-text\">textarea<\/code> element. The value of the property selected by the <code class=\"fm-code-in-text\">asp-for<\/code> attribute is used as the content for the <code class=\"fm-code-in-text\">textarea<\/code> element. Listing 27.27 replaces the <code class=\"fm-code-in-text\">input<\/code> element for the <code class=\"fm-code-in-text\">Supplier.Name<\/code> property with a text area to which the <code class=\"fm-code-in-text\">asp-for<\/code> attribute has been applied.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.27 Using a text area in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"ProductId\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"ProductId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        @{ #pragma warning disable CS8602 } \n        &lt;label asp-for=\"Category.Name\"&gt;Category&lt;\/label&gt;\n        @{ #pragma warning restore CS8602 } \n        &lt;select class=\"form-control\" asp-for=\"CategoryId\" \n            asp-items=\"@ViewBag.Categories\"&gt;\n        &lt;\/select&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        @{ #pragma warning disable CS8602 } \n        &lt;label asp-for=\"Supplier.Name\"&gt;Supplier&lt;\/label&gt;\n        <b class=\"fm-bold\">&lt;textarea class=\"form-control\" asp-for=\"Supplier.Name\"&gt;&lt;\/textarea&gt;<\/b>\n        @{ #pragma warning restore CS8602 } \n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;button form=\"htmlform\" asp-action=\"submitform\" \n        class=\"btn btn-primary mt-2\"&gt;\n    Submit (Outside Form)\n&lt;\/button&gt;<a id=\"calibre_link-2454\"><\/a><\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/controllers\/form and examine the HTML received by the browser to see the transformation of the <code class=\"fm-code-in-text\">textarea<\/code> element.<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"Supplier_Name\"&gt;Supplier&lt;\/label&gt;\n    <b class=\"fm-bold\">&lt;textarea class=\"form-control\" data-val=\"true\"<\/b> \n            <b class=\"fm-bold\">data-val-required=\"The Name field is required.\"<\/b>\n            <b class=\"fm-bold\">id=\"Supplier_Name\"<\/b>   \n            <b class=\"fm-bold\">name=\"Supplier.Name\"&gt;<\/b>\n        <b class=\"fm-bold\">Splash Dudes<\/b>\n    <b class=\"fm-bold\">&lt;\/textarea&gt;<\/b>\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">TextAreaTagHelper<\/code> is relatively simple, but it provides consistency with the rest of the form element tag helpers that I have described in this chapter.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-518\">27.8 Using the anti-forgery feature<\/h2>\n<p class=\"body\">When I defined the controller action method and page handler methods that process form data, I filtered out form data whose name begins with an underscore, like this:<a id=\"calibre_link-2455\"><\/a><a id=\"calibre_link-975\"><\/a><\/p>\n<pre class=\"programlisting\">...\n[HttpPost]\npublic IActionResult SubmitForm() {\n    foreach (string key in Request.Form.Keys\n            <b class=\"fm-bold\">.Where(k =&gt; !k.StartsWith(\"_\"))) {<\/b>\n        TempData[key] = string.Join(\", \", Request.Form[key]);\n    }\n    return RedirectToAction(nameof(Results));\n}\n...<\/pre>\n<p class=\"body\">I applied this filter to focus on the values provided by the HTML elements in the form. Listing 27.28 removes the filter from the action method so that all the data received from the HTML form is stored in temp data.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.28 Removing a filter in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc.Rendering;\n\nnamespace WebApp.Controllers {\n\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            ViewBag.Categories = new SelectList(context.Categories, \n                \"CategoryId\", \"Name\");\n            return View(\"Form\", await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstAsync(p =&gt; p.ProductId == id)\n            ?? new() { Name = string.Empty });\n        }\n                \n        [HttpPost]\n        public IActionResult SubmitForm() {\n            <b class=\"fm-bold\">foreach (string key in Request.Form.Keys) {<\/b>\n                TempData[key] = string.Join(\", \",\n                    (string?)Request.Form[key]);\n            }\n            return RedirectToAction(nameof(Results));\n        }\n                \n        public IActionResult Results() {\n            return View();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/controllers\/form. Click the Submit button to send the form to the application, and you will see a new item in the results, as shown in figure 27.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre282\" src=\"\/images\/proaspnetcore7\/000289.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 27.9 Showing all form data<\/p>\n<\/div>\n<p class=\"body\">The <code class=\"fm-code-in-text\">_RequestVerificationToken<\/code> form value displayed in the results is a security feature that is applied by the <code class=\"fm-code-in-text\">FormTagHelper<\/code> to guard against cross-site request forgery. Cross-site request forgery (CSRF) exploits web applications by taking advantage of the way that user requests are typically authenticated. Most web applications&mdash;including those created using ASP.NET Core&mdash;use cookies to identify which requests are related to a specific session, with which a user identity is usually associated.<a id=\"calibre_link-2456\"><\/a><a id=\"calibre_link-979\"><\/a><\/p>\n<p class=\"body\">CSRF&mdash;also known as <i class=\"fm-italics\">XSRF<\/i>&mdash;relies on the user visiting a malicious website after using your web application and without explicitly ending their session. The application still regards the user\u2019s session as being active, and the cookie that the browser has stored has not yet expired. The malicious site contains JavaScript code that sends a form request to your application to perform an operation without the user\u2019s consent&mdash;the exact nature of the operation will depend on the application being attacked. Since the JavaScript code is executed by the user\u2019s browser, the request to the application includes the session cookie, and the application performs the operation without the user\u2019s knowledge or consent.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> CSRF is described in detail at <a class=\"url\" href=\"http:\/\/en.wikipedia.org\/wiki\/Cross-site_request_forgery\">http:\/\/en.wikipedia.org\/wiki\/Cross-site_request_forgery<\/a>.<\/p>\n<p class=\"body\">If a <code class=\"fm-code-in-text\">form<\/code> element doesn\u2019t contain an <code class=\"fm-code-in-text\">action<\/code> attribute&mdash;because it is being generated from the routing system with the <code class=\"fm-code-in-text\">asp-controller<\/code>, <code class=\"fm-code-in-text\">asp-action<\/code>, and <code class=\"fm-code-in-text\">asp-page<\/code> attributes&mdash;then the <code class=\"fm-code-in-text\">FormTagHelper<\/code> class automatically enables an anti-CSRF feature, whereby a security token is added to the response as a cookie. A hidden <code class=\"fm-code-in-text\">input<\/code> element containing the same security token is added to the HTML form, and it is this token that is shown in figure 27.9.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-519\">27.8.1 Enabling the anti-forgery feature in a controller<\/h3>\n<p class=\"body\">By default, controllers accept POST requests even when they don\u2019t contain the required security tokens. To enable the anti-forgery feature, an attribute is applied to the controller class, as shown in listing 27.29.<a id=\"calibre_link-2457\"><\/a><a id=\"calibre_link-2458\"><\/a><a id=\"calibre_link-976\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.29 Enabling the anti-forgery feature in the Controllers\/FormController.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc.Rendering;\n\nnamespace WebApp.Controllers {\n\n    <b class=\"fm-bold\">[AutoValidateAntiforgeryToken]<\/b>\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            ViewBag.Categories = new SelectList(context.Categories, \n                \"CategoryId\", \"Name\");\n            return View(\"Form\", await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstAsync(p =&gt; p.ProductId == id)\n            ?? new() { Name = string.Empty });\n        }\n                \n        [HttpPost]\n        public IActionResult SubmitForm() {\n            foreach (string key in Request.Form.Keys) {\n                TempData[key] = string.Join(\", \",\n                    (string?)Request.Form[key]);\n            }\n            return RedirectToAction(nameof(Results));\n        }\n                \n        public IActionResult Results() {\n            return View();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Not all requests require an anti-forgery token, and the <code class=\"fm-code-in-text\">AutoValidateAntiforgeryToken<\/code> ensures that checks are performed for all HTTP methods except GET, HEAD, OPTIONS, and TRACE.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Two other attributes can be used to control token validation. The <code class=\"fm-code-in-text1\">IgnoreValidationToken<\/code> attribute suppresses validation for an action method or controller. The <code class=\"fm-code-in-text1\">ValidateAntiForgeryToken<\/code> attribute does the opposite and enforces validation, even for requests that would not normally require validation, such as HTTP GET requests. I recommend using the <code class=\"fm-code-in-text1\">AutoValidateAntiforgeryToken<\/code> attribute, as shown in the listing.<a id=\"calibre_link-2459\"><\/a><a id=\"calibre_link-2460\"><\/a><a id=\"calibre_link-977\"><\/a><\/p>\n<p class=\"body\">Testing the anti-CSRF feature is a little tricky. I do it by requesting the URL that contains the form (http:\/\/localhost:5000\/controllers\/forms for this example) and then using the browser\u2019s F12 developer tools to locate and remove the hidden <code class=\"fm-code-in-text\">input<\/code> element from the form (or change the element\u2019s value). When I populate and submit the form, it is missing one part of the required data, and the request will fail.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-520\">27.8.2 Enabling the anti-forgery feature in a Razor Page<\/h3>\n<p class=\"body\">The anti-forgery feature is enabled by default in Razor Pages, which is why I applied the <code class=\"fm-code-in-text\">IgnoreAntiforgeryToken<\/code> attribute to the page handler method in listing 27.29 when I created the <code class=\"fm-code-in-text\">FormHandler<\/code> page. Listing 27.30 removes the attribute to enable the validation feature.<a id=\"calibre_link-2461\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.30 Enabling Request Validation in the Pages\/FormHandler.cshtml File<\/p>\n<pre class=\"programlisting\">...\n@functions {\n\n    <b class=\"fm-bold\">\/\/[IgnoreAntiforgeryToken]<\/b>\n    public class FormHandlerModel : PageModel {\n        private DataContext context;\n...<\/pre>\n<p class=\"body\">Testing the validation feature is done in the same way as for controllers and requires altering the HTML document using the browser\u2019s developer tools before submitting the form to the application.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-521\">27.8.3 Using anti-forgery tokens with JavaScript clients<\/h3>\n<p class=\"body\">By default, the anti-forgery feature relies on the ASP.NET Core application being able to include an element in an HTML form that the browser sends back when the form is submitted. This doesn\u2019t work for JavaScript clients because the ASP.NET Core application provides data and not HTML, so there is no way to insert the hidden element and receive it in a future request.<a id=\"calibre_link-2462\"><\/a><\/p>\n<p class=\"body\">For web services, the anti-forgery token can be sent as a JavaScript-readable cookie, which the JavaScript client code reads and includes as a header in its POST requests. Some JavaScript frameworks, such as Angular, will automatically detect the cookie and include a header in requests. For other frameworks and custom JavaScript code, additional work is required.<\/p>\n<p class=\"body\">Listing 27.31 shows the changes required to the ASP.NET Core application to configure the anti-forgery feature for use with JavaScript clients.<a id=\"calibre_link-2463\"><\/a><a id=\"calibre_link-978\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.31 Configuring the anti-forgery token in the WebApp\/Program.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Antiforgery;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddSingleton&lt;CitiesData&gt;();\n\n<b class=\"fm-bold\">builder.Services.Configure&lt;AntiforgeryOptions&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.HeaderName = \"X-XSRF-TOKEN\";<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\n<b class=\"fm-bold\">IAntiforgery antiforgery<\/b> \n    <b class=\"fm-bold\">= app.Services.GetRequiredService&lt;IAntiforgery&gt;();<\/b>\n<b class=\"fm-bold\">app.Use(async (context, next) =&gt; {<\/b>\n    <b class=\"fm-bold\">if (!context.Request.Path.StartsWithSegments(\"\/api\")) {<\/b>\n        <b class=\"fm-bold\">string? token =<\/b> \n            <b class=\"fm-bold\">antiforgery.GetAndStoreTokens(context).RequestToken;<\/b>\n        <b class=\"fm-bold\">if (token != null) {<\/b>\n            <b class=\"fm-bold\">context.Response.Cookies.Append(\"XSRF-TOKEN\",<\/b>\n               <b class=\"fm-bold\">token,<\/b>\n               <b class=\"fm-bold\">new CookieOptions { HttpOnly = false });<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">await next();<\/b>\n<b class=\"fm-bold\">});<\/b>\n\napp.MapControllerRoute(\"forms\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The options pattern is used to configure the anti-forgery feature, through the <code class=\"fm-code-in-text\">AntiforgeryOptions<\/code> class. The <code class=\"fm-code-in-text\">HeaderName<\/code> property is used to specify the name of a header through which anti-forgery tokens will be accepted, which is <code class=\"fm-code-in-text\">X-XSRF-TOKEN<\/code> in this case.<\/p>\n<p class=\"body\">A custom middleware component is required to set the cookie, which is named <code class=\"fm-code-in-text\">XSRF-TOKEN<\/code> in this example. The value of the cookie is obtained through the <code class=\"fm-code-in-text\">IAntiForgery<\/code> service and must be configured with the <code class=\"fm-code-in-text\">HttpOnly<\/code> option set to <code class=\"fm-code-in-text\">false<\/code> so that the browser will allow JavaScript code to read the cookie.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> I have followed the names that are supported by Angular in this example. Other frameworks follow their own conventions but can usually be configured to use any set of cookie and header names.<\/p>\n<p class=\"body\">To create a simple JavaScript client that uses the cookie and header, add a Razor Page named <code class=\"fm-code-in-text\">JavaScriptForm.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 27.32.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 27.32 The contents of the JavaScriptForm.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/jsform\"\n\n&lt;script type=\"text\/javascript\"&gt;\n    async function sendRequest() {\n        const token = document.cookie.replace(\n           \/(?:(?:^|.*;\\s*)XSRF-TOKEN\\s*\\=\\s*([^;]*).*$)|^.*$\/, \"$1\");\n                   \n        let form = new FormData();\n        form.append(\"name\", \"Paddle\");\n        form.append(\"price\", 100);\n        form.append(\"categoryId\", 1);\n        form.append(\"supplierId\", 1);\n                \n        let response = await fetch(\"@Url.Page(\"FormHandler\")\", {\n            method: \"POST\",\n            headers: { \"X-XSRF-TOKEN\": token },\n            body: form\n        });\n        document.getElementById(\"content\").innerHTML \n            = await response.text();\n    }\n        \n    document.addEventListener(\"DOMContentLoaded\",\n        () =&gt; document.getElementById(\"submit\").onclick = sendRequest);\n&lt;\/script&gt;\n\n&lt;button class=\"btn btn-primary m-2\" id=\"submit\"&gt;\n    Submit JavaScript Form\n&lt;\/button&gt;\n&lt;div id=\"content\"&gt;&lt;\/div&gt;<\/pre>\n<p class=\"body\">The JavaScript code in this Razor Page responds to a button click by sending an HTTP POST request to the <code class=\"fm-code-in-text\">FormHandler<\/code> Razor Page. The value of the <code class=\"fm-code-in-text\">XSRF-TOKEN<\/code> cookie is read and included in the <code class=\"fm-code-in-text\">X-XSRF-TOKEN<\/code> request header. The response from the <code class=\"fm-code-in-text\">FormHandler<\/code> page is a redirection to the <code class=\"fm-code-in-text\">Results<\/code> page, which the browser will follow automatically. The response from the <code class=\"fm-code-in-text\">Results<\/code> page is read by the JavaScript code and inserted into an element so it can be displayed to the user. To test the JavaScript code, restart ASP.NET Core, use a browser to request http:\/\/localhost:5000\/pages\/jsform, and click the button. The JavaScript code will submit the form and display the response, as shown in figure 27.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre283\" src=\"\/images\/proaspnetcore7\/000290.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 27.10 Using a security token in JavaScript code<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2464\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">FormTagHelper<\/code> class transforms <code class=\"fm-code-in-text\">form<\/code> elements so they target specific action methods or Razor pages.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">InputTagHelper<\/code> class transforms <code class=\"fm-code-in-text\">input<\/code> elements, generating form content from model properties and their values.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">LabelTagHelper<\/code> class transforms <code class=\"fm-code-in-text\">label<\/code> elements, setting their content from model property names.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">TextAreaTagHelper<\/code> transforms <code class=\"fm-code-in-text\">textarea<\/code> elements.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">AutoValidateAntiforgeryToken<\/code> and <code class=\"fm-code-in-text\">IgnoreAntiforgeryToken<\/code> attributes are used to control the anti-cross site request forgery feature.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-522\">\n<div class=\"calibre1\" id=\"calibre_link-2465\">\n<h1 class=\"tochead\" id=\"calibre_link-2466\"><a id=\"calibre_link-2467\"><\/a><a id=\"calibre_link-2468\"><\/a><a id=\"calibre_link-2469\"><\/a><a id=\"calibre_link-2470\"><\/a>28 Using model binding<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Understanding how the ASP.NET Core model binder reads data values from HTTP requests<\/li>\n<li class=\"co-summary-bullet\">Finding data values during model binding<\/li>\n<li class=\"co-summary-bullet\">Customizing the model binding process and performing model binding manually<\/li>\n<\/ul>\n<p class=\"body\"><i class=\"fm-italics\">Model binding<\/i> is the process of creating .NET objects using the values from the HTTP request to provide easy access to the data required by action methods and Razor Pages. In this chapter, I describe the way the model binding system works; show how it binds simple types, complex types, and collections; and demonstrate how you can take control of the process to specify which part of the request provides the data values your application requires. Table 28.1 puts model binding in context.<\/p>\n<p class=\"fm-table-caption\">Table 28.1 Putting model binding in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2471\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What is it?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Model binding is the process of creating the objects that action methods and page handlers require using data values obtained from the HTTP request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why is it useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Model binding lets controllers or page handlers declare method parameters or properties using C# types and automatically receive data from the request without having to inspect, parse, and process the data directly.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How is it used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">In its simplest form, methods declare parameters, or classes define properties whose names are used to retrieve data values from the HTTP request. The part of the request used to obtain the data can be configured by applying attributes to the method parameters or properties.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The main pitfall is getting data from the wrong part of the request. I explain the way that requests are searched for data in the \u201cUnderstanding Model Binding\u201d section, and the search locations can be specified explicitly using the attributes that I describe in the \u201cSpecifying a Model Binding Source\u201d section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Data can be obtained without model binding using context objects. However, the result is more complicated code that is hard to read and maintain.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 28.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 28.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2472\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Binding simple types<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define method parameters with primitive types.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">5&ndash;9<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Binding complex types<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Define method parameters with class types.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">10<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Binding to a property<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">BindProperty<\/code> attribute.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">11, 12<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Binding nested types<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Ensure the form value types follow the dotted notation.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">13&ndash;18<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Selecting properties for binding<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">Bind<\/code> and <code class=\"fm-code-in-text1\">BindNever<\/code> attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">19&ndash;21<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Binding collections<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Follow the sequence binding conventions.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">22&ndash;27<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Specifying the source for binding<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use one of the source attributes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">28&ndash;33<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Manually performing binding<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">TryUpdateModel<\/code> method.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">34<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-523\">28.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the WebApp project from chapter 27. To prepare for this chapter, replace the contents of the <code class=\"fm-code-in-text\">Form.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Form<\/code> folder with the content shown in listing 28.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.1 The contents of the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">Next, comment out the <code class=\"fm-code-in-text\">DisplayFormat<\/code> attribute that has been applied to the <code class=\"fm-code-in-text\">Product<\/code> model class, as shown in listing 28.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.2 Removing an attribute in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Text.Json.Serialization;\n\nnamespace WebApp.Models {\n    public class Product {\n        \n        public long ProductId { get; set; }\n                \n        public required string Name { get; set; }\n                \n        [Column(TypeName = \"decimal(8, 2)\")]\n        <b class=\"fm-bold\">\/\/[DisplayFormat(DataFormatString = \"{0:c2}\",<\/b> \n        <b class=\"fm-bold\">\/\/    ApplyFormatInEditMode = true)]<\/b>\n        public decimal Price { get; set; }\n                \n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        public long SupplierId { get; set; }\n                \n        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-524\">28.1.1 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 28.3 to drop the database.<a id=\"calibre_link-1060\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.3 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-525\">28.1.2 Running the example application<\/h3>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 28.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.4 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/controllers\/form, which will display an HTML form. Click the Submit button, and the form data will be displayed, as shown in figure 28.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre284\" src=\"\/images\/proaspnetcore7\/000291.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-526\">28.2 Understanding model binding<\/h2>\n<p class=\"body\">Model binding is an elegant bridge between the HTTP request and action or page handler methods. Most ASP.NET Core applications rely on model binding to some extent, including the example application for this chapter.<a id=\"calibre_link-2473\"><\/a><\/p>\n<p class=\"body\">You can see model binding at work by using the browser to request http:\/\/localhost:5000\/controllers\/form\/index\/5. This URL contains the value of the <code class=\"fm-code-in-text\">ProductId<\/code> property of the <code class=\"fm-code-in-text\">Product<\/code> object that I want to view, like this:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost:5000\/controllers\/form\/index\/<b class=\"fm-bold\">5<\/b><\/pre>\n<p class=\"body\">This part of the URL corresponds to the <code class=\"fm-code-in-text\">id<\/code> segment variable defined by the controller routing pattern and matches the name of the parameter defined by the <code class=\"fm-code-in-text\">Form<\/code> controller\u2019s <code class=\"fm-code-in-text\">Index<\/code> action:<\/p>\n<pre class=\"programlisting\">...\npublic async Task&lt;IActionResult&gt; Index(long <b class=\"fm-bold\">id<\/b> = 1) {\n...<\/pre>\n<p class=\"body\">A value for the <code class=\"fm-code-in-text\">id<\/code> parameter is required before the MVC Framework can invoke the action method, and finding a suitable value is the responsibility of the <i class=\"fm-italics\">model binding<\/i> system. The model binding system relies on <i class=\"fm-italics\">model binders<\/i>, which are components responsible for providing data values from one part of the request or application. The default model binders look for data values in these four places:<a id=\"calibre_link-2474\"><\/a><a id=\"calibre_link-1058\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Form data<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The request body (only for controllers decorated with <code class=\"fm-code-in-text\">ApiController<\/code>)<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Routing segment variables<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Query strings<\/p>\n<\/li>\n<\/ul>\n<p class=\"body\">Each source of data is inspected in order until a value for the argument is found. There is no form data in the example application, so no value will be found there, and the <code class=\"fm-code-in-text\">Form<\/code> controller isn\u2019t decorated with the <code class=\"fm-code-in-text\">ApiController<\/code> attribute, so the request body won\u2019t be checked. The next step is to check the routing data, which contains a segment variable named <code class=\"fm-code-in-text\">id<\/code>. This allows the model binding system to provide a value that allows the <code class=\"fm-code-in-text\">Index<\/code> action method to be invoked. The search stops after a suitable data value has been found, which means that the query string isn\u2019t searched for a data value.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> In the \u201cSpecifying a Model Binding Source\u201d section, I explain how you can specify the source of model binding data using attributes. This allows you to specify that a data value is obtained from, for example, the query string, even if there is also suitable data in the routing data.<\/p>\n<p class=\"body\">Knowing the order in which data values are sought is important because a request can contain multiple values, like this URL:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost:5000\/controllers\/Form\/Index\/<b class=\"fm-bold\">5<\/b>?id=<b class=\"fm-bold\">1<\/b><\/pre>\n<p class=\"body\">The routing system will process the request and match the <code class=\"fm-code-in-text\">id<\/code> segment in the URL template to the value <code class=\"fm-code-in-text\">5<\/code>, and the query string contains an <code class=\"fm-code-in-text\">id<\/code> value of <code class=\"fm-code-in-text\">1<\/code>. Since the routing data is searched for data before the query string, the <code class=\"fm-code-in-text\">Index<\/code> action method will receive the value <code class=\"fm-code-in-text\">5<\/code>, and the query string value will be ignored.<\/p>\n<p class=\"body\">On the other hand, if you request a URL that doesn\u2019t have an <code class=\"fm-code-in-text\">id<\/code> segment, then the query string will be examined, which means that a URL like this one will also allow the model binding system to provide a value for the <code class=\"fm-code-in-text\">id<\/code> argument so that it can invoke the <code class=\"fm-code-in-text\">Index<\/code> method.<\/p>\n<pre class=\"programlisting\">http:\/\/localhost:5000\/controllers\/Form\/Index?id=<b class=\"fm-bold\">4<\/b><\/pre>\n<p class=\"body\">You can see the effect of both these URLs in figure 28.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre285\" src=\"\/images\/proaspnetcore7\/000292.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.2 The effect of model binding data source order<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-527\">28.3 Binding simple data types<\/h2>\n<p class=\"body\">Request data values must be converted into C# values so they can be used to invoke action or page handler methods. <i class=\"fm-italics\">Simple types<\/i> are values that originate from one item of data in the request that can be parsed from a string. This includes numeric values, <code class=\"fm-code-in-text\">bool<\/code> values, dates, and, of course, <code class=\"fm-code-in-text\">string<\/code> values.<a id=\"calibre_link-2475\"><\/a><a id=\"calibre_link-1059\"><\/a><\/p>\n<p class=\"body\">Data binding for simple types makes it easy to extract single data items from the request without having to work through the context data to find out where it is defined. Listing 28.5 adds parameters to the <code class=\"fm-code-in-text\">SubmitForm<\/code> action method defined by the <code class=\"fm-code-in-text\">SubmitForm<\/code> action method so that the model binder will be used to provide <code class=\"fm-code-in-text\">name<\/code> and <code class=\"fm-code-in-text\">price<\/code> values.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.5 Adding parameters in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc.Rendering;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long id = 1) {\n            ViewBag.Categories = new SelectList(context.Categories, \n                \"CategoryId\", \"Name\");\n            return View(\"Form\", await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstAsync(p =&gt; p.ProductId == id)\n            ?? new() { Name = string.Empty });\n        }\n                \n        [HttpPost]\n        <b class=\"fm-bold\">public IActionResult SubmitForm(string name, decimal price) {<\/b>\n            <b class=\"fm-bold\">TempData[\"name param\"] = name;<\/b>\n            <b class=\"fm-bold\">TempData[\"price param\"] = price.ToString();<\/b>\n            return RedirectToAction(nameof(Results));\n        }\n                \n        public IActionResult Results() {\n            return View();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The model binding system will be used to obtain <code class=\"fm-code-in-text\">name<\/code> and <code class=\"fm-code-in-text\">price<\/code> values when ASP.NET Core receives a request that will be processed by the <code class=\"fm-code-in-text\">SubmitForm<\/code> action method. The use of parameters simplifies the action method and takes care of converting the request data into C# data types so that the <code class=\"fm-code-in-text\">price<\/code> value will be converted to the C# <code class=\"fm-code-in-text\">decimal<\/code> type before the action method is invoked. (I had to convert the <code class=\"fm-code-in-text\">decimal<\/code> back to a string to store it as temp data in this example. I demonstrate more useful ways of dealing with form data in chapter 31.) Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/controllers\/form. Click the Submit button, and you will see the values that were extracted from the request by the model binding feature, as shown in figure 28.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre286\" src=\"\/images\/proaspnetcore7\/000293.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.3 Model binding for simple types<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-528\">28.3.1 Binding simple data types in Razor Pages<\/h3>\n<p class=\"body\">Razor Pages can use model binding, but care must be taken to ensure that the value of the form element\u2019s <code class=\"fm-code-in-text\">name<\/code> attribute matches the name of the handler method parameter, which may not be the case if the <code class=\"fm-code-in-text\">asp-for<\/code> attribute has been used to select a nested property. To ensure the names match, the <code class=\"fm-code-in-text\">name<\/code> attribute can be defined explicitly, as shown in listing 28.6, which also simplifies the HTML form so that it matches the controller example.<a id=\"calibre_link-2476\"><\/a><a id=\"calibre_link-1057\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.6 Using model binding in the FormHandler.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/form\/{id:long?}\"\n@model FormHandlerModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using Microsoft.EntityFrameworkCore\n\n&lt;div class=\"m-2\"&gt;\n    &lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n    &lt;form asp-page=\"FormHandler\" method=\"post\" id=\"htmlform\"&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Name&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Product.Name\"<\/b> \n                <b class=\"fm-bold\">name=\"name\" \/&gt;<\/b>\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Price&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Product.Price\"<\/b> \n                <b class=\"fm-bold\">name=\"price\" \/&gt;<\/b>\n        &lt;\/div&gt;\n        &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n    &lt;\/form&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class FormHandlerModel : PageModel {\n        private DataContext context;\n                \n        public FormHandlerModel(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public Product Product { get; set; } \n            = new() { Name = string.Empty };\n\n        public async Task OnGetAsync(long id = 1) {\n            Product = await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstAsync(p =&gt; p.ProductId == id);\n        }\n                \n        <b class=\"fm-bold\">public IActionResult OnPost(string name, decimal price) {<\/b>\n            <b class=\"fm-bold\">TempData[\"name param\"] = name;<\/b>\n            <b class=\"fm-bold\">TempData[\"price param\"] = price.ToString();<\/b>\n            return RedirectToPage(\"FormResults\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The tag helper would have set the name attributes of the input elements to <code class=\"fm-code-in-text\">Product.Name<\/code> and <code class=\"fm-code-in-text\">Product.Price<\/code>, which prevents the model binder from matching the values. Explicitly setting the <code class=\"fm-code-in-text\">name<\/code> attribute overrides the tag helper and ensures the model binding process works correctly. Restart ASP.NET Core, use a browser to request http:\/\/localhost:5000\/pages\/form, and click the Submit button, and you will see the values found by the model binder, as shown in figure 28.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre287\" src=\"\/images\/proaspnetcore7\/000294.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.4 Model binding in a Razor Page<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-529\">28.3.2 Understanding default binding values<\/h3>\n<p class=\"body\">Model binding is a best-effort feature, which means the model binder will try to get values for method parameters but will still invoke the method if data values cannot be located. You can see how this works by removing the default value for the <code class=\"fm-code-in-text\">id<\/code> parameter in the <code class=\"fm-code-in-text\">Form<\/code> controller\u2019s <code class=\"fm-code-in-text\">Index<\/code> action method, as shown in listing 28.7.<a id=\"calibre_link-2477\"><\/a><a id=\"calibre_link-2478\"><\/a><a id=\"calibre_link-1048\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.7 Removing a parameter value in the Controllers\/FormController.cs file<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">public async Task&lt;IActionResult&gt; Index(long id) {<\/b>\n    ViewBag.Categories = new SelectList(context.Categories, \n        \"CategoryId\", \"Name\");\n    return View(\"Form\", await context.Products\n        .Include(p =&gt; p.Category)\n        .Include(p =&gt; p.Supplier)\n        .FirstAsync(p =&gt; p.ProductId == id)\n    ?? new() { Name = string.Empty });\n}\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/controllers\/form. The URL doesn\u2019t contain a value that the model binder can use for the <code class=\"fm-code-in-text\">id<\/code> parameter, and there is no query string or form data, but the method is still invoked, producing the error shown in figure 28.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre288\" src=\"\/images\/proaspnetcore7\/000295.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.5 An error caused by a missing data value<\/p>\n<\/div>\n<p class=\"body\">This exception isn\u2019t reported by the model binding system. Instead, it occurred when the Entity Framework Core query was executed. The MVC Framework must provide <i class=\"fm-italics\">some<\/i> value for the <code class=\"fm-code-in-text\">id<\/code> argument to invoke the <code class=\"fm-code-in-text\">Index<\/code> action method, so it uses a default value and hopes for the best. For <code class=\"fm-code-in-text\">long<\/code> arguments, the default value is <code class=\"fm-code-in-text\">0<\/code>, and this is what leads to the exception. The <code class=\"fm-code-in-text\">Index<\/code> action method uses the <code class=\"fm-code-in-text\">id<\/code> value as the key to query the database for a <code class=\"fm-code-in-text\">Product<\/code> object, like this:<\/p>\n<pre class=\"programlisting\">...\npublic async Task&lt;IActionResult&gt; Index(long id) {\n    ViewBag.Categories = new SelectList(context.Categories, \n        \"CategoryId\", \"Name\");\n    return View(\"Form\", await context.Products\n        .Include(p =&gt; p.Category)\n        .Include(p =&gt; p.Supplier)\n        <b class=\"fm-bold\">.FirstAsync(p =&gt; p.ProductId == id)<\/b>\n    ?? new() { Name = string.Empty });\n}\n...<\/pre>\n<p class=\"body\">When there is no value available for model binding, the action method tries to query the database with an <code class=\"fm-code-in-text\">id<\/code> of zero. There is no such object, which causes the error shown in the figure when Entity Framework Core tries to process the result.<\/p>\n<p class=\"body\">Applications must be written to cope with default argument values, which can be done in several ways. You can add fallback values to the routing URL patterns used by controllers (as shown in chapter 21) or pages (as shown in chapter 23). You can assign default values when defining the parameter in the action or page handler method, which is the approach that I have taken so far in this part of the book. Or you can simply write methods that accommodate the default values without causing an error, as shown in listing 28.8.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.8 Avoiding a query error in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">...\npublic async Task&lt;IActionResult&gt; Index(long id) {\n    ViewBag.Categories = new SelectList(context.Categories, \n        \"CategoryId\", \"Name\");\n    return View(\"Form\", await context.Products\n        .Include(p =&gt; p.Category)\n        .Include(p =&gt; p.Supplier)\n        <b class=\"fm-bold\">.FirstOrDefaultAsync(p =&gt; p.ProductId == id)<\/b>\n    ?? new() { Name = string.Empty });\n}\n...<\/pre>\n<p class=\"body\">The Entity Framework Core <code class=\"fm-code-in-text\">FirstOrDefaultAsync<\/code> method will return <code class=\"fm-code-in-text\">null<\/code> if there is no matching object in the database and won\u2019t attempt to load related data. The tag helpers cope with <code class=\"fm-code-in-text\">null<\/code> values and display empty fields, which you can see by restarting ASP.NET Core and requesting http:\/\/localhost:5000\/controllers\/form, which produces the result shown in figure 28.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre289\" src=\"\/images\/proaspnetcore7\/000296.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.6 Avoiding an error<\/p>\n<\/div>\n<p class=\"body\">Some applications need to differentiate between a missing value and any value provided by the user. In these situations, a nullable parameter type can be used, as shown in listing 28.9.<a id=\"calibre_link-2479\"><\/a><a id=\"calibre_link-1049\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.9. A nullable parameter in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">public async Task&lt;IActionResult&gt; Index(long? id) {<\/b>\n    ViewBag.Categories = new SelectList(context.Categories, \n        \"CategoryId\", \"Name\");\n    return View(\"Form\", await context.Products.Include(p =&gt; p.Category)\n        .Include(p =&gt; p.Supplier)\n        <b class=\"fm-bold\">.FirstOrDefaultAsync(p =&gt; id == null || p.ProductId == id));<\/b>\n}\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">id<\/code> parameter will be <code class=\"fm-code-in-text\">null<\/code> only if the request doesn\u2019t contain a suitable value, which allows the expression passed to the <code class=\"fm-code-in-text\">FirstOrDefaultAsync<\/code> method to default to the first object in the database when there is no value and to query for any other value. To see the effect, restart ASP.NET Core and request http:\/\/localhost:5000\/controllers\/form and http:\/\/localhost:5000\/controllers\/form\/index\/0. The first URL contains no <code class=\"fm-code-in-text\">id<\/code> value, so the first object in the database is selected. The second URL provides an <code class=\"fm-code-in-text\">id<\/code> value of zero, which doesn\u2019t correspond to any object in the database. Figure 28.7 shows both results.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre290\" src=\"\/images\/proaspnetcore7\/000297.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.7 Using a nullable type to determine whether a request contains a value<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-530\">28.4 Binding complex types<\/h2>\n<p class=\"body\">The model binding system shines when dealing with complex types, which are any type that cannot be parsed from a single string value. The model binding process inspects the complex type and performs the binding process on each of the <code class=\"fm-code-in-text\">public<\/code> properties it defines. This means that instead of dealing with individual values such as <code class=\"fm-code-in-text\">name<\/code> and <code class=\"fm-code-in-text\">price<\/code>, I can use the binder to create complete <code class=\"fm-code-in-text\">Product<\/code> objects, as shown in listing 28.10.<a id=\"calibre_link-2480\"><\/a><a id=\"calibre_link-1044\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.10 &nbsp;Binding a complex type in the Controllers\/FormController.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc.Rendering;\n<b class=\"fm-bold\">using System.Text.Json;<\/b>\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long? id) {\n            ViewBag.Categories = new SelectList(context.Categories, \n                \"CategoryId\", \"Name\");\n            return View(\"Form\", await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstOrDefaultAsync(p =&gt; id == null || p.ProductId == id)\n            ?? new() { Name = string.Empty });\n        }\n                \n        [HttpPost]\n        public IActionResult SubmitForm(Product product) {\n            <b class=\"fm-bold\">TempData[\"product\"] = JsonSerializer.Serialize(product);<\/b>\n            return RedirectToAction(nameof(Results));\n        }\n                \n        public IActionResult Results() {\n            return View();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The listing changes the <code class=\"fm-code-in-text\">SubmitForm<\/code> action method so that it defines a <code class=\"fm-code-in-text\">Product<\/code> parameter. Before the action method is invoked, a new <code class=\"fm-code-in-text\">Product<\/code> object is created, and the model binding process is applied to each of its <code class=\"fm-code-in-text\">public<\/code> properties. The <code class=\"fm-code-in-text\">SubmitForm<\/code> method is then invoked, using the <code class=\"fm-code-in-text\">Product<\/code> object as its argument.<\/p>\n<p class=\"body\">To see the model binding process, restart ASP.NET Core, navigate to http:\/\/localhost:5000\/controllers\/form, and click the Submit button. The model binding process will extract the data values from the request and produce the result shown in figure 28.8. The <code class=\"fm-code-in-text\">Product<\/code> object created by the model binding process is serialized as JSON data so that it can be stored as temp data, making it easy to see the request data.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre291\" src=\"\/images\/proaspnetcore7\/000298.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.8 Data binding a complex type<\/p>\n<\/div>\n<p class=\"body\">The data binding process for complex types remains a best-effort feature, meaning that a value will be sought for each public property defined by the <code class=\"fm-code-in-text\">Product<\/code> class, but missing values won\u2019t prevent the action method from being invoked. Instead, properties for which no value can be located will be left as the default value for the property type. The example provided values for the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Price<\/code> properties, but the <code class=\"fm-code-in-text\">ProductId<\/code>, <code class=\"fm-code-in-text\">CategoryId<\/code>, and <code class=\"fm-code-in-text\">SupplierId<\/code> properties are zero, and the <code class=\"fm-code-in-text\">Category<\/code> and <code class=\"fm-code-in-text\">Supplier<\/code> properties are null.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-531\">28.4.1 Binding to a property<\/h3>\n<p class=\"body\">Using parameters for model binding doesn\u2019t fit with the Razor Pages development style because the parameters often duplicate properties defined by the page model class, as shown in listing 28.11.<a id=\"calibre_link-2481\"><\/a><a id=\"calibre_link-2482\"><\/a><a id=\"calibre_link-1042\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.11 Binding a complex type in the FormHandler.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">...\n@functions {\n\n    public class FormHandlerModel : PageModel {\n        private DataContext context;\n                \n        public FormHandlerModel(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public Product Product { get; set; } \n            = new() { Name = string.Empty };\n                        \n        public async Task OnGetAsync(long id = 1) {\n            Product = await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstAsync(p =&gt; p.ProductId == id);\n        }\n                \n        public IActionResult OnPost(Product product) {\n            <b class=\"fm-bold\">TempData[\"product\"] =<\/b> \n                <b class=\"fm-bold\">System.Text.Json.JsonSerializer.Serialize(product);<\/b>\n            return RedirectToPage(\"FormResults\");\n        }\n    }\n}\n...<\/pre>\n<p class=\"body\">This code works, but the <code class=\"fm-code-in-text\">OnPost<\/code> handler method has its own version of the <code class=\"fm-code-in-text\">Product<\/code> object, mirroring the property used by the <code class=\"fm-code-in-text\">OnGetAsync<\/code> handler. A more elegant approach is to use the existing property for model binding, as shown in listing 28.12.<a id=\"calibre_link-2483\"><\/a><a id=\"calibre_link-1041\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.12 Using a property for model binding in the Pages\/FormHandler.cshtml file<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/form\/{id:long?}\"\n@model FormHandlerModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using Microsoft.EntityFrameworkCore\n\n&lt;div class=\"m-2\"&gt;\n    &lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n    &lt;form asp-page=\"FormHandler\" method=\"post\" id=\"htmlform\"&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Name&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Product.Name\" \/&gt;<\/b>\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Price&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Product.Price\" \/&gt;<\/b>\n        &lt;\/div&gt;\n        &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n    &lt;\/form&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class FormHandlerModel : PageModel {\n        private DataContext context;\n                \n        public FormHandlerModel(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        <b class=\"fm-bold\">[BindProperty]<\/b>\n        public Product Product { get; set; } \n            = new() { Name = string.Empty };\n                        \n        public async Task OnGetAsync(long id = 1) {\n            Product = await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstAsync(p =&gt; p.ProductId == id);\n        }\n                \n        public IActionResult OnPost() {\n            <b class=\"fm-bold\">TempData[\"product\"] =<\/b> \n                <b class=\"fm-bold\">System.Text.Json.JsonSerializer.Serialize(Product);<\/b>\n            return RedirectToPage(\"FormResults\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">Decorating a property with the <code class=\"fm-code-in-text\">BindProperty<\/code> attribute indicates that its properties should be subject to the model binding process, which means the <code class=\"fm-code-in-text\">OnPost<\/code> handler method can get the data it requires without declaring a parameter. When the <code class=\"fm-code-in-text\">BindProperty<\/code> attribute is used, the model binder uses the property name when locating data values, so the explicit <code class=\"fm-code-in-text\">name<\/code> attributes added to the <code class=\"fm-code-in-text\">input<\/code> element are not required. By default, <code class=\"fm-code-in-text\">BindProperty<\/code> won\u2019t bind data for GET requests, but this can be changed by setting the <code class=\"fm-code-in-text\">BindProperty<\/code> attribute\u2019s <code class=\"fm-code-in-text\">SupportsGet<\/code> argument to <code class=\"fm-code-in-text\">true<\/code>.<a id=\"calibre_link-2484\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The <code class=\"fm-code-in-text1\">BindProperties<\/code> attribute can be applied to classes that require the model binding process for all the <code class=\"fm-code-in-text1\">public<\/code> properties they define, which can be more convenient than applying <code class=\"fm-code-in-text1\">BindProperty<\/code> to many individual properties. Decorate properties with the <code class=\"fm-code-in-text1\">BindNever<\/code> attribute to exclude them from model binding.<a id=\"calibre_link-2485\"><\/a><a id=\"calibre_link-2486\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-532\">28.4.2 Binding nested complex types<\/h3>\n<p class=\"body\">If a property that is subject to model binding is defined using a complex type, then the model binding process is repeated using the property name as a prefix. For example, the <code class=\"fm-code-in-text\">Product<\/code> class defines the <code class=\"fm-code-in-text\">Category<\/code> property, whose type is the complex <code class=\"fm-code-in-text\">Category<\/code> type. Listing 28.13 adds elements to the HTML form to provide the model binder with values for the properties defined by the <code class=\"fm-code-in-text\">Category<\/code> class.<a id=\"calibre_link-2487\"><\/a><a id=\"calibre_link-2488\"><\/a><a id=\"calibre_link-1045\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.13 Nested form elements in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;label&gt;Category Name&lt;\/label&gt;<\/b>\n        <b class=\"fm-bold\">@{ #pragma warning disable CS8602 }<\/b> \n        <b class=\"fm-bold\">&lt;input class=\"form-control\" name=\"Category.Name\"<\/b> \n             <b class=\"fm-bold\">value=\"@Model.Category.Name\" \/&gt;<\/b>\n        <b class=\"fm-bold\">@{ #pragma warning restore CS8602 }<\/b> \n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">name<\/code> attribute combines the property names, separated by periods. In this case, the element is for the <code class=\"fm-code-in-text\">Name<\/code> property of the object assigned to the view model\u2019s <code class=\"fm-code-in-text\">Category<\/code> property, so the <code class=\"fm-code-in-text\">name<\/code> attribute is set to <code class=\"fm-code-in-text\">Category.Name<\/code>. The input element tag helper will automatically use this format for the <code class=\"fm-code-in-text\">name<\/code> attribute when the <code class=\"fm-code-in-text\">asp-for<\/code> attribute is applied, as shown in listing 28.14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.14 Using a tag helper in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Category Name&lt;\/label&gt;\n        @{ #pragma warning disable CS8602 } \n        <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Category.Name\" \/&gt;<\/b>\n        @{ #pragma warning restore CS8602 } \n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">The tag helper is a more reliable method of creating elements for nested properties and avoids the risk of typos producing elements that are ignored by the model binding process. To see the effect of the new elements, request http:\/\/localhost:5000\/controllers\/form and click the Submit button, which will produce the response shown in figure 28.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre292\" src=\"\/images\/proaspnetcore7\/000299.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.9 Model binding a nested property<\/p>\n<\/div>\n<p class=\"body\">During the model binding process, a new <code class=\"fm-code-in-text\">Category<\/code> object is created and assigned to the <code class=\"fm-code-in-text\">Category<\/code> property of the <code class=\"fm-code-in-text\">Product<\/code> object. The model binder locates the value for the <code class=\"fm-code-in-text\">Category<\/code> object\u2019s <code class=\"fm-code-in-text\">Name<\/code> property, which can be seen in the figure, but there is no value for the <code class=\"fm-code-in-text\">CategoryId<\/code> property, which is left as the default value.<\/p>\n<p class=\"fm-head2\">Specifying Custom Prefixes for Nested Complex Types<\/p>\n<p class=\"body\">There are occasions when the HTML you generate relates to one type of object but you want to bind it to another. This means that the prefixes containing the view won\u2019t correspond to the structure that the model binder is expecting, and your data won\u2019t be properly processed. Listing 28.15 demonstrates this problem by changing the type of the parameter defined by the controller\u2019s <code class=\"fm-code-in-text\">SubmitForm<\/code> action method.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.15 Changing a parameter in the Controllers\/FormController.cs file<\/p>\n<pre class=\"programlisting\">...\n[HttpPost]\n<b class=\"fm-bold\">public IActionResult SubmitForm(Category category) {<\/b>\n    <b class=\"fm-bold\">TempData[\"category\"] = JsonSerializer.Serialize(category);<\/b>\n    return RedirectToAction(nameof(Results));\n}\n...<\/pre>\n<p class=\"body\">The new parameter is a <code class=\"fm-code-in-text\">Category<\/code>, which means that the model binder will look for form data using the category prefix. This process fails if the form data doesn\u2019t conform to that naming convention. To demonstrate, listing 28.16 overrides the name attribute input element that provides the category name.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.16 Setting the name in the Form.cshtml file in the Views\/Forms folder<\/p>\n<pre class=\"programlisting\">...\n@{ #pragma warning disable CS8602 } \n&lt;input class=\"form-control\" asp-for=\"Category.Name\" <b class=\"fm-bold\">name=\"cat.name\"<\/b> \/&gt;\n@{ #pragma warning restore CS8602 }\n...<\/pre>\n<p class=\"body\">This change creates a mismatch between the structure of the form data that is received by the application and the property names used by the model binder. The model binding process isn\u2019t able to identify the modified <code class=\"fm-code-in-text\">input<\/code> element. Instead, the model binder will find the <code class=\"fm-code-in-text\">Name<\/code> value for the <code class=\"fm-code-in-text\">Product<\/code> object and use that instead, which you can see by restarting ASP.NET Core, requesting http:\/\/localhost:5000\/controllers\/form, and submitting the form data, which will produce the first response shown in figure 28.10.<\/p>\n<p class=\"body\">This problem is solved by applying the <code class=\"fm-code-in-text\">Bind<\/code> attribute to the parameter and using the <code class=\"fm-code-in-text\">Prefix<\/code> argument to specify a prefix for the model binder, as shown in listing 28.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.17 &nbsp;Setting a prefix in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">...\n[HttpPost]\n<b class=\"fm-bold\">public IActionResult SubmitForm([Bind(Prefix =\"cat\")]Category category) {<\/b>\n    TempData[\"category\"] = JsonSerializer.Serialize(category);\n    return RedirectToAction(nameof(Results));\n}\n...<\/pre>\n<p class=\"body\">The syntax is awkward, but the attribute ensures the model binder can locate the data the action method requires. In this case, setting the prefix to <code class=\"fm-code-in-text\">cat<\/code> ensures the correct data values are used to bind the <code class=\"fm-code-in-text\">Category<\/code> parameter. Restart ASP.NET Core, request http:\/\/localhost:5000\/controllers\/form, and submit the form, which produces the second response shown in figure 28.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre293\" src=\"\/images\/proaspnetcore7\/000300.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.10 Specifying a model binding prefix<\/p>\n<\/div>\n<p class=\"body\">When using the <code class=\"fm-code-in-text\">BindProperty<\/code> attribute, the prefix is specified using the <code class=\"fm-code-in-text\">Name<\/code> argument, as shown in listing 28.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.18 Specifying a model binding prefix in the Pages\/FormHandler.cshtml file<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/form\/{id:long?}\"\n@model FormHandlerModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using Microsoft.EntityFrameworkCore\n\n&lt;div class=\"m-2\"&gt;\n    &lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n        \n    &lt;form asp-page=\"FormHandler\" method=\"post\" id=\"htmlform\"&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Name&lt;\/label&gt;\n            &lt;input class=\"form-control\" asp-for=\"Product.Name\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Price&lt;\/label&gt;\n            &lt;input class=\"form-control\" asp-for=\"Product.Price\" \/&gt;\n        &lt;\/div&gt;\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;Category Name&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">@{ #pragma warning disable CS8602 }<\/b> \n            <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Product.Category.Name\"  \/&gt;<\/b>\n            <b class=\"fm-bold\">@{ #pragma warning restore CS8602 }<\/b> \n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n    &lt;\/form&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class FormHandlerModel : PageModel {\n        private DataContext context;\n                \n        public FormHandlerModel(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        [BindProperty]\n        public Product Product { get; set; } \n            = new() { Name = string.Empty };\n                        \n        <b class=\"fm-bold\">[BindProperty(Name = \"Product.Category\")]<\/b>\n        <b class=\"fm-bold\">public Category Category { get; set; }<\/b> \n            <b class=\"fm-bold\">= new() { Name = string.Empty };<\/b>\n                        \n        public async Task OnGetAsync(long id = 1) {\n            Product = await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstAsync(p =&gt; p.ProductId == id);\n        }\n                \n        public IActionResult OnPost() {\n            TempData[\"product\"] = \n                System.Text.Json.JsonSerializer.Serialize(Product);\n            <b class=\"fm-bold\">TempData[\"category\"] =<\/b> \n                <b class=\"fm-bold\">System.Text.Json.JsonSerializer.Serialize(Category);<\/b>\n            return RedirectToPage(\"FormResults\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">This listing adds an <code class=\"fm-code-in-text\">input<\/code> element that uses the <code class=\"fm-code-in-text\">asp-for<\/code> attribute to select the <code class=\"fm-code-in-text\">Product.Category<\/code> property. A page handler class defined a <code class=\"fm-code-in-text\">Category<\/code> property that is decorated with the <code class=\"fm-code-in-text\">BindProperty<\/code> attribute and configured with the <code class=\"fm-code-in-text\">Name<\/code> argument. To see the result of the model binding process, restart ASP.NET Core, use a browser to request http:\/\/localhost:5000\/pages\/form, and click the Submit button. The model binding finds values for both the decorated properties, which produces the response shown in figure 28.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre294\" src=\"\/images\/proaspnetcore7\/000301.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.11 Specifying a model binding prefix in a Razor Page<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-533\">28.4.3 Selectively binding properties<\/h3>\n<p class=\"body\">Some model classes define properties that are sensitive and for which the user should not be able to specify values. A user may be able to change the category for a <code class=\"fm-code-in-text\">Product<\/code> object, for example, but should not be able to alter the price.<\/p>\n<p class=\"body\">You might be tempted to simply create views that omit HTML elements for sensitive properties but that won\u2019t prevent malicious users from crafting HTTP requests that contain values anyway, which is known as an <i class=\"fm-italics\">over-binding attack<\/i>. To prevent the model binder from using values for sensitive properties, the list of properties that should be bound can be specified, as shown in listing 28.19.<a id=\"calibre_link-2489\"><\/a><a id=\"calibre_link-1046\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.19 Selectively binding properties in the Controllers\/FormController.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing System.Text.Json;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        public async Task&lt;IActionResult&gt; Index(long? id) {\n            ViewBag.Categories = new SelectList(context.Categories, \n                \"CategoryId\", \"Name\");\n            return View(\"Form\", await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstOrDefaultAsync(p =&gt; id == null || p.ProductId == id)\n            ?? new() { Name = string.Empty });\n        }\n                \n        [HttpPost]\n        <b class=\"fm-bold\">public IActionResult SubmitForm([Bind(\"Name\", \"Category\")]<\/b> \n                <b class=\"fm-bold\">Product product) {<\/b>\n            <b class=\"fm-bold\">TempData[\"name\"] = product.Name;<\/b>\n            <b class=\"fm-bold\">TempData[\"price\"] = product.Price.ToString();<\/b>\n            <b class=\"fm-bold\">TempData[\"category name\"] = product.Category?.Name;<\/b>\n            return RedirectToAction(nameof(Results));\n        }\n                \n        public IActionResult Results() {\n            return View();\n        }\n    }\n}<\/pre>\n<p class=\"body\">I have returned to the <code class=\"fm-code-in-text\">Product<\/code> type for the action method parameter, which has been decorated with the <code class=\"fm-code-in-text\">Bind<\/code> attribute to specify the names of the properties that should be included in the model binding process. This example tells the model binding feature to look for values for the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Category<\/code> properties, which excludes any other property from the process.<\/p>\n<p class=\"body\">Listing 28.20 removes the custom <code class=\"fm-code-in-text\">name<\/code> attribute I added earlier so that the model binder can find the values it needs using the standard naming conventions.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.20 Removing an attribute in the Form.cshtml file in the Views\/Forms folder<\/p>\n<pre class=\"programlisting\">...\n@{ #pragma warning disable CS8602 } \n<b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Category.Name\" \/&gt;<\/b>\n@{ #pragma warning restore CS8602 }\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core, navigate to http:\/\/localhost:5000\/controllers\/form, and submit the form. Even though the browser sends a value for the <code class=\"fm-code-in-text\">Price<\/code> property as part of the HTTP POST request, it is ignored by the model binder, as shown in figure 28.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre295\" src=\"\/images\/proaspnetcore7\/000302.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.12 Selectively binding properties<\/p>\n<\/div>\n<p class=\"fm-head2\">Selectively binding in the model class<\/p>\n<p class=\"body\">If you are using Razor Pages or you want to use the same set of properties for model binding throughout the application, you can apply the <code class=\"fm-code-in-text\">BindNever<\/code> attribute directly to the model class, as shown in listing 28.21.<a id=\"calibre_link-2490\"><\/a><a id=\"calibre_link-1047\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.21 Decorating a property in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Text.Json.Serialization;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc.ModelBinding;<\/b>\n\nnamespace WebApp.Models {\n    public class Product {\n        \n        public long ProductId { get; set; }\n                \n        public required string Name { get; set; }\n                \n        [Column(TypeName = \"decimal(8, 2)\")]\n        <b class=\"fm-bold\">[BindNever]<\/b>        \n        public decimal Price { get; set; }\n                \n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        public long SupplierId { get; set; }\n                \n        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">BindNever<\/code> attribute excludes a property from the model binder, which has the same effect as omitting it from the list used in the previous section. To see the effect, restart ASP.NET Core so the change to the <code class=\"fm-code-in-text\">Product<\/code> class takes effect, request http:\/\/localhost:5000\/pages\/form, and submit the form. Just as with the previous example, the model binder ignores the value for the <code class=\"fm-code-in-text\">Price<\/code> property, as shown in figure 28.13.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> There is also a <code class=\"fm-code-in-text1\">BindRequired<\/code> attribute that tells the model binding process that a request must include a value for a property. If the request doesn\u2019t have a required value, then a model validation error is produced, as described in chapter 29.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre296\" src=\"\/images\/proaspnetcore7\/000303.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.13 Excluding a property from model binding<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-534\">28.5 Binding to arrays and collections<\/h2>\n<p class=\"body\">The model binding process has some nice features for binding request data to arrays and collections, which I demonstrate in the following sections.<a id=\"calibre_link-2491\"><\/a><a id=\"calibre_link-2492\"><\/a><a id=\"calibre_link-2493\"><\/a><a id=\"calibre_link-2494\"><\/a><a id=\"calibre_link-740\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-535\">28.5.1 Binding to arrays<\/h3>\n<p class=\"body\">One elegant feature of the default model binder is how it supports arrays. To see how this feature works, add a Razor Page named <code class=\"fm-code-in-text\">Bindings.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 28.22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.22 The contents of the Bindings.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/bindings\"\n@model BindingsModel\n@using Microsoft.AspNetCore.Mvc\n@using Microsoft.AspNetCore.Mvc.RazorPages\n\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row\"&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;form asp-page=\"Bindings\" method=\"post\"&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label&gt;Value #1&lt;\/label&gt;\n                    &lt;input class=\"form-control\" name=\"Data\" \n                        value=\"Item 1\" \/&gt;\n                &lt;\/div&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label&gt;Value #2&lt;\/label&gt;\n                    &lt;input class=\"form-control\" name=\"Data\" \n                        value=\"Item 2\" \/&gt;\n                &lt;\/div&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label&gt;Value #3&lt;\/label&gt;\n                    &lt;input class=\"form-control\" name=\"Data\" \n                        value=\"Item 3\" \/&gt;\n                &lt;\/div&gt;\n                &lt;button type=\"submit\" class=\"btn btn-primary\"&gt;\n                    Submit\n                &lt;\/button&gt;\n                &lt;a class=\"btn btn-secondary\" asp-page=\"Bindings\"&gt;Reset&lt;\/a&gt;\n            &lt;\/form&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;ul class=\"list-group\"&gt;\n                @foreach (string s in Model.Data.Where(s =&gt; s != null)) {\n                    &lt;li class=\"list-group-item\"&gt;@s&lt;\/li&gt;\n                }\n            &lt;\/ul&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class BindingsModel : PageModel {\n        \n        [BindProperty(Name = \"Data\")]\n        public string[] Data { get; set; } = Array.Empty&lt;string&gt;();\n    }\n}<\/pre>\n<p class=\"body\">Model binding for an array requires setting the <code class=\"fm-code-in-text\">name<\/code> attribute to the same value for all the elements that will provide an array value. This page displays three <code class=\"fm-code-in-text\">input<\/code> elements, all of which have a <code class=\"fm-code-in-text\">name<\/code> attribute value of <code class=\"fm-code-in-text\">Data<\/code>. To allow the model binder to find the array values, I have decorated the page model\u2019s <code class=\"fm-code-in-text\">Data<\/code> property with the <code class=\"fm-code-in-text\">BindProperty<\/code> attribute and used the <code class=\"fm-code-in-text\">Name<\/code> argument.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Notice that the page model class in listing 28.22 defines no handler methods. This is unusual, but it works because there is no explicit processing required for any requests since requests only provide values for and display the <code class=\"fm-code-in-text1\">Data<\/code> array.<\/p>\n<p class=\"body\">When the HTML form is submitted, a new array is created and populated with the values from all three <code class=\"fm-code-in-text\">input<\/code> elements, which are displayed to the user. To see the binding process, restart ASP.NET Core, request http:\/\/localhost:5000\/pages\/bindings, edit the form fields, and click the Submit button. The contents of the <code class=\"fm-code-in-text\">Data<\/code> array are displayed in a list using an <code class=\"fm-code-in-text\">@foreach<\/code> expression, as shown in figure 28.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre297\" src=\"\/images\/proaspnetcore7\/000304.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.14 Model binding for array values<\/p>\n<\/div>\n<p class=\"body\">Notice that I filter out <code class=\"fm-code-in-text\">null<\/code> values when displaying the array contents.<\/p>\n<pre class=\"programlisting\">...\n@foreach (string s in Model.Data.<b class=\"fm-bold\">Where(s =&gt; s != null)<\/b>) {\n    &lt;li class=\"list-group-item\"&gt;@s&lt;\/li&gt;\n}\n...<\/pre>\n<p class=\"body\">Empty form fields produce <code class=\"fm-code-in-text\">null<\/code> values in the array, which I don\u2019t want to show in the results. In chapter 29, I show you how to ensure that values are provided for model binding properties.<\/p>\n<p class=\"fm-head2\">Specifying index positions for array values<\/p>\n<p class=\"body\">By default, arrays are populated in the order in which the form values are received from the browser, which will generally be the order in which the HTML elements are defined. The <code class=\"fm-code-in-text\">name<\/code> attribute can be used to specify the position of values in the array if you need to override the default, as shown in listing 28.23.<a id=\"calibre_link-2495\"><\/a><a id=\"calibre_link-1040\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.23 Specifying array position in the Bindings.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/bindings\"\n@model BindingsModel\n@using Microsoft.AspNetCore.Mvc\n@using Microsoft.AspNetCore.Mvc.RazorPages\n\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row\"&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;form asp-page=\"Bindings\" method=\"post\"&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label&gt;Value #1&lt;\/label&gt;\n                    <b class=\"fm-bold\">&lt;input class=\"form-control\" name=\"Data[1]\"<\/b> \n                        <b class=\"fm-bold\">value=\"Item 1\" \/&gt;<\/b>\n                &lt;\/div&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label&gt;Value #2&lt;\/label&gt;\n                    <b class=\"fm-bold\">&lt;input class=\"form-control\" name=\"Data[0]\"<\/b> \n                        <b class=\"fm-bold\">value=\"Item 2\" \/&gt;<\/b>\n                &lt;\/div&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label&gt;Value #3&lt;\/label&gt;\n                    <b class=\"fm-bold\">&lt;input class=\"form-control\" name=\"Data[2]\"<\/b> \n                        <b class=\"fm-bold\">value=\"Item 3\" \/&gt;<\/b>\n                &lt;\/div&gt;\n                &lt;button type=\"submit\" class=\"btn btn-primary\"&gt;\n                    Submit\n                &lt;\/button&gt;\n                &lt;a class=\"btn btn-secondary\" asp-page=\"Bindings\"&gt;Reset&lt;\/a&gt;\n            &lt;\/form&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;ul class=\"list-group\"&gt;\n                @foreach (string s in Model.Data.Where(s =&gt; s != null)) {\n                    &lt;li class=\"list-group-item\"&gt;@s&lt;\/li&gt;\n                }\n            &lt;\/ul&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class BindingsModel : PageModel {\n        \n        [BindProperty(Name = \"Data\")]\n        public string[] Data { get; set; } = Array.Empty&lt;string&gt;();\n    }\n}<\/pre>\n<p class=\"body\">The array index notation is used to specify the position of a value in the data-bound array. Restart ASP.NET Core, use a browser to request http:\/\/localhost:5000\/pages\/bindings, and submit the form; you will see the items appear in the order dictated by the <code class=\"fm-code-in-text\">name<\/code> attributes, as shown in figure 28.15. The index notation must be applied to all the HTML elements that provide array values, and there must not be any gaps in the numbering sequence.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre298\" src=\"\/images\/proaspnetcore7\/000305.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.15 Specifying array position<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-536\">28.5.2 Binding to simple collections<\/h3>\n<p class=\"body\">The model binding process can create collections as well as arrays. For sequence collections, such as lists and sets, only the type of the property or parameter that is used by the model binder is changed, as shown in listing 28.24.<a id=\"calibre_link-2496\"><\/a><a id=\"calibre_link-2497\"><\/a><a id=\"calibre_link-2498\"><\/a><a id=\"calibre_link-2499\"><\/a><a id=\"calibre_link-2500\"><\/a><a id=\"calibre_link-2501\"><\/a><a id=\"calibre_link-868\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.24 Binding to a list in the Bindings.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">...\n@functions {\n\n    public class BindingsModel : PageModel {\n        \n        [BindProperty(Name = \"Data\")]\n        <b class=\"fm-bold\">public SortedSet&lt;string&gt; Data { get; set; }<\/b> \n            <b class=\"fm-bold\">= new SortedSet&lt;string&gt;();<\/b>\n    }\n}\n...<\/pre>\n<p class=\"body\">I changed the type of the <code class=\"fm-code-in-text\">Data<\/code> property to <code class=\"fm-code-in-text\">SortedSet&lt;string&gt;<\/code>. The model binding process will populate the set with the values from the <code class=\"fm-code-in-text\">input<\/code> elements, which will be sorted alphabetically. I have left the index notation on the <code class=\"fm-code-in-text\">input<\/code> element <code class=\"fm-code-in-text\">name<\/code> attributes, but they have no effect since the collection class will sort its values alphabetically. To see the effect, restart ASP.NET Core, use a browser to request http:\/\/localhost:5000\/pages\/bindings, edit the text fields, and click the Submit button. The model binding process will populate the sorted set with the form values, which will be presented in order, as shown in figure 28.16.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre299\" src=\"\/images\/proaspnetcore7\/000306.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.16 Model binding to a collection<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-537\">28.5.3 Binding to dictionaries<\/h3>\n<p class=\"body\">For elements whose <code class=\"fm-code-in-text\">name<\/code> attribute is expressed using the index notation, the model binder will use the index as the key when binding to a <code class=\"fm-code-in-text\">Dictionary<\/code>, allowing a series of elements to be transformed into key-value pairs, as shown in listing 28.25.<a id=\"calibre_link-2502\"><\/a><a id=\"calibre_link-2503\"><\/a><a id=\"calibre_link-2504\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.25 Binding to a dictionary in the Bindings.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/bindings\"\n@model BindingsModel\n@using Microsoft.AspNetCore.Mvc\n@using Microsoft.AspNetCore.Mvc.RazorPages\n\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row\"&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;form asp-page=\"Bindings\" method=\"post\"&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label&gt;Value #1&lt;\/label&gt;\n                    <b class=\"fm-bold\">&lt;input class=\"form-control\" name=\"Data[first]\"<\/b> \n                        <b class=\"fm-bold\">value=\"Item 1\" \/&gt;<\/b>\n                &lt;\/div&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label&gt;Value #2&lt;\/label&gt;\n                    <b class=\"fm-bold\">&lt;input class=\"form-control\" name=\"Data[second]\"<\/b> \n                        <b class=\"fm-bold\">value=\"Item 2\" \/&gt;<\/b>\n                &lt;\/div&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label&gt;Value #3&lt;\/label&gt;\n                    <b class=\"fm-bold\">&lt;input class=\"form-control\" name=\"Data[third]\"<\/b> \n                        <b class=\"fm-bold\">value=\"Item 3\" \/&gt;<\/b>\n                &lt;\/div&gt;\n                &lt;button type=\"submit\" class=\"btn btn-primary\"&gt;\n                    Submit\n                &lt;\/button&gt;\n                &lt;a class=\"btn btn-secondary\" asp-page=\"Bindings\"&gt;Reset&lt;\/a&gt;\n            &lt;\/form&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            <b class=\"fm-bold\">&lt;table class=\"table table-sm table-striped\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;tbody&gt;<\/b>\n                    <b class=\"fm-bold\">@foreach (string key in Model.Data.Keys) {<\/b>\n                        <b class=\"fm-bold\">&lt;tr&gt;<\/b>\n                            <b class=\"fm-bold\">&lt;th&gt;@key&lt;\/th&gt;<\/b>\n                            <b class=\"fm-bold\">&lt;td&gt;@Model.Data[key]&lt;\/td&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n                    <b class=\"fm-bold\">}<\/b>\n                <b class=\"fm-bold\">&lt;\/tbody&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/table&gt;<\/b>\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class BindingsModel : PageModel {\n        \n        [BindProperty(Name = \"Data\")]\n        <b class=\"fm-bold\">public Dictionary&lt;string, string&gt; Data { get; set; }<\/b>\n            <b class=\"fm-bold\">= new Dictionary&lt;string, string&gt;();<\/b>\n    }\n}<\/pre>\n<p class=\"body\">All elements that provide values for the collection must share a common prefix, which is <code class=\"fm-code-in-text\">Data<\/code> in this example, followed by the key value in square brackets. The keys for this example are the strings <code class=\"fm-code-in-text\">first<\/code>, <code class=\"fm-code-in-text\">second<\/code>, and <code class=\"fm-code-in-text\">third<\/code>, and they will be used as the keys for the content the user provides in the text fields. To see the binding process, restart ASP.NET Core, request http:\/\/localhost:5000\/pages\/bindings, edit the text fields, and submit the form. The keys and values from the form data will be displayed in a table, as shown in figure 28.17.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre300\" src=\"\/images\/proaspnetcore7\/000307.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.17 Model binding to a dictionary<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-538\">28.5.4 Binding to collections of complex types<\/h3>\n<p class=\"body\">The examples in this section have all been collections of simple types, but the same process can be used for complex types, too. To demonstrate, listing 28.26 revises the Razor Page to gather details used to bind to an array of <code class=\"fm-code-in-text\">Product<\/code> objects.<a id=\"calibre_link-2505\"><\/a><a id=\"calibre_link-1043\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.26 Binding to complex types in the Bindings.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/bindings\"\n@model BindingsModel\n@using Microsoft.AspNetCore.Mvc\n@using Microsoft.AspNetCore.Mvc.RazorPages\n\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row\"&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;form asp-page=\"Bindings\" method=\"post\"&gt;\n                <b class=\"fm-bold\">@for (int i = 0; i &lt; 2; i++) {<\/b>\n                    <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;label&gt;Name #@i&lt;\/label&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;input class=\"form-control\" name=\"Data[@i].Name\"<\/b>\n                           <b class=\"fm-bold\">value=\"Product-@i\" \/&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;label&gt;Price #@i&lt;\/label&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;input class=\"form-control\" name=\"Data[@i].Price\"<\/b>\n                           <b class=\"fm-bold\">value=\"@(100 + i)\" \/&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n                <b class=\"fm-bold\">}<\/b>\n                &lt;button type=\"submit\" class=\"btn btn-primary\"&gt;\n                    Submit\n                &lt;\/button&gt;\n                &lt;a class=\"btn btn-secondary\" asp-page=\"Bindings\"&gt;Reset&lt;\/a&gt;\n            &lt;\/form&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;table class=\"table table-sm table-striped\"&gt;\n                &lt;tbody&gt;\n                    <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;\/tr&gt;<\/b>\n                    <b class=\"fm-bold\">@foreach (Product p in Model.Data) {<\/b>\n                        <b class=\"fm-bold\">&lt;tr&gt;<\/b>\n                            <b class=\"fm-bold\">&lt;td&gt;@p.Name&lt;\/td&gt;<\/b>\n                            <b class=\"fm-bold\">&lt;td&gt;@p.Price&lt;\/td&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n                    <b class=\"fm-bold\">}<\/b>\n                &lt;\/tbody&gt;\n            &lt;\/table&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class BindingsModel : PageModel {\n        \n        [BindProperty(Name = \"Data\")]\n        <b class=\"fm-bold\">public Product[] Data { get; set; } = Array.Empty&lt;Product&gt;();<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">name<\/code> attributes for the <code class=\"fm-code-in-text\">input<\/code> elements use the array notation, followed by a period, followed by the name of the complex type properties they represent. To define elements for the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Price<\/code> properties, this requires elements like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;input class=\"form-control\" <b class=\"fm-bold\">name=\"Data[0].Name\"<\/b> \/&gt;\n...\n&lt;input class=\"form-control\" <b class=\"fm-bold\">name=\"Data[0].Price\"<\/b> \/&gt;\n...<\/pre>\n<p class=\"body\">During the binding process, the model binder will attempt to locate values for all the <code class=\"fm-code-in-text\">public<\/code> properties defined by the target type, repeating the process for each set of values in the form data.<\/p>\n<p class=\"body\">This example relies on model binding for the <code class=\"fm-code-in-text\">Price<\/code> property defined by the <code class=\"fm-code-in-text\">Product<\/code> class, which was excluded from the binding process with the <code class=\"fm-code-in-text\">BindNever<\/code> attribute. Remove the attribute from the property, as shown in listing 28.27.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.27 Removing an attribute in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Text.Json.Serialization;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\n\nnamespace WebApp.Models {\n    public class Product {\n        \n        public long ProductId { get; set; }\n                \n        public required string Name { get; set; }\n                \n        [Column(TypeName = \"decimal(8, 2)\")]\n        <b class=\"fm-bold\">\/\/[BindNever]<\/b>        \n        public decimal Price { get; set; }\n                \n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        public long SupplierId { get; set; }\n                \n        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/pages\/bindings. Enter names and prices into the text fields and submit the form, and you will see the details of the <code class=\"fm-code-in-text\">Product<\/code> objects created from the data displayed in a table, as shown in figure 28.18.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre301\" src=\"\/images\/proaspnetcore7\/000308.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.18 Binding to a collection of complex types<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-539\">28.6 Specifying a model binding source<\/h2>\n<p class=\"body\">As I explained at the start of the chapter, the default model binding process looks for data in four places: the form data values, the request body (for web service controllers only), the routing data, and the request query string.<a id=\"calibre_link-2506\"><\/a><a id=\"calibre_link-2507\"><\/a><a id=\"calibre_link-1051\"><\/a><\/p>\n<p class=\"body\">The default search sequence isn\u2019t always helpful, either because you always want data to come from a specific part of the request or because you want to use a data source that isn\u2019t searched by default. The model binding feature includes a set of attributes used to override the default search behavior, as described in table 28.3.<a id=\"calibre_link-2508\"><\/a><a id=\"calibre_link-2509\"><\/a><a id=\"calibre_link-2510\"><\/a><a id=\"calibre_link-2511\"><\/a><a id=\"calibre_link-2512\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> There is also the <code class=\"fm-code-in-text1\">FromService<\/code> attribute, which doesn\u2019t get a value from the request, but through the dependency injection feature described in chapter 14.<a id=\"calibre_link-2513\"><\/a><a id=\"calibre_link-1054\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 28.3 The model binding source attributes<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2514\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">FromForm<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to select form data as the source of binding data. The name of the parameter is used to locate a form value by default, but this can be changed using the <code class=\"fm-code-in-text1\">Name<\/code> property, which allows a different name to be specified.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">FromRoute<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to select the routing system as the source of binding data. The name of the parameter is used to locate a route data value by default, but this can be changed using the <code class=\"fm-code-in-text1\">Name<\/code> property, which allows a different name to be specified.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">FromQuery<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to select the query string as the source of binding data. The name of the parameter is used to locate a query string value by default, but this can be changed using the <code class=\"fm-code-in-text1\">Name<\/code> property, which allows a different query string key to be specified.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">FromHeader<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to select a request header as the source of binding data. The name of the parameter is used as the header name by default, but this can be changed using the <code class=\"fm-code-in-text1\">Name<\/code> property, which allows a different header name to be specified.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">FromBody<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify that the request body should be used as the source of binding data, which is required when you want to receive data from requests that are not form-encoded, such as in API controllers that provide web services.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">FromForm<\/code>, <code class=\"fm-code-in-text\">FromRoute<\/code>, and <code class=\"fm-code-in-text\">FromQuery<\/code> attributes allow you to specify that the model binding data will be obtained from one of the standard locations but without the normal search sequence. Earlier in the chapter, I used this URL:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost:5000\/controllers\/Form\/Index\/<b class=\"fm-bold\">5<\/b>?id=<b class=\"fm-bold\">1<\/b><\/pre>\n<p class=\"body\">This URL contains two possible values that can be used for the <code class=\"fm-code-in-text\">id<\/code> parameter of the <code class=\"fm-code-in-text\">Index<\/code> action method on the <code class=\"fm-code-in-text\">Form<\/code> controller. The routing system will assign the final segment of the URL to a variable called <code class=\"fm-code-in-text\">id<\/code>, which is defined in the default URL pattern for controllers, and the query string also contains an <code class=\"fm-code-in-text\">id<\/code> value. The default search pattern means that the model binding data will be taken from the route data and the query string will be ignored.<\/p>\n<p class=\"body\">In listing 28.28, I have applied the <code class=\"fm-code-in-text\">FromQuery<\/code> attribute to the <code class=\"fm-code-in-text\">id<\/code> parameter defined by the <code class=\"fm-code-in-text\">Index<\/code> action method, which overrides the default search sequence.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.28 Selecting the query string in the Controllers\/FormController.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing System.Text.Json;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; Index([FromQuery] long? id) {<\/b>\n            ViewBag.Categories = new SelectList(context.Categories, \n                \"CategoryId\", \"Name\");\n            return View(\"Form\", await context.Products\n                .Include(p =&gt; p.Category)\n                .Include(p =&gt; p.Supplier)\n                .FirstOrDefaultAsync(p =&gt; id == null || p.ProductId == id)\n            ?? new() { Name = string.Empty });\n        }\n                \n        [HttpPost]\n        public IActionResult SubmitForm([Bind(\"Name\", \"Category\")] \n                Product product) {\n            TempData[\"name\"] = product.Name;\n            TempData[\"price\"] = product.Price.ToString();\n            TempData[\"category name\"] = product.Category?.Name;\n            return RedirectToAction(nameof(Results));\n        }\n                \n        public IActionResult Results() {\n            return View();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The attribute specifies the source for the model binding process, which you can see by restarting ASP.NET Core and using a browser to request http:\/\/localhost:5000\/controllers\/form\/index\/5?id=1. Instead of using the value that has been matched by the routing system, the query string will be used instead, producing the response shown in figure 28.19. No other location will be used if the query string doesn\u2019t contain a suitable value for the model binding process.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can still bind complex types when specifying a model binding source such as the query string. For each simple property in the parameter type, the model binding process will look for a query string key with the same name.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre302\" src=\"\/images\/proaspnetcore7\/000309.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.19 Specifying a model binding data source<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-540\">28.6.1 Selecting a binding source for a property<\/h3>\n<p class=\"body\">The same attributes can be used to model bind properties defined by a page model or a controller, as shown in listing 28.29.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.29 Selecting the query string in the Bindings.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">...\n@functions {\n\n    public class BindingsModel : PageModel {\n        \n        <b class=\"fm-bold\">\/\/[BindProperty(Name = \"Data\")]<\/b>\n        <b class=\"fm-bold\">[FromQuery(Name = \"Data\")]<\/b>\n        public Product[] Data { get; set; } = Array.Empty&lt;Product&gt;();\n    }\n}\n...<\/pre>\n<p class=\"body\">The use of the <code class=\"fm-code-in-text\">FromQuery<\/code> attribute means the query string is used as the source of values for the model binder as it creates the <code class=\"fm-code-in-text\">Product<\/code> array, which you can see by starting ASP.NET Core and requesting http:\/\/localhost:5000\/pages\/bindings?data[0].name=Skis&amp;data[0].price=500, which produces the response shown in figure 28.20.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> In this example, I have used a GET request because it allows the query string to be easily set. Although it is harmless in such a simple example, care must be taken when sending GET requests that modify the state of the application. As noted previously, making changes in GET requests can lead to problems.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre303\" src=\"\/images\/proaspnetcore7\/000310.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.20 Specifying a model binding data source in a Razor Page<\/p>\n<\/div>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Although it is rarely used, you can bind complex types using header values by applying the <code class=\"fm-code-in-text1\">FromHeader<\/code> attribute to the properties of a model class.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-541\">28.6.2 Using headers for model binding<\/h3>\n<p class=\"body\"><a id=\"calibre_link-1053\"><\/a>The <code class=\"fm-code-in-text\">FromHeader<\/code> attribute allows HTTP request headers to be used as the source for binding data. In listing 28.30, I have added a simple action method to the <code class=\"fm-code-in-text\">Form<\/code> controller that defines a parameter that will be model bound from a standard HTTP request header.<a id=\"calibre_link-2515\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.30 Model binding from a header in the Controllers\/FormController.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing System.Text.Json;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n\n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        \/\/ ...other actions omitted for brevity...\n                \n        <b class=\"fm-bold\">public string Header([FromHeader] string accept) {<\/b>\n            <b class=\"fm-bold\">return $\"Header: {accept}\";<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-2516\"><\/a>The <code class=\"fm-code-in-text\">Header<\/code> action method defines an <code class=\"fm-code-in-text\">accept<\/code> parameter, the value for which will be taken from the <code class=\"fm-code-in-text\">Accept<\/code> header in the current request and returned as the method result. Restart ASP.NET Core and request http:\/\/localhost:5000\/controllers\/form\/header, and you will see a result like this:<\/p>\n<pre class=\"programlisting\">Header: text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,\n   image\/apng,*\/*;q=0.8,application\/signed-exchange;v=b3<\/pre>\n<p class=\"body\">Not all HTTP header names can be easily selected by relying on the name of the action method parameter because the model binding system doesn\u2019t convert from C# naming conventions to those used by HTTP headers. In these situations, you must configure the <code class=\"fm-code-in-text\">FromHeader<\/code> attribute using the <code class=\"fm-code-in-text\">Name<\/code> property to specify the name of the header, as shown in listing 28.31.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.31 Selecting a header by name in the Controllers\/FormController.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing System.Text.Json;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        \/\/ ...other actions omitted for brevity...\n                \n        <b class=\"fm-bold\">public string Header([FromHeader(Name = \"Accept-Language\")]<\/b> \n                <b class=\"fm-bold\">string accept) {<\/b>\n            return $\"Header: {accept}\";\n        }\n    }\n}<\/pre>\n<p class=\"body\">I can\u2019t use <code class=\"fm-code-in-text\">Accept-Language<\/code> as the name of a C# parameter, and the model binder won\u2019t automatically convert a name like <code class=\"fm-code-in-text\">AcceptLanguage<\/code> into <code class=\"fm-code-in-text\">Accept-Language<\/code> so that it matches the header. Instead, I used the <code class=\"fm-code-in-text\">Name<\/code> property to configure the attribute so that it matches the right header. If you restart ASP.NET Core and request http:\/\/localhost:5000\/controllers\/form\/header, you will see a result like this, which will vary based on your locale settings:<\/p>\n<pre class=\"programlisting\">Header: en-GB,en-US;q=0.9,en;q=0.8<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-542\">28.6.3 Using request bodies as binding sources<\/h3>\n<p class=\"body\">Not all data sent by clients is sent as form data, such as when a JavaScript client sends JSON data to an API controller. The <code class=\"fm-code-in-text\">FromBody<\/code> attribute specifies that the request body should be decoded and used as a source of model binding data. In listing 28.32, I have added a new action method to the <code class=\"fm-code-in-text\">Form<\/code> controller with a parameter that is decorated with the <code class=\"fm-code-in-text\">FromBody<\/code> attribute.<a id=\"calibre_link-2517\"><\/a><a id=\"calibre_link-1052\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">FromBody<\/code> attribute isn\u2019t required for controllers that are decorated with the <code class=\"fm-code-in-text1\">ApiController<\/code> attribute.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.32 Adding an action method in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc.Rendering;\nusing System.Text.Json;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n                \n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        \/\/ ...other actions omitted for brevity...\n                \n        <b class=\"fm-bold\">[HttpPost]<\/b>\n        <b class=\"fm-bold\">[IgnoreAntiforgeryToken]<\/b>\n        <b class=\"fm-bold\">public Product Body([FromBody] Product model) {<\/b>\n            <b class=\"fm-bold\">return model;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">To test the model binding process, restart ASP.NET Core, open a new PowerShell command prompt, and run the command in listing 28.33 to send a request to the application.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> I added the <code class=\"fm-code-in-text1\">IgnoreAntiforgeryToken<\/code> to the action method in listing 28.32 because the request that I am going to send won\u2019t include an anti-forgery token, which I described in chapter 27.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.33 Sending a request<\/p>\n<pre class=\"programlisting\">Invoke-RestMethod http:\/\/localhost:5000\/controllers\/form\/body -Method POST \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>-Body  (@{ Name=\"Soccer Boots\"; Price=89.99} | ConvertTo-Json) \n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>-ContentType \"application\/json\"<\/pre>\n<p class=\"body\">The JSON-encoded request body is used to model bind the action method parameter, which produces the following response:<\/p>\n<pre class=\"programlisting\">productId  : 0\nname       : Soccer Boots\nprice      : 89.99\ncategoryId : 0\ncategory   :\nsupplierId : 0\nsupplier   :<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-543\">28.7 Manual model binding<\/h2>\n<p class=\"body\">Model binding is applied automatically when you define a parameter for an action or handler method or apply the <code class=\"fm-code-in-text\">BindProperty<\/code> attribute. Automatic model binding works well if you can consistently follow the name conventions and you always want the process to be applied. If you need to take control of the binding process or you want to perform binding selectively, then you can perform model binding manually, as shown in listing 28.34.<a id=\"calibre_link-2518\"><\/a><a id=\"calibre_link-1055\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 28.34 Manually binding in the Bindings.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/bindings\"\n@model BindingsModel\n@using Microsoft.AspNetCore.Mvc\n@using Microsoft.AspNetCore.Mvc.RazorPages\n\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row\"&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;form asp-page=\"Bindings\" method=\"post\"&gt;\n                <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;label&gt;Name&lt;\/label&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Data.Name\" \/&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n                <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;label&gt;Price&lt;\/label&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"Data.Price\"<\/b>\n                           <b class=\"fm-bold\">value=\"@(Model.Data.Price + 1)\" \/&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n                <b class=\"fm-bold\">&lt;div class=\"form-check m-2\"&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;input class=\"form-check-input\" type=\"checkbox\"<\/b> \n                        <b class=\"fm-bold\">name=\"bind\" value=\"true\" checked \/&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;label class=\"form-check-label\"&gt;Model Bind?&lt;\/label&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n                <b class=\"fm-bold\">&lt;button type=\"submit\" class=\"btn btn-primary\"&gt;<\/b>\n                    <b class=\"fm-bold\">Submit<\/b>\n                <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n                <b class=\"fm-bold\">&lt;a class=\"btn btn-secondary\" asp-page=\"Bindings\"&gt;Reset&lt;\/a&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/form&gt;<\/b>\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;table class=\"table table-sm table-striped\"&gt;\n                &lt;tbody&gt;\n                    &lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;&lt;\/tr&gt;\n                    &lt;tr&gt;\n                        &lt;td&gt;@Model.Data.Name&lt;\/td&gt;\n                        &lt;td&gt;@Model.Data.Price&lt;\/td&gt;\n                    &lt;\/tr&gt;\n                &lt;\/tbody&gt;\n            &lt;\/table&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class BindingsModel : PageModel {\n        \n        <b class=\"fm-bold\">public Product Data { get; set; }<\/b>\n            <b class=\"fm-bold\">= new Product() { Name = \"Skis\", Price = 500 };<\/b>\n                        \n        <b class=\"fm-bold\">public async Task OnPostAsync([FromForm] bool bind) {<\/b>\n            <b class=\"fm-bold\">if (bind) {<\/b>\n                <b class=\"fm-bold\">await TryUpdateModelAsync&lt;Product&gt;(Data,<\/b>\n                    <b class=\"fm-bold\">\"data\", p =&gt; p.Name, p =&gt; p.Price);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-2519\"><\/a>Manual model binding is performed using the <code class=\"fm-code-in-text\">TryUpdateModelAsync<\/code> method, which is provided by the <code class=\"fm-code-in-text\">PageModel<\/code> and <code class=\"fm-code-in-text\">ControllerBase<\/code> classes, which means it is available for both Razor Pages and MVC controllers.<a id=\"calibre_link-2520\"><\/a><a id=\"calibre_link-2521\"><\/a><\/p>\n<p class=\"body\">This example mixes automatic and manual model binding. The <code class=\"fm-code-in-text\">OnPostAsync<\/code> method uses automatic model binding to receive a value for its <code class=\"fm-code-in-text\">bind<\/code> parameter, which has been decorated with the <code class=\"fm-code-in-text\">FromForm<\/code> attribute. If the value of the parameter is <code class=\"fm-code-in-text\">true<\/code>, the <code class=\"fm-code-in-text\">TryUpdateModelAsync<\/code> method is used to apply model binding. The arguments to the <code class=\"fm-code-in-text\">TryUpdateModelAsync<\/code> method are the object that will be model bound, the prefix for the values, and a series of expressions that select the properties that will be included in the process, although there are other versions of the <code class=\"fm-code-in-text\">TryUpdateModelAsync<\/code> method available.<\/p>\n<p class=\"body\">The result is that the model binding process for the <code class=\"fm-code-in-text\">Data<\/code> property is performed only when the user checks the checkbox added to the form in listing 28.34. If the checkbox is unchecked, then no model binding occurs, and the form data is ignored. To make it obvious when model binding is used, the value of the <code class=\"fm-code-in-text\">Price<\/code> property is incremented when the form is rendered. To see the effect, restart ASP.NET Core, request http:\/\/localhost:5000\/pages\/bindings, and submit the form with the checkbox checked and then unchecked, as shown in figure 28.21.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre304\" src=\"\/images\/proaspnetcore7\/000311.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 28.21 Using manual model binding<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2522\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Model binding is the process by which request data is converted into parameters for action methods or page-handler methods.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The model binder is integrated into ASP.NET Core and follows well-defined conventions to bind to simple and complex data types.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core provides attributes for fine-tuning the model binding process when the request data doesn\u2019t match the expected conventions.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The model-binding process can read values from form data, the current route, the query string, the request header, and request body.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Model binding is performed automatically but can be performed manually, which provides precise control over the binding process.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-544\">\n<div class=\"calibre1\" id=\"calibre_link-2523\">\n<h1 class=\"tochead\" id=\"calibre_link-2524\"><a id=\"calibre_link-2525\"><\/a><a id=\"calibre_link-2526\"><\/a><a id=\"calibre_link-2527\"><\/a><a id=\"calibre_link-2528\"><\/a><a id=\"calibre_link-2529\"><\/a>29 Using model validation<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Understanding the ASP.NET Core data validation features<\/li>\n<li class=\"co-summary-bullet\">Applying validation to form data<\/li>\n<li class=\"co-summary-bullet\">Displaying validation errors to the user<\/li>\n<li class=\"co-summary-bullet\">Explicitly validating data in a controller or Razor Page<\/li>\n<li class=\"co-summary-bullet\">Specifying validation rules using attributes<\/li>\n<li class=\"co-summary-bullet\">Using JavaScript to perform client-side validation<\/li>\n<\/ul>\n<p class=\"body\">In the previous chapter, I showed you how the model binding process creates objects from HTTP requests. Throughout that chapter, I simply displayed the data that the application received. That\u2019s because the data that users provide should not be used until it has been inspected to ensure that the application is able to use it. The reality is that users will often enter data that isn\u2019t valid and cannot be used, which leads me to the topic of this chapter: model validation.<\/p>\n<p class=\"body\"><i class=\"fm-italics\">Model validation<\/i> is the process of ensuring the data received by the application is suitable for binding to the model and, when this is not the case, providing useful information to the user that will help explain the problem.<\/p>\n<p class=\"body\">The first part of the process, checking the data received, is one of the most important ways to preserve the integrity of an application\u2019s data. Rejecting data that cannot be used can prevent odd and unwanted states from arising in the application. The second part of the validation process is helping the user correct the problem and is equally important. Without the feedback needed to correct the problem, users become frustrated and confused. In public-facing applications, this means users will simply stop using the application. In corporate applications, this means the user\u2019s workflow will be hindered. Neither outcome is desirable, but fortunately, ASP.NET Core provides extensive support for model validation. Table 29.1 puts model validation in context.<\/p>\n<p class=\"fm-table-caption\">Table 29.1 Putting model validation in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2530\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What is it?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Model validation is the process of ensuring that the data provided in a request is valid for use in the application.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why is it useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Users do not always enter valid data, and using unvalidated data can produce unexpected and undesirable errors.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How is it used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Controllers and Razor Pages check the outcome of the validation process, and tag helpers are used to include validation feedback in views displayed to the user. Validation can be performed automatically during the model binding process and can be supplemented with custom validation.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">It is important to test the efficacy of your validation code to ensure that it covers the full range of values that the application can receive.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Model validation is optional, but it is a good idea to use it whenever using model binding.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 29.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 29.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2531\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Validating data<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Manually use the <code class=\"fm-code-in-text1\">ModelState<\/code> features or apply validation attributes<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">5, 9, 14&ndash;22<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Displaying validation messages<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the classes to which form elements are assigned and the validation tag helpers<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">6&ndash;8, 10&ndash;13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Validating data before the form is submitted<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use client-side and remote validation<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">23&ndash;27<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-545\">29.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the WebApp project from chapter 28. To prepare for this chapter, change the contents of the <code class=\"fm-code-in-text\">Form<\/code> controller\u2019s <code class=\"fm-code-in-text\">Form<\/code> view so it contains <code class=\"fm-code-in-text\">input<\/code> elements for each of the properties defined by the <code class=\"fm-code-in-text\">Product<\/code> class, excluding the navigation properties used by Entity Framework Core, as shown in listing 29.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.1 Changing elements in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;label&gt;CategoryId&lt;\/label&gt;<\/b>\n        <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"CategoryId\"  \/&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;label&gt;SupplierId&lt;\/label&gt;<\/b>\n        <b class=\"fm-bold\">&lt;input class=\"form-control\" asp-for=\"SupplierId\"  \/&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">Replace the contents of the <code class=\"fm-code-in-text\">FormController.cs<\/code> file with those shown in listing 29.2, which adds support for displaying the properties defined in listing 29.1 and removes model binding attributes and action methods that are no longer required.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.2 Replacing the FormController.cs file\u2019s contents in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n\n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n \n        public async Task&lt;IActionResult&gt; Index(long? id) {\n            return View(\"Form\", await context.Products\n                .OrderBy(p =&gt; p.ProductId)\n                .FirstOrDefaultAsync(p =&gt; id == null \n                    || p.ProductId == id));\n        }\n \n        [HttpPost]\n        public IActionResult SubmitForm(Product product) {\n            TempData[\"name\"] = product.Name;\n            TempData[\"price\"] = product.Price.ToString();\n            TempData[\"categoryId\"] = product.CategoryId.ToString();\n            TempData[\"supplierId\"] = product.SupplierId.ToString();\n            return RedirectToAction(nameof(Results));\n        }\n \n        public IActionResult Results() {\n            return View(TempData);\n        }\n    }\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-546\">29.1.1 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 29.3 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.3 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-547\">29.1.2 Running the example application<\/h3>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 29.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.4 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/controllers\/form, which will display an HTML form. Click the Submit button, and the form data will be displayed, as shown in figure 29.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre305\" src=\"\/images\/proaspnetcore7\/000312.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-548\">29.2 Understanding the need for model validation<\/h2>\n<p class=\"body\">Model validation is the process of enforcing the requirements that an application has for the data it receives from clients. Without validation, an application will try to operate on any data it receives, which can lead to exceptions and unexpected behaviors that appear immediately or long-term problems that appear gradually as the database is populated with bad, incomplete, or malicious data.<\/p>\n<p class=\"body\">Currently, the action and handler methods that receive form data will accept any data that the user submits, which is why the examples just display the form data and don\u2019t store it in the database.<\/p>\n<p class=\"body\">Most data values have constraints of some sort. This can involve requiring a value to be provided, requiring the value to be a specific type, and requiring the value to fall within a specific range.<\/p>\n<p class=\"body\">Before I can safely store a <code class=\"fm-code-in-text\">Product<\/code> object in the database, for example, I need to make sure that the user provides values for the <code class=\"fm-code-in-text\">Name<\/code>, <code class=\"fm-code-in-text\">Price<\/code>, <code class=\"fm-code-in-text\">CategoryId<\/code>, and <code class=\"fm-code-in-text\">SupplierId<\/code> properties. The <code class=\"fm-code-in-text\">Name<\/code> value can be any valid string, the <code class=\"fm-code-in-text\">Price<\/code> property must be a valid currency amount, and the <code class=\"fm-code-in-text\">CategoryId<\/code> and <code class=\"fm-code-in-text\">SupplierId<\/code> properties must correspond to existing <code class=\"fm-code-in-text\">Supplier<\/code> and <code class=\"fm-code-in-text\">Category<\/code> products in the database. In the following sections, I demonstrate how model validation can be used to enforce these requirements by checking the data that the application receives and providing feedback to the user when the application cannot use the data the user has submitted.<a id=\"calibre_link-2532\"><\/a><a id=\"calibre_link-1066\"><\/a><\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-549\">29.3 Validating data<\/h2>\n<p class=\"body\">Although it is not evident, ASP.NET Core is already performing some basic data validation during the model binding process, but the errors it detects are being discarded because ASP.NET Core hasn\u2019t been told how to respond to them. Listing 29.5 checks the outcome of the validation process so that the data values the user has provided will be used only if they are valid.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.5 Checking the outcome in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n\n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n \n        public async Task&lt;IActionResult&gt; Index(long? id) {\n            return View(\"Form\", await context.Products\n                .OrderBy(p =&gt; p.ProductId)\n                .FirstOrDefaultAsync(p =&gt; id == null \n                    || p.ProductId == id));\n        }\n \n        [HttpPost]\n        public IActionResult SubmitForm(Product product) {\n            <b class=\"fm-bold\">if (ModelState.IsValid) {<\/b>\n                TempData[\"name\"] = product.Name;\n                TempData[\"price\"] = product.Price.ToString();\n                TempData[\"categoryId\"] = product.CategoryId.ToString();\n                TempData[\"supplierId\"] = product.SupplierId.ToString();\n                return RedirectToAction(nameof(Results));\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return View(\"Form\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        }\n \n        public IActionResult Results() {\n            return View(TempData);\n        }\n    }\n}<\/pre>\n<p class=\"body\">I determine if the data provided by the user is valid using the <code class=\"fm-code-in-text\">ModelStateDictionary<\/code> object that is returned by the <code class=\"fm-code-in-text\">ModelState<\/code> property inherited from the <code class=\"fm-code-in-text\">ControllerBase<\/code> class.<\/p>\n<p class=\"body\"><a id=\"calibre_link-1061\"><\/a>As its name suggests, the <code class=\"fm-code-in-text\">ModelStateDictionary<\/code> class is a dictionary used to track details of the state of the model object, with an emphasis on validation errors. Table 29.3 describes the most important <code class=\"fm-code-in-text\">ModelStateDictionary<\/code> members.<a id=\"calibre_link-2533\"><\/a><a id=\"calibre_link-2534\"><\/a><a id=\"calibre_link-2535\"><\/a><a id=\"calibre_link-2536\"><\/a><a id=\"calibre_link-2537\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 29.3 Selected ModelStateDictionary members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2538\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddModelError(property, message)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to record a model validation error for the specified property.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetValidationState(property)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to determine whether there are model validation errors for a specific property, expressed as a value from the <code class=\"fm-code-in-text1\">ModelValidationState<\/code> enumeration.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsValid<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns <code class=\"fm-code-in-text1\">true<\/code> if all the model properties are valid and returns <code class=\"fm-code-in-text1\">false<\/code> otherwise.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Clear()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property clears the validation state.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">If the validation process has detected problems, then the <code class=\"fm-code-in-text\">IsValid<\/code> property will return <code class=\"fm-code-in-text\">false<\/code>. The <code class=\"fm-code-in-text\">SubmitForm<\/code> action method deals with invalid data by returning the same view, like this:<\/p>\n<pre class=\"programlisting\">...\nif (ModelState.IsValid) {\n   TempData[\"name\"] = product.Name;\n   TempData[\"price\"] = product.Price.ToString();\n   TempData[\"category name\"] = product.Category?.Name;\n   return RedirectToAction(nameof(Results));\n} else {\n    <b class=\"fm-bold\">return View(\"Form\");<\/b>\n}\n...<\/pre>\n<p class=\"body\">It may seem odd to deal with a validation error by calling the <code class=\"fm-code-in-text\">View<\/code> method, but the context data provided to the view contains details of the model validation errors; these details are used by the tag helper to transform the <code class=\"fm-code-in-text\">input<\/code> elements.<a id=\"calibre_link-2539\"><\/a><a id=\"calibre_link-2540\"><\/a><a id=\"calibre_link-1070\"><\/a><\/p>\n<p class=\"body\">To see how this works, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/controllers\/form. Clear the contents of the Name field and click the Submit button. There won\u2019t be any visible change in the content displayed by the browser, but if you examine the <code class=\"fm-code-in-text\">input<\/code> element for the <code class=\"fm-code-in-text\">Name<\/code> field, you will see the element has been transformed. Here is the <code class=\"fm-code-in-text\">input<\/code> element before the form was submitted:<\/p>\n<pre class=\"programlisting\">&lt;input class=\"form-control\" type=\"text\" data-val=\"true\"\n    data-val-required=\"The Name field is required.\" id=\"Name\"\n    name=\"Name\" value=\"Kayak\"&gt;<\/pre>\n<p class=\"body\">Here is the <code class=\"fm-code-in-text\">input<\/code> element after the form has been submitted:<\/p>\n<pre class=\"programlisting\">&lt;input class=\"form-control <b class=\"fm-bold\">input-validation-error<\/b>\" type=\"text\" \n    data-val=\"true\" data-val-required=\"The Name field is required.\"\n    id=\"Name\" name=\"Name\" value=\"\"&gt;<\/pre>\n<p class=\"body\">The tag helper adds elements whose values have failed validation to the <code class=\"fm-code-in-text\">input-validation-error<\/code> class, which can then be styled to highlight the problem to the user.<\/p>\n<p class=\"body\">You can do this by defining custom CSS styles in a stylesheet, but a little extra work is required if you want to use the built-in validation styles that CSS libraries like Bootstrap provides. The name of the class added to the <code class=\"fm-code-in-text\">input<\/code> elements cannot be changed, which means that some JavaScript code is required to map between the name used by ASP.NET Core and the CSS error classes provided by Bootstrap.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Using JavaScript code like this can be awkward, and it can be tempting to use custom CSS styles, even when working with a CSS library like Bootstrap. However, the colors used for validation classes in Bootstrap can be overridden by using themes or by customizing the package and defining your own styles, which means you have to ensure that any changes to the theme are matched by corresponding changes to any custom styles you define. Ideally, Microsoft will make the validation class names configurable in a future release of ASP.NET Core, but until then, using JavaScript to apply Bootstrap styles is a more robust approach than creating custom stylesheets.<\/p>\n<p class=\"body\">To define the JavaScript code so that it can be used by both controllers and Razor Pages, use the Razor View - Empty template in Visual Studio to add a file named <code class=\"fm-code-in-text\">_Validation.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder with the content shown in listing 29.6. Visual Studio Code doesn\u2019t require templates, and you can just add a file named <code class=\"fm-code-in-text\">_Validation.cshtml<\/code> in the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder with the code shown in the listing.<a id=\"calibre_link-2541\"><\/a><a id=\"calibre_link-1072\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.6 The contents of the _Validation.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;script type=\"text\/javascript\"&gt;\n    window.addEventListener(\"DOMContentLoaded\", () =&gt; {\n        document.querySelectorAll(\"input.input-validation-error\")\n            .forEach((elem) =&gt; { elem.classList.add(\"is-invalid\"); }\n        );\n    });\n&lt;\/script&gt;<\/pre>\n<p class=\"body\">I will use the new file as a partial view, which contains a script element that uses the browser\u2019s JavaScript Document Object Model (DOM) API to locate <code class=\"fm-code-in-text\">input<\/code> elements that are members of the <code class=\"fm-code-in-text\">input-validation-error<\/code> class and adds them to the <code class=\"fm-code-in-text\">is-invalid<\/code> class (which Bootstrap uses to set the error color for form elements). Listing 29.7 uses the <code class=\"fm-code-in-text\">partial<\/code> tag helper to incorporate the new partial view into the HTML form so that fields with validation errors are highlighted.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.7 Including a partial view in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n<b class=\"fm-bold\">&lt;partial name=\"_Validation\" \/&gt;<\/b>\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;CategoryId&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"CategoryId\"  \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;SupplierId&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"SupplierId\"  \/&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">The JavaScript code runs when the browser has finished parsing all the elements in the HTML document, and the effect is to highlight the <code class=\"fm-code-in-text\">input<\/code> elements that have been assigned to the <code class=\"fm-code-in-text\">input-validaton-error<\/code> class. You can see the effect by restarting ASP.NET Core, navigating to http:\/\/localhost:5000\/controllers\/form, clearing the contents of the Name field, and submitting the form, which produces the response shown in figure 29.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre306\" src=\"\/images\/proaspnetcore7\/000313.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.2 Highlighting a validation error<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-550\">29.3.1 Displaying validation messages<\/h3>\n<p class=\"body\">Figure 29.2 makes it clear that something is wrong with the <code class=\"fm-code-in-text\">Name<\/code> field but doesn\u2019t provide any details about what problem has been detected. Providing the user with more information requires the use of a different tag helper, which adds a summary of the problems to the view, as shown in listing 29.8.<a id=\"calibre_link-1075\"><\/a><a id=\"calibre_link-2542\"><\/a><a id=\"calibre_link-2543\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.8 Displaying a summary in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{  Layout = \"_SimpleLayout\"; }\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;partial name=\"_Validation\" \/&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    <b class=\"fm-bold\">&lt;div asp-validation-summary=\"All\" class=\"text-danger\"&gt;&lt;\/div&gt;<\/b>\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;CategoryId&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"CategoryId\"  \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;SupplierId&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"SupplierId\"  \/&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ValidationSummaryTagHelper<\/code> class detects the <code class=\"fm-code-in-text\">asp-validation-summary<\/code> attribute on <code class=\"fm-code-in-text\">div<\/code> elements and responds by adding messages that describe any validation errors that have been recorded. The value of the <code class=\"fm-code-in-text\">asp-validation-summary<\/code> attribute is a value from the <code class=\"fm-code-in-text\">ValidationSummary<\/code> enumeration, which defines the values shown in table 29.4 and which I demonstrate shortly.<a id=\"calibre_link-2544\"><\/a><a id=\"calibre_link-1076\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 29.4 The ValidationSummary values<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2545\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">All<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This value is used to display all the validation errors that have been recorded.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ModelOnly<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This value is used to display only the validation errors for the entire model, excluding those that have been recorded for individual properties, as described in the \"Displaying Model-Level Messages\" section.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">None<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This value is used to disable the tag helper so that it does not transform the HTML element.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Presenting error messages helps the user understand why the form cannot be processed. Restart ASP.NET Core, request http:\/\/localhost:5000\/controllers\/form, clear the Name field, and submit the form. As figure 29.3 shows, there is now an error message that explains the problem has been detected.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre307\" src=\"\/images\/proaspnetcore7\/000314.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.3 Displaying a validation message<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-551\">29.3.2 Understanding the implicit validation checks<\/h3>\n<p class=\"body\">The error message displayed in figure 29.3 is generated by the implicit validation process, which is performed automatically during model binding.<\/p>\n<p class=\"body\">Implicit validation is simple but effective, and there are two basic checks: the user must provide a value for all properties that are defined with a non-nullable type, and ASP.NET Core must be able to parse the <code class=\"fm-code-in-text\">string<\/code> values received in the HTTP request into the corresponding property type.<\/p>\n<p class=\"body\">As a reminder, here is the definition of the <code class=\"fm-code-in-text\">Product<\/code> class, which is the class used to receive the form data:<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Text.Json.Serialization;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\n\nnamespace WebApp.Models {\n    public class Product {\n\n        public long ProductId { get; set; }\n                \n        public required string Name { get; set; }\n                \n        [Column(TypeName = \"decimal(8, 2)\")]\n        \/\/[BindNever]        \n        public decimal Price { get; set; }\n                \n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        public long SupplierId { get; set; }\n                \n        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">The required keyword has been applied to the <code class=\"fm-code-in-text\">Name<\/code> property, which is why a validation error was reported when the field was cleared in the previous section. There are no parsing issues for the <code class=\"fm-code-in-text\">Name<\/code> property because the <code class=\"fm-code-in-text\">string<\/code> value received from the HTTP request does not need any type conversion.<\/p>\n<p class=\"body\">Enter <code class=\"fm-code-in-text\">ten<\/code> into the <code class=\"fm-code-in-text\">Price<\/code> field and submit the form; you will see an error that shows that ASP.NET Core cannot parse the string in the HTTP request into the <code class=\"fm-code-in-text\">decimal<\/code> value required by the <code class=\"fm-code-in-text\">Price<\/code> property, as shown in figure 29.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre308\" src=\"\/images\/proaspnetcore7\/000315.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.4 Displaying a parsing validation message<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-552\">29.3.3 Performing explicit validation<\/h3>\n<p class=\"body\">Implicit validation takes care of the basics, but most applications require additional checks to ensure that they receive useful data. This is known as <i class=\"fm-italics\">explicit validation<\/i>, and it is done using the <code class=\"fm-code-in-text\">ModelStateDictionary<\/code> methods described in table 29.4.<\/p>\n<p class=\"body\">To avoid displaying conflicting error messages, explicit validation is typically done only when the user has provided a value that has passed the implicit checks. The <code class=\"fm-code-in-text\">ModelStateDictionary.GetValidationState<\/code> method is used to see whether there have been any errors recorded for a model property. The <code class=\"fm-code-in-text\">GetValidationState<\/code> method returns a value from the <code class=\"fm-code-in-text\">ModelValidationState<\/code> enumeration, which defines the values described in table 29.5.<a id=\"calibre_link-2546\"><\/a><a id=\"calibre_link-1077\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 29.5 The ModelValidationState values<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2547\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Unvalidated<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This value means that no validation has been performed on the model property, usually because there was no value in the request that corresponded to the property name.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Valid<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This value means that the request value associated with the property is valid.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Invalid<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This value means that the request value associated with the property is invalid and should not be used.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Skipped<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This value means that the model property has not been processed, which usually means that there have been so many validation errors that there is no point continuing to perform validation checks.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Listing 29.9 defines explicit validation checks for some of the properties defined by the <code class=\"fm-code-in-text\">Product<\/code> class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.9 Explicit validation in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc.ModelBinding;<\/b>\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n\n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n \n        public async Task&lt;IActionResult&gt; Index(long? id) {\n            return View(\"Form\", await context.Products\n                .OrderBy(p =&gt; p.ProductId)\n                .FirstOrDefaultAsync(p =&gt; id == null \n                    || p.ProductId == id));\n        }\n \n        [HttpPost]\n        public IActionResult SubmitForm(Product product) {\n                \n            <b class=\"fm-bold\">if (ModelState.GetValidationState(nameof(Product.Price))<\/b>\n                    <b class=\"fm-bold\">== ModelValidationState.Valid &amp;&amp; product.Price &lt;= 0) {<\/b>\n                <b class=\"fm-bold\">ModelState.AddModelError(nameof(Product.Price),<\/b>\n                    <b class=\"fm-bold\">\"Enter a positive price\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n                        \n            <b class=\"fm-bold\">if (ModelState.GetValidationState(nameof(Product.CategoryId))<\/b>\n                <b class=\"fm-bold\">== ModelValidationState.Valid &amp;&amp;<\/b> \n                        <b class=\"fm-bold\">!context.Categories.Any(c =&gt;<\/b>\n                            <b class=\"fm-bold\">c.CategoryId == product.CategoryId)) {<\/b>\n                <b class=\"fm-bold\">ModelState.AddModelError(nameof(Product.CategoryId),<\/b>\n                    <b class=\"fm-bold\">\"Enter an existing category ID\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n                        \n            <b class=\"fm-bold\">if (ModelState.GetValidationState(nameof(Product.SupplierId))<\/b>\n                    <b class=\"fm-bold\">== ModelValidationState.Valid &amp;&amp;<\/b> \n                        <b class=\"fm-bold\">!context.Suppliers.Any(s =&gt;<\/b>\n                            <b class=\"fm-bold\">s.SupplierId == product.SupplierId)) {<\/b>\n                <b class=\"fm-bold\">ModelState.AddModelError(nameof(Product.SupplierId),<\/b>\n                    <b class=\"fm-bold\">\"Enter an existing supplier ID\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n                        \n            if (ModelState.IsValid) {\n                TempData[\"name\"] = product.Name;\n                TempData[\"price\"] = product.Price.ToString();\n                TempData[\"categoryId\"] = product.CategoryId.ToString();\n                TempData[\"supplierId\"] = product.SupplierId.ToString();\n                return RedirectToAction(nameof(Results));\n            } else {\n                return View(\"Form\");\n            }\n        }\n \n        public IActionResult Results() {\n            return View(TempData);\n        }\n    }\n}<\/pre>\n<p class=\"body\">As an example of using the <code class=\"fm-code-in-text\">ModelStateDictionary<\/code>, consider how the <code class=\"fm-code-in-text\">Price<\/code> property is validated. One of the validation requirements for the <code class=\"fm-code-in-text\">Product<\/code> class is to ensure the user provides a positive value for the <code class=\"fm-code-in-text\">Price<\/code> property. This is something that ASP.NET Core cannot infer from the <code class=\"fm-code-in-text\">Product<\/code> class and so explicit validation is required.<\/p>\n<p class=\"body\">I start by ensuring that there are no existing validation errors for the <code class=\"fm-code-in-text\">Price<\/code> property:<\/p>\n<pre class=\"programlisting\">...\nif (<b class=\"fm-bold\">ModelState.GetValidationState(nameof(Product.Price))<\/b>\n        <b class=\"fm-bold\">== ModelValidationState.Valid<\/b> &amp;&amp; product.Price &lt;= 0) {\n    ModelState.AddModelError(nameof(Product.Price),\n        \"Enter a positive price\");\n}\n...<\/pre>\n<p class=\"body\">I want to make sure that the user provides a <code class=\"fm-code-in-text\">Price<\/code> value that is greater than zero, but there is no point in recording an error about zero or negative values if the user has provided a value that the model binder cannot convert into a <code class=\"fm-code-in-text\">decimal<\/code> value. I use the <code class=\"fm-code-in-text\">GetValidationState<\/code> method to determine the validation status of the <code class=\"fm-code-in-text\">Price<\/code> property before performing my own validation check:<\/p>\n<pre class=\"programlisting\">...\nif (ModelState.GetValidationState(nameof(Product.Price))\n        == ModelValidationState.Valid &amp;&amp; <b class=\"fm-bold\">product.Price &lt;= 0<\/b>) {\n    ModelState.AddModelError(nameof(Product.Price),\n        \"Enter a positive price\");\n}\n...<\/pre>\n<p class=\"body\">If the user has provided a value that is less than or equal to zero, then I use the <code class=\"fm-code-in-text\">AddModelError<\/code> method to record a validation error:<\/p>\n<pre class=\"programlisting\">...\nif (ModelState.GetValidationState(nameof(Product.Price))\n        == ModelValidationState.Valid &amp;&amp; product.Price &lt;= 0) {\n    <b class=\"fm-bold\">ModelState.AddModelError(nameof(Product.Price),<\/b>\n        <b class=\"fm-bold\">\"Enter a positive price\");<\/b>\n}\n...<\/pre>\n<p class=\"body\">The arguments to the <code class=\"fm-code-in-text\">AddModelError<\/code> method are the name of the property and a string that will be displayed to the user to describe the validation issue.<\/p>\n<p class=\"body\">For the <code class=\"fm-code-in-text\">CategoryId<\/code> and <code class=\"fm-code-in-text\">SupplierId<\/code> properties, I follow a similar process and use Entity Framework Core to ensure that the value the user has provided corresponds to an ID stored in the database.<\/p>\n<p class=\"body\">After performing the explicit validation checks, I use the <code class=\"fm-code-in-text\">ModelState.IsValid<\/code> property to see whether there were errors, which means that implicit or explicit validation errors will be reported in the same way.<\/p>\n<p class=\"body\">To see the effect of explicit validation, restart ASP.NET Core, request http:\/\/localhost:5000\/controllers\/form, and enter <b class=\"fm-bold\">0<\/b> into the <code class=\"fm-code-in-text\">Price<\/code>, <code class=\"fm-code-in-text\">CategoryId<\/code>, and <code class=\"fm-code-in-text\">SupplierId<\/code> fields. Submit the form, and you will see the validation errors shown in figure 29.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre309\" src=\"\/images\/proaspnetcore7\/000316.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.5 Explicit validation messages<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-553\">29.3.4 Configuring the default validation error messages<\/h3>\n<p class=\"body\">The validation process has some inconsistencies when it comes to the validation messages that are displayed. Not all the validation messages produced by the model binder are helpful to the user, which you can see by clearing the Price field and submitting the form. The empty field produces the following message:<a id=\"calibre_link-2548\"><\/a><a id=\"calibre_link-1071\"><\/a><\/p>\n<pre class=\"programlisting\">The value '' is invalid<\/pre>\n<p class=\"body\">This message is added to the <code class=\"fm-code-in-text\">ModelStateDictionary<\/code> by the implicit validation process when it can\u2019t find a value for a property. A missing value for a <code class=\"fm-code-in-text\">decimal<\/code> property, for example, causes a different&mdash;and less useful&mdash;message than a missing value for a <code class=\"fm-code-in-text\">string<\/code> property. This is because of differences in the way that the validation checks are performed. The default messages for some validation errors can be replaced with custom messages using the methods defined by the <code class=\"fm-code-in-text\">DefaultModelBindingMessageProvider<\/code> class, the most useful of which are described in table 29.6.<\/p>\n<p class=\"fm-table-caption\">Table 29.6 Useful DefaultModelBindingMessageProvider methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2549\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetValueMustNotBeNullAccessor<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The function assigned to this property is used to generate a validation error message when a value is <code class=\"fm-code-in-text1\">null<\/code> for a model property that is non-nullable.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetMissingBindRequiredValueAccessor<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The function assigned to this property is used to generate a validation error message when the request does not contain a value for a required property.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetMissingKeyOrValueAccessor<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The function assigned to this property is used to generate a validation error message when the data required for dictionary model object contains null keys or values.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetAttemptedValueIsInvalidAccessor<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The function assigned to this property is used to generate a validation error message when the model binding system cannot convert the data value into the required C# type.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetUnknownValueIsInvalidAccessor<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The function assigned to this property is used to generate a validation error message when the model binding system cannot convert the data value into the required C# type.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetValueMustBeANumberAccessor<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The function assigned to this property is used to generate a validation error message when the data value cannot be parsed into a C# numeric type.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SetValueIsInvalidAccessor<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The function assigned to this property is used to generate a fallback validation error message that is used as a last resort.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Each of the methods described in the table accepts a function that is invoked to get the validation message to display to the user. These methods are applied through the options pattern in the <code class=\"fm-code-in-text\">Program.cs<\/code> file, as shown in listing 29.10, in which I have replaced the default message that is displayed when a value is null or cannot be converted.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.10 Changing a validation message in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing Microsoft.AspNetCore.Antiforgery;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddSingleton&lt;CitiesData&gt;();\nbuilder.Services.Configure&lt;AntiforgeryOptions&gt;(opts =&gt; {\n    opts.HeaderName = \"X-XSRF-TOKEN\";\n});\n\n<b class=\"fm-bold\">builder.Services.Configure&lt;MvcOptions&gt;(opts =&gt;<\/b> \n    <b class=\"fm-bold\">opts.ModelBindingMessageProvider<\/b>\n    <b class=\"fm-bold\">.SetValueMustNotBeNullAccessor(value =&gt; \"Please enter a value\"));<\/b>\n        \nvar app = builder.Build();\n\napp.UseStaticFiles();\n\nIAntiforgery antiforgery \n    = app.Services.GetRequiredService&lt;IAntiforgery&gt;();\napp.Use(async (context, next) =&gt; {\n    if (!context.Request.Path.StartsWithSegments(\"\/api\")) {\n        string? token = \n            antiforgery.GetAndStoreTokens(context).RequestToken;\n        if (token != null) {\n            context.Response.Cookies.Append(\"XSRF-TOKEN\",\n               token,\n               new CookieOptions { HttpOnly = false });\n        }\n    }\n    await next();\n});\n\napp.MapControllerRoute(\"forms\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The function that you specify receives the value that the user has supplied, although that is not especially useful when dealing with <code class=\"fm-code-in-text\">null<\/code> values. To see the custom message, restart ASP.NET Core, use the browser to request http:\/\/localhost:5000\/controllers\/form, and submit the form with an empty Price field. The response will include the custom error message, as shown in figure 29.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre310\" src=\"\/images\/proaspnetcore7\/000317.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.6 Changing the default validation messages<\/p>\n<\/div>\n<p class=\"body\">Figure 29.6 also shows the message displayed for a missing <code class=\"fm-code-in-text\">Name<\/code> field, which isn\u2019t affected by the settings in table 29.6. This is a quirk of the way that non-nullable model properties are validated, which behaves as though the <code class=\"fm-code-in-text\">Required<\/code> attribute has been applied to the non-nullable property. I describe the <code class=\"fm-code-in-text\">Required<\/code> attribute later in this chapter and explain how it can be used to change the error message for non-nullable properties.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-554\">29.3.4 Displaying property-level validation messages<\/h3>\n<p class=\"body\">Although the custom error message is more meaningful than the default one, it still isn\u2019t that helpful because it doesn\u2019t clearly indicate which field the problem relates to. For this kind of error, it is more useful to display the validation error messages alongside the HTML elements that contain the problem data. This can be done using the <code class=\"fm-code-in-text\">ValidationMessageTag<\/code> tag helper, which looks for <code class=\"fm-code-in-text\">span<\/code> elements that have the <code class=\"fm-code-in-text\">asp-validation-for<\/code> attribute, which is used to specify the property for which error messages should be displayed.<\/p>\n<p class=\"body\">In listing 29.11, I have added property-level validation message elements for each of the <code class=\"fm-code-in-text\">input<\/code> elements in the form.<a id=\"calibre_link-2550\"><\/a><a id=\"calibre_link-1074\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.11 Property-level messages in the Form.cshtml file in the Views\/Form folder<\/p>\n<pre class=\"programlisting\">@model Product\n@{\n    Layout = \"_SimpleLayout\";\n}\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;partial name=\"_Validation\" \/&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    &lt;div asp-validation-summary=\"All\" class=\"text-danger\"&gt;&lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        <b class=\"fm-bold\">&lt;div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;span asp-validation-for=\"Name\" class=\"text-danger\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/span&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        <b class=\"fm-bold\">&lt;div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;span asp-validation-for=\"Price\" class=\"text-danger\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/span&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;CategoryId&lt;\/label&gt;\n        <b class=\"fm-bold\">&lt;div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;span asp-validation-for=\"CategoryId\" class=\"text-danger\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/span&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        &lt;input class=\"form-control\" asp-for=\"CategoryId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;SupplierId&lt;\/label&gt;\n        <b class=\"fm-bold\">&lt;div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;span asp-validation-for=\"SupplierId\" class=\"text-danger\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/span&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        &lt;input class=\"form-control\" asp-for=\"SupplierId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">Since <code class=\"fm-code-in-text\">span<\/code> elements are displayed inline, care must be taken to present the validation messages to make it obvious which element the message relates to. You can see the effect of the new validation messages by restarting ASP.NET Core, requesting http:\/\/localhost:5000\/controllers\/form, clearing the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Price<\/code> fields, and submitting the form. The response, shown in figure 29.7, includes validation messages alongside the text fields.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre311\" src=\"\/images\/proaspnetcore7\/000318.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.7 Displaying property-level validation messages<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-555\">29.3.5 Displaying model-level messages<\/h3>\n<p class=\"body\">It may seem that the validation summary message is superfluous because it duplicates the property-level messages. But the summary has a useful trick, which is the ability to display messages that apply to the entire model and not just individual properties. This means you can report errors that arise from a combination of individual properties, which would otherwise be hard to express with a property-level message.<\/p>\n<p class=\"body\">In listing 29.12, I have added a check to the <code class=\"fm-code-in-text\">FormController.SubmitForm<\/code> action that records a validation error when the <code class=\"fm-code-in-text\">Price<\/code> value exceeds <code class=\"fm-code-in-text\">100<\/code> at the time that the <code class=\"fm-code-in-text\">Name<\/code> value starts with <code class=\"fm-code-in-text\">Small<\/code>.<a id=\"calibre_link-2551\"><\/a><a id=\"calibre_link-1073\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.12 Model-level validation in the FormController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">...\n[HttpPost]\npublic IActionResult SubmitForm(Product product) {\n\n    if (ModelState.GetValidationState(nameof(Product.Price))\n            == ModelValidationState.Valid &amp;&amp; product.Price &lt;= 0) {\n        ModelState.AddModelError(nameof(Product.Price),\n            \"Enter a positive price\");\n    }\n        \n    <b class=\"fm-bold\">if (ModelState.GetValidationState(nameof(Product.Name))<\/b>\n            <b class=\"fm-bold\">== ModelValidationState.Valid<\/b>\n        <b class=\"fm-bold\">&amp;&amp; ModelState.GetValidationState(nameof(Product.Price))<\/b>\n            <b class=\"fm-bold\">== ModelValidationState.Valid<\/b>\n        <b class=\"fm-bold\">&amp;&amp; product.Name.ToLower().StartsWith(\"small\")<\/b> \n        <b class=\"fm-bold\">&amp;&amp; product.Price &gt; 100) {<\/b>\n            <b class=\"fm-bold\">ModelState.AddModelError(\"\",<\/b> \n                <b class=\"fm-bold\">\"Small products cannot cost more than $100\");<\/b>\n    <b class=\"fm-bold\">}<\/b>\n        \n    if (ModelState.GetValidationState(nameof(Product.CategoryId))\n        == ModelValidationState.Valid &amp;&amp; \n                !context.Categories.Any(c =&gt;\n                    c.CategoryId == product.CategoryId)) {\n        ModelState.AddModelError(nameof(Product.CategoryId),\n            \"Enter an existing category ID\");\n    }\n        \n    if (ModelState.GetValidationState(nameof(Product.SupplierId))\n            == ModelValidationState.Valid &amp;&amp; \n                !context.Suppliers.Any(s =&gt;\n                    s.SupplierId == product.SupplierId)) {\n        ModelState.AddModelError(nameof(Product.SupplierId),\n            \"Enter an existing supplier ID\");\n    }\n        \n    if (ModelState.IsValid) {\n        TempData[\"name\"] = product.Name;\n        TempData[\"price\"] = product.Price.ToString();\n        TempData[\"categoryId\"] = product.CategoryId.ToString();\n        TempData[\"supplierId\"] = product.SupplierId.ToString();\n        return RedirectToAction(nameof(Results));\n    } else {\n        return View(\"Form\");\n    }\n}\n...<\/pre>\n<p class=\"body\">If the user enters a <code class=\"fm-code-in-text\">Name<\/code> value that starts with <code class=\"fm-code-in-text\">Small<\/code> and a <code class=\"fm-code-in-text\">Price<\/code> value that is greater than <code class=\"fm-code-in-text\">100<\/code>, then a model-level validation error is recorded. I check for the combination of values only if there are no validation problems with the individual property values, which ensures the user doesn\u2019t see conflicting messages. Validation errors that relate to the entire model are recorded using the <code class=\"fm-code-in-text\">AddModelError<\/code> with the empty string as the first argument.<\/p>\n<p class=\"body\">Listing 29.13 changes the value of the <code class=\"fm-code-in-text\">asp-validation-summary<\/code> attribute to <code class=\"fm-code-in-text\">ModelOnly<\/code>, which excludes property-level errors, meaning that the summary will display only those errors that apply to the entire model.<a id=\"calibre_link-2552\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.13 Configuring the validation summary in the Views\/Form\/Form.cshtml file<\/p>\n<pre class=\"programlisting\">@model Product\n@{\n    Layout = \"_SimpleLayout\";\n}\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n\n&lt;partial name=\"_Validation\" \/&gt;\n\n&lt;form asp-action=\"submitform\" method=\"post\" id=\"htmlform\"&gt;\n    <b class=\"fm-bold\">&lt;div asp-validation-summary=\"ModelOnly\" class=\"text-danger\"&gt;&lt;\/div&gt;<\/b>\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Name\"&gt;&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"Name\" class=\"text-danger\"&gt;\n            &lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;input class=\"form-control\" asp-for=\"Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Price\"&gt;&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"Price\" class=\"text-danger\"&gt;\n            &lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;input class=\"form-control\" asp-for=\"Price\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;CategoryId&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"CategoryId\" class=\"text-danger\"&gt;\n            &lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;input class=\"form-control\" asp-for=\"CategoryId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;SupplierId&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"SupplierId\" class=\"text-danger\"&gt;\n            &lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;input class=\"form-control\" asp-for=\"SupplierId\" \/&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/controllers\/form. Enter Small Kayak into the Name field and 150 into the Price field and submit the form. The response will include the model-level error message, as shown in figure 29.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre312\" src=\"\/images\/proaspnetcore7\/000319.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.8 Displaying a model-level validation message<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-556\">29.4 Explicitly validating data in a Razor Page<\/h2>\n<p class=\"body\">Razor Page validation relies on the same features used in the controller in the previous section. Listing 29.14 adds explicit validation checks and error summaries to the <code class=\"fm-code-in-text\">FormHandler<\/code> page.<a id=\"calibre_link-2553\"><\/a><a id=\"calibre_link-2554\"><\/a><a id=\"calibre_link-1065\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.14 Validating data in the FormHandler.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/form\/{id:long?}\"\n@model FormHandlerModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using Microsoft.EntityFrameworkCore\n@using Microsoft.AspNetCore.Mvc.ModelBinding\n\n&lt;partial name=\"_Validation\" \/&gt;\n\n&lt;div class=\"m-2\"&gt;\n    &lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;HTML Form&lt;\/h5&gt;\n    &lt;form asp-page=\"FormHandler\" method=\"post\" id=\"htmlform\"&gt;\n        &lt;div asp-validation-summary=\"ModelOnly\" class=\"text-danger\"&gt;&lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Name&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;div&gt;<\/b>\n                <b class=\"fm-bold\">&lt;span asp-validation-for=\"Product.Name\"<\/b> \n                    <b class=\"fm-bold\">class=\"text-danger\"&gt;&lt;\/span&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n            &lt;input class=\"form-control\" asp-for=\"Product.Name\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Price&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;div&gt;<\/b>\n                <b class=\"fm-bold\">&lt;span asp-validation-for=\"Product.Price\"<\/b> \n                    <b class=\"fm-bold\">class=\"text-danger\"&gt;&lt;\/span&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n            &lt;input class=\"form-control\" asp-for=\"Product.Price\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;CategoryId&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;div&gt;<\/b>\n                <b class=\"fm-bold\">&lt;span asp-validation-for=\"Product.CategoryId\"<\/b> \n                    <b class=\"fm-bold\">class=\"text-danger\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/span&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n            &lt;input class=\"form-control\" asp-for=\"Product.CategoryId\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;SupplierId&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;div&gt;<\/b>\n                <b class=\"fm-bold\">&lt;span asp-validation-for=\"Product.SupplierId\"<\/b> \n                    <b class=\"fm-bold\">class=\"text-danger\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/span&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n            &lt;input class=\"form-control\" asp-for=\"Product.SupplierId\" \/&gt;\n        &lt;\/div&gt;\n        &lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;Submit&lt;\/button&gt;\n    &lt;\/form&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class FormHandlerModel : PageModel {\n        private DataContext context;\n\n        public FormHandlerModel(DataContext dbContext) {\n            context = dbContext;\n        }\n\n        [BindProperty]\n        public Product Product { get; set; } \n            = new() { Name = string.Empty };\n                        \n        <b class=\"fm-bold\">\/\/[BindProperty(Name = \"Product.Category\")]<\/b>\n        <b class=\"fm-bold\">\/\/public Category Category { get; set; } = new();<\/b>\n        <b class=\"fm-bold\">public async Task OnGetAsync(long id = 1) {<\/b>\n            <b class=\"fm-bold\">Product = await context.Products<\/b>\n                <b class=\"fm-bold\">.OrderBy(p =&gt; p.ProductId)<\/b>\n                <b class=\"fm-bold\">.FirstAsync(p =&gt; p.ProductId == id);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        public IActionResult OnPost() {\n            <b class=\"fm-bold\">if (ModelState.GetValidationState(\"Product.Price\")<\/b>\n                    <b class=\"fm-bold\">== ModelValidationState.Valid &amp;&amp; Product.Price &lt; 1) {<\/b>\n                <b class=\"fm-bold\">ModelState.AddModelError(\"Product.Price\",<\/b> \n                    <b class=\"fm-bold\">\"Enter a positive price\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n                        \n            <b class=\"fm-bold\">if (ModelState.GetValidationState(\"Product.Name\")<\/b>\n                    <b class=\"fm-bold\">== ModelValidationState.Valid<\/b>\n                <b class=\"fm-bold\">&amp;&amp; ModelState.GetValidationState(\"Product.Price\")<\/b>\n                    <b class=\"fm-bold\">== ModelValidationState.Valid<\/b>\n                <b class=\"fm-bold\">&amp;&amp; Product.Name.ToLower().StartsWith(\"small\")<\/b>\n                <b class=\"fm-bold\">&amp;&amp; Product.Price &gt; 100) {<\/b>\n                <b class=\"fm-bold\">ModelState.AddModelError(\"\",<\/b>\n                    <b class=\"fm-bold\">\"Small products cannot cost more than $100\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n                        \n            <b class=\"fm-bold\">if (ModelState.GetValidationState(\"Product.CategoryId\")<\/b>\n                    <b class=\"fm-bold\">== ModelValidationState.Valid &amp;&amp;<\/b>\n                <b class=\"fm-bold\">!context.Categories<\/b>\n                    <b class=\"fm-bold\">.Any(c =&gt; c.CategoryId == Product.CategoryId)) {<\/b>\n                        <b class=\"fm-bold\">ModelState.AddModelError(\"Product.CategoryId\",<\/b>\n                            <b class=\"fm-bold\">\"Enter an existing category ID\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n                        \n            <b class=\"fm-bold\">if (ModelState.GetValidationState(\"Product.SupplierId\")<\/b>\n                    <b class=\"fm-bold\">== ModelValidationState.Valid &amp;&amp;<\/b>\n                <b class=\"fm-bold\">!context.Suppliers<\/b>\n                    <b class=\"fm-bold\">.Any(s =&gt; s.SupplierId == Product.SupplierId)) {<\/b>\n                        <b class=\"fm-bold\">ModelState.AddModelError(\"Product.SupplierId\",<\/b>\n                            <b class=\"fm-bold\">\"Enter an existing supplier ID\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n                        \n            <b class=\"fm-bold\">if (ModelState.IsValid) {<\/b>\n                <b class=\"fm-bold\">TempData[\"name\"] = Product.Name;<\/b>\n                <b class=\"fm-bold\">TempData[\"price\"] = Product.Price.ToString();<\/b>\n                <b class=\"fm-bold\">TempData[\"categoryId\"] = Product.CategoryId.ToString();<\/b>\n                <b class=\"fm-bold\">TempData[\"supplierId\"] = Product.SupplierId.ToString();<\/b>\n                <b class=\"fm-bold\">return RedirectToPage(\"FormResults\");<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return Page();<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">PageModel<\/code> class defines a <code class=\"fm-code-in-text\">ModelState<\/code> property that is the equivalent of the one I used in the controller and allows validation errors to be recorded. The process for validation is the same, but you must take care when recording errors to ensure the names match the pattern used by Razor Pages. When I recorded an error in the controller, I used the <code class=\"fm-code-in-text\">nameof<\/code> keyword to select the property to which the error relates, like this:<\/p>\n<pre class=\"programlisting\">...\nModelState.AddModelError(<b class=\"fm-bold\">nameof(Product.Price)<\/b>,\"Enter a positive price\"); \n...<\/pre>\n<p class=\"body\">This is a common convention because it ensures that a typo won\u2019t cause errors to be recorded incorrectly. This expression won\u2019t work in the Razor Page, where the error must be recorded against <code class=\"fm-code-in-text\">Product.Price<\/code>, rather than <code class=\"fm-code-in-text\">Price<\/code>, to reflect that <code class=\"fm-code-in-text\">@Model<\/code> expressions in Razor Pages return the page model object, like this:<\/p>\n<pre class=\"programlisting\">...\nModelState.AddModelError(<b class=\"fm-bold\">\"Product.Price\"<\/b>, \"Enter a positive price\");\n...<\/pre>\n<p class=\"body\">To test the validation process, restart ASP.NET Core, use a browser to request http:\/\/localhost:5000\/pages\/form, and submit the form with empty fields or with values that cannot be converted into the C# types required by the <code class=\"fm-code-in-text\">Product<\/code> class. The error messages are displayed just as they are for controllers, as shown in figure 29.9. (The values 1, 2, and 3 are valid for both the <code class=\"fm-code-in-text\">CategoryId<\/code> and <code class=\"fm-code-in-text\">SupplierId<\/code> fields.)<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The methods described in table 29.6 that change the default validation messages affect Razor Pages as well as controllers.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre313\" src=\"\/images\/proaspnetcore7\/000320.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.9 Validating data in a Razor Page<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-557\">29.5 Specifying validation rules using metadata<\/h2>\n<p class=\"body\">One problem with putting validation logic into an action method is that it ends up being duplicated in every action or handler method that receives data from the user. To help reduce duplication, the validation process supports the use of attributes to express model validation rules directly in the model class, ensuring that the same set of validation rules will be applied regardless of which action method is used to process a request. In listing 29.15, I have applied attributes to the <code class=\"fm-code-in-text\">Product<\/code> class to describe the validation required for the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Price<\/code> properties.<a id=\"calibre_link-2555\"><\/a><a id=\"calibre_link-2556\"><\/a><a id=\"calibre_link-1063\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.15 Applying validation attributes in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Text.Json.Serialization;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\n\nnamespace WebApp.Models {\n    public class Product {\n        \n        public long ProductId { get; set; }\n        <b class=\"fm-bold\">[Required(ErrorMessage = \"Please enter a name\")]<\/b>\n        public required string Name { get; set; }\n                \n        <b class=\"fm-bold\">[Range(1, 999999, ErrorMessage = \"Please enter a positive price\")]<\/b>\n        [Column(TypeName = \"decimal(8, 2)\")]\n        public decimal Price { get; set; }\n                \n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        public long SupplierId { get; set; }\n                \n        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">I used two validation attributes in the listing: <code class=\"fm-code-in-text\">Required<\/code> and <code class=\"fm-code-in-text\">Range<\/code>. The <code class=\"fm-code-in-text\">Required<\/code> attribute specifies that it is a validation error if the user doesn\u2019t submit a value for a property, which is useful when you have a nullable property but want to require a value from the user.<\/p>\n<p class=\"body\">I used the <code class=\"fm-code-in-text\">Required<\/code> attribute in listing 29.15 to change the error message that is displayed when the user doesn\u2019t provide a value for the <code class=\"fm-code-in-text\">Name<\/code> property. As noted earlier, the implicit validation checks are inconsistent in the way that non-nullable properties are processed, but this can be corrected using the <code class=\"fm-code-in-text\">ErrorMessage<\/code> argument that all of the validation attributes define.<\/p>\n<p class=\"body\">I also applied the <code class=\"fm-code-in-text\">Range<\/code> attribute in listing 29.15, which allows me to specify the set of acceptable values for the <code class=\"fm-code-in-text\">Price<\/code> property. Table 29.7 shows the set of built-in validation attributes available.<a id=\"calibre_link-2557\"><\/a><a id=\"calibre_link-2558\"><\/a><a id=\"calibre_link-2559\"><\/a><a id=\"calibre_link-2560\"><\/a><a id=\"calibre_link-2561\"><\/a><a id=\"calibre_link-1068\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 29.7 The Built-in Validation attributes<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2562\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Attribute<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Example<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Compare<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[Compare<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">(\"OtherProperty\")]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute ensures that properties must have the same value, which is useful when you ask the user to provide the same information twice, such as an e-mail address or a password.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Range<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[Range(10, 20)]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute ensures that a numeric value (or any property type that implements <code class=\"fm-code-in-text1\">IComparable<\/code>) is not outside the range of specified minimum and maximum values. To specify a boundary on only one side, use a <code class=\"fm-code-in-text1\">MinValue<\/code> or <code class=\"fm-code-in-text1\">MaxValue<\/code> constant.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RegularExpression<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[RegularExpression<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">&nbsp;&nbsp; (\"pattern\")]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute ensures that a string value matches the specified regular expression pattern. Note that the pattern must match the <i class=\"fm-italics\">entire<\/i> user-supplied value, not just a substring within it. By default, it matches case sensitively, but you can make it case insensitive by applying the <code class=\"fm-code-in-text1\">(?i)<\/code> modifier&mdash;that is, <code class=\"fm-code-in-text1\">[RegularExpression(\"(?i)mypattern\")]<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Required<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[Required]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute ensures that the value is not empty or a string consisting only of spaces. If you want to treat whitespace as valid, use <code class=\"fm-code-in-text1\">[Required(AllowEmptyStrings = true)]<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">StringLength<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">[StringLength(10)]<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute ensures that a string value is no longer than a specified maximum length. You can also specify a minimum length: <code class=\"fm-code-in-text1\">[StringLength(10, MinimumLength=2)]<\/code>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The use of the validation attribute allows me to remove some of the explicit validation from the action method, as shown in listing 29.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.16 Removing explicit validation in the Controllers\/FormController.cs file<\/p>\n<pre class=\"programlisting\">...\n[HttpPost]\npublic IActionResult SubmitForm(Product product) {\n\n    <b class=\"fm-bold\">\/\/if (ModelState.GetValidationState(nameof(Product.Price))<\/b>\n    <b class=\"fm-bold\">\/\/        == ModelValidationState.Valid &amp;&amp; product.Price &lt;= 0) {<\/b>\n    <b class=\"fm-bold\">\/\/    ModelState.AddModelError(nameof(Product.Price),<\/b>\n    <b class=\"fm-bold\">\/\/        \"Enter a positive price\");<\/b>\n    <b class=\"fm-bold\">\/\/}<\/b>\n        \n    if (ModelState.GetValidationState(nameof(Product.Name))\n            == ModelValidationState.Valid\n        &amp;&amp; ModelState.GetValidationState(nameof(Product.Price))\n            == ModelValidationState.Valid\n        &amp;&amp; product.Name.ToLower().StartsWith(\"small\") \n        &amp;&amp; product.Price &gt; 100) {\n            ModelState.AddModelError(\"\", \n                \"Small products cannot cost more than $100\");\n    }\n \n    if (ModelState.GetValidationState(nameof(Product.CategoryId))\n        == ModelValidationState.Valid &amp;&amp; \n                !context.Categories.Any(c =&gt;\n                    c.CategoryId == product.CategoryId)) {\n        ModelState.AddModelError(nameof(Product.CategoryId),\n            \"Enter an existing category ID\");\n    }\n        \n    if (ModelState.GetValidationState(nameof(Product.SupplierId))\n            == ModelValidationState.Valid &amp;&amp; \n                !context.Suppliers.Any(s =&gt;\n                    s.SupplierId == product.SupplierId)) {\n        ModelState.AddModelError(nameof(Product.SupplierId),\n            \"Enter an existing supplier ID\");\n    }\n \n    if (ModelState.IsValid) {\n        TempData[\"name\"] = product.Name;\n        TempData[\"price\"] = product.Price.ToString();\n        TempData[\"categoryId\"] = product.CategoryId.ToString();\n        TempData[\"supplierId\"] = product.SupplierId.ToString();\n        return RedirectToAction(nameof(Results));\n    } else {\n        return View(\"Form\");\n    }\n}\n...<\/pre>\n<p class=\"body\">To see the validation attributes in action, restart ASP.NET Core MVC, request http:\/\/localhost:5000\/controllers\/form, clear the Name and Price fields, and submit the form. The response will include the validation errors produced by the attributes for the Price field and the new message for the Name field, as shown in figure 29.10. The validation attributes are applied before the action method is called, which means that I can still rely on the model state to determine whether individual properties are valid when performing model-level validation.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Validation work arounds<\/p>\n<p class=\"fm-sidebar-text\">Getting the validation results you require can take some care when using the validation attributes. For example, you cannot use the <code class=\"fm-code-in-text1\">Required<\/code> attribute if you want to ensure that a user has checked a checkbox because the browser will send a <code class=\"fm-code-in-text1\">false<\/code> value when the checkbox is unchecked, which will always pass the checks applied by the <code class=\"fm-code-in-text1\">Required<\/code> attribute. Instead, use the <code class=\"fm-code-in-text1\">Range<\/code> attribute and specify the minimum and maximum values as <code class=\"fm-code-in-text1\">true<\/code>, like this:<a id=\"calibre_link-2563\"><\/a><a id=\"calibre_link-1067\"><\/a><\/p>\n<pre class=\"programlisting\">...\n[Range(typeof(bool), \"true\", \"true\",\n   ErrorMessage=\"You must check the box\")]\n...<\/pre>\n<p class=\"fm-sidebar-text\">If this sort of workaround feels uncomfortable, then you can create custom validation attributes, as described in the next section.<\/p>\n<\/div>\n<p class=\"body\">To see the validation attributes in action, restart ASP.NET Core MVC, request http:\/\/localhost:5000\/controllers\/form, clear the Name and Price fields, and submit the form. The response will include the validation errors produced by the attributes, as shown in figure 29.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre314\" src=\"\/images\/proaspnetcore7\/000321.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.10 Using validation attributes<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding web service controller validation<\/p>\n<p class=\"fm-sidebar-text\">Controllers that have been decorated with the <code class=\"fm-code-in-text1\">ApiController<\/code> attribute do not need to check the <code class=\"fm-code-in-text1\">ModelState.IsValid<\/code> property. Instead, the action method is invoked only if there are no validation errors, which means you can always rely on receiving validated objects through the model binding feature. If any validation errors are detected, then the request is terminated, and an error response is sent to the browser.<a id=\"calibre_link-2564\"><\/a><a id=\"calibre_link-2565\"><\/a><\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-558\">29.5.1 Creating a custom property validation attribute<\/h3>\n<p class=\"body\">The validation process can be extended by creating an attribute that extends the <code class=\"fm-code-in-text\">ValidationAttribute<\/code> class. To demonstrate, I created the <code class=\"fm-code-in-text\">WebApp\/Validation<\/code> folder and added to it a class file named <code class=\"fm-code-in-text\">PrimaryKeyAttribute.cs<\/code>, which I used to define the class shown in listing 29.17.<a id=\"calibre_link-2566\"><\/a><a id=\"calibre_link-1069\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.17 The contents of the PrimaryKeyAttribute.cs file in the Validation folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing System.ComponentModel.DataAnnotations;\n\nnamespace WebApp.Validation {\n    public class PrimaryKeyAttribute : ValidationAttribute {\n        \n        public Type? ContextType { get; set; }\n                \n        public Type? DataType { get; set; }\n                \n        protected override ValidationResult? IsValid(object? value,\n                ValidationContext validationContext) {\n            if (ContextType != null &amp;&amp; DataType != null) {\n                DbContext? context =\n                    validationContext.GetRequiredService(ContextType) \n                        as DbContext;\n                if (context != null \n                        &amp;&amp; context.Find(DataType, value) == null) {\n                    return new ValidationResult(ErrorMessage ??\n                        \"Enter an existing key value\");\n                }\n            }\n            return ValidationResult.Success;\n        }\n    }\n}<\/pre>\n<p class=\"body\">Custom attributes override the <code class=\"fm-code-in-text\">IsValid<\/code> method, which is called with the value to check, and a <code class=\"fm-code-in-text\">ValidationContext<\/code> object that provides context about the validation process and provides access to the application\u2019s services through its <code class=\"fm-code-in-text\">GetService<\/code> method.<\/p>\n<p class=\"body\">In listing 29.17, the custom attribute receives the type of an Entity Framework Core database context class and the type of a model class. In the <code class=\"fm-code-in-text\">IsValid<\/code> method, the attribute obtains an instance of the context class and uses it to query the database to determine whether the value has been used as a primary key value.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Revalidating data<\/p>\n<p class=\"fm-sidebar-text\">You may need to perform the validation process again if you modify the object received from the model binder. For these situations, use the <code class=\"fm-code-in-text1\">ModelState.Clear<\/code> method to clear any existing validation errors and call the <code class=\"fm-code-in-text1\">TryValidateModel<\/code> method.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-559\">29.5.2 Creating a custom model validation attribute<\/h3>\n<p class=\"body\">Custom validation attributes can also be used to perform model-level validation. To demonstrate, I added a class file named <code class=\"fm-code-in-text\">PhraseAndPriceAttribute.cs<\/code> to the <code class=\"fm-code-in-text\">Validation<\/code> folder and used it to define the class shown in listing 29.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.18 The contents of the PhraseAndPriceAttribute.cs file in the Validation folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing WebApp.Models;\n\nnamespace WebApp.Validation {\n    public class PhraseAndPriceAttribute : ValidationAttribute {\n        \n        public string? Phrase { get; set; }\n                \n        public string? Price { get; set; }\n                \n        protected override ValidationResult? IsValid(object? value,\n                ValidationContext validationContext) {\n                                \n            if (value != null &amp;&amp; Phrase != null &amp;&amp; Price != null) {\n                Product? product = value as Product;\n                if (product != null\n                    &amp;&amp; product.Name.StartsWith(Phrase,\n                        StringComparison.OrdinalIgnoreCase)\n                    &amp;&amp; product.Price &gt; decimal.Parse(Price)) {\n                        return new ValidationResult(ErrorMessage ?? \n                            $\"{Phrase} products cannot cost more than $\" \n                            + Price);\n                }\n            }\n            return ValidationResult.Success;\n        }\n    }\n}<\/pre>\n<p class=\"body\">This attribute is configured with <code class=\"fm-code-in-text\">Phrase<\/code> and <code class=\"fm-code-in-text\">Price<\/code> properties, which are used in the <code class=\"fm-code-in-text\">IsValid<\/code> method to check the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">Price<\/code> properties of the model object. Property-level custom validation attributes are applied directly to the properties they validate, and model-level attributes are applied to the entire class, as shown in listing 29.19.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.19 Applying validation attributes in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Text.Json.Serialization;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\n<b class=\"fm-bold\">using WebApp.Validation;<\/b>\n\nnamespace WebApp.Models {\n\n    <b class=\"fm-bold\">[PhraseAndPrice(Phrase = \"Small\", Price = \"100\")]<\/b>\n    public class Product {\n        \n        public long ProductId { get; set; }\n                \n        [Required(ErrorMessage = \"Please enter a name\")]\n        public required string Name { get; set; }\n                \n        [Range(1, 999999, ErrorMessage = \"Please enter a positive price\")]\n        [Column(TypeName = \"decimal(8, 2)\")]\n        public decimal Price { get; set; }\n                \n        <b class=\"fm-bold\">[PrimaryKey(ContextType = typeof(DataContext),<\/b> \n            <b class=\"fm-bold\">DataType = typeof(Category))]<\/b>\n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        <b class=\"fm-bold\">[PrimaryKey(ContextType = typeof(DataContext),<\/b> \n            <b class=\"fm-bold\">DataType = typeof(Supplier))]<\/b>\n        public long SupplierId { get; set; }\n                \n        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">The custom attributes allow the remaining explicit validation statements to be removed from the <code class=\"fm-code-in-text\">Form<\/code> controller\u2019s action method, as shown in listing 29.20.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.20 Removing explicit validation in the Controllers\/FormController.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class FormController : Controller {\n        private DataContext context;\n\n        public FormController(DataContext dbContext) {\n            context = dbContext;\n        }\n \n        public async Task&lt;IActionResult&gt; Index(long? id) {\n            return View(\"Form\", await context.Products\n                .OrderBy(p =&gt; p.ProductId)\n                .FirstOrDefaultAsync(p =&gt; id == null \n                    || p.ProductId == id));\n        }\n \n        [HttpPost]\n        public IActionResult SubmitForm(Product product) {\n            <b class=\"fm-bold\">if (ModelState.IsValid) {<\/b>\n                <b class=\"fm-bold\">TempData[\"name\"] = product.Name;<\/b>\n                <b class=\"fm-bold\">TempData[\"price\"] = product.Price.ToString();<\/b>\n                <b class=\"fm-bold\">TempData[\"categoryId\"] = product.CategoryId.ToString();<\/b>\n                <b class=\"fm-bold\">TempData[\"supplierId\"] = product.SupplierId.ToString();<\/b>\n                <b class=\"fm-bold\">return RedirectToAction(nameof(Results));<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return View(\"Form\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        }\n \n        public IActionResult Results() {\n            return View(TempData);\n        }\n    }\n}<\/pre>\n<p class=\"body\">The validation attributes are applied automatically before the action method is invoked, which means that the validation outcome can be determined simply by reading the <code class=\"fm-code-in-text\">ModelState.IsValid<\/code> property.<\/p>\n<p class=\"fm-head2\">Using a custom model validation attribute in a Razor Page<\/p>\n<p class=\"body\">An adaptation is required to support custom model validation attributes in Razor Pages. When the validation attribute is applied in a Razor Page, the errors it generates are associated with the <code class=\"fm-code-in-text\">Product<\/code> property, rather than with the entire model, which means that the errors are not displayed by the validation summary tag helper.<\/p>\n<p class=\"body\">To resolve this issue, add a class file named <code class=\"fm-code-in-text\">ModelStateExtensions.cs<\/code> to the <code class=\"fm-code-in-text\">WebApp\/Validation<\/code> folder and use it to define the extension method shown in listing 29.21.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.21 The contents of the ModelStateExtensions.cs file in the Validation folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.ModelBinding;\n\nnamespace WebApp.Validation {\n\n    public static class ModelStateExtensions {\n        \n        public static void PromotePropertyErrors(\n                this ModelStateDictionary modelState,\n                string propertyName) {\n            foreach (var err in modelState) {\n                if (err.Key == propertyName &amp;&amp; err.Value.ValidationState\n                        == ModelValidationState.Invalid) {\n                    foreach (var e in err.Value.Errors) {\n                        modelState.AddModelError(string.Empty, \n                            e.ErrorMessage);\n                    }\n                }\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">PromotePropertyErrors<\/code> extension method locates validation errors associated with a specified property and adds corresponding model-level errors. Listing 29.22 removes the explicit validation from the Razor Page and applies the new extension method.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.22 Removing explicit validation in the Pages\/FormHandler.cshtml file<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/form\/{id:long?}\"\n@model FormHandlerModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using Microsoft.EntityFrameworkCore\n@using Microsoft.AspNetCore.Mvc.ModelBinding\n<b class=\"fm-bold\">@using WebApp.Validation<\/b>\n\n&lt;partial name=\"_Validation\" \/&gt;\n\n&lt;div class=\"m-2\"&gt;\n\n    &lt;!-- ...markup omitted for brevity... --&gt;\n        \n&lt;\/div&gt;\n\n@functions {\n\n    public class FormHandlerModel : PageModel {\n        private DataContext context;\n\n        public FormHandlerModel(DataContext dbContext) {\n            context = dbContext;\n        }\n                \n        [BindProperty]\n        public Product Product { get; set; } \n            = new() { Name = string.Empty };\n                        \n        public async Task OnGetAsync(long id = 1) {\n            Product = await context.Products\n                .OrderBy(p =&gt; p.ProductId)\n                .FirstAsync(p =&gt; p.ProductId == id);\n        }\n \n        public IActionResult OnPost() {\n            <b class=\"fm-bold\">if (ModelState.IsValid) {<\/b>\n                <b class=\"fm-bold\">TempData[\"name\"] = Product.Name;<\/b>\n                <b class=\"fm-bold\">TempData[\"price\"] = Product.Price.ToString();<\/b>\n                <b class=\"fm-bold\">TempData[\"categoryId\"] = Product.CategoryId.ToString();<\/b>\n                <b class=\"fm-bold\">TempData[\"supplierId\"] = Product.SupplierId.ToString();<\/b>\n                <b class=\"fm-bold\">return RedirectToPage(\"FormResults\");<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">ModelState.PromotePropertyErrors(nameof(Product));<\/b>\n                <b class=\"fm-bold\">return Page();<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">Expressing the validation through the custom attributes removes the code duplication between the controller and the Razor Page and ensures that validation is applied consistently wherever model binding is used for <code class=\"fm-code-in-text\">Product<\/code> objects. To test the validation attributes, restart ASP.NET Core and navigate to http:\/\/localhost:5000\/controllers\/form or http:\/\/localhost:5000\/pages\/form. Clear the form fields or enter bad key values and submit the form, and you will see the error messages produced by the attributes, some of which are shown in figure 29.11. (The values 1, 2, and 3 are valid for both the CategoryId and SupplierId fields.)<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre315\" src=\"\/images\/proaspnetcore7\/000322.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.11 Using custom validation attributes<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-560\">29.6 Performing client-side validation<\/h2>\n<p class=\"body\">The validation techniques I have demonstrated so far have all been examples of <i class=\"fm-italics\">server-side validation<\/i>. This means the user submits their data to the server, and the server validates the data and sends back the results of the validation (either success in processing the data or a list of errors that need to be corrected).<\/p>\n<p class=\"body\">In web applications, users typically expect immediate validation feedback&mdash;without having to submit anything to the server. This is known as <i class=\"fm-italics\">client-side validation<\/i> and is implemented using JavaScript. The data that the user has entered is validated before being sent to the server, providing the user with immediate feedback and an opportunity to correct any problems.<\/p>\n<p class=\"body\">ASP.NET Core supports <i class=\"fm-italics\">unobtrusive client-side validation<\/i>. The term <i class=\"fm-italics\">unobtrusive<\/i> means that validation rules are expressed using attributes added to the HTML elements that views generate. These attributes are interpreted by a JavaScript library distributed by Microsoft that, in turn, configures the jQuery Validation library, which does the actual validation work. In the following sections, I will show you how the built-in validation support works and demonstrate how I can extend the functionality to provide custom client-side validation.<\/p>\n<p class=\"body\">The first step is to install the JavaScript packages that deal with validation. Open a new PowerShell command prompt, navigate to the <code class=\"fm-code-in-text\">WebApp<\/code> project folder, and run the command shown in listing 29.23.<a id=\"calibre_link-2567\"><\/a><a id=\"calibre_link-2568\"><\/a><a id=\"calibre_link-1012\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The core jQuery command was added to the project in chapter 26. Run the following command if you need to install it again: <code class=\"fm-code-in-text1\">libman install jquery@3.6.3 -d wwwroot\/lib\/jquery<\/code>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.23 Installing the validation packa<a id=\"calibre_link-2569\"><\/a>ges<\/p>\n<pre class=\"programlisting\">libman install jquery-validate@1.19.5 -d wwwroot\/lib\/jquery-validate\nlibman install jquery-validation-unobtrusive@4.0.0\n    -d wwwroot\/lib\/jquery-validation-unobtrusive<\/pre>\n<p class=\"body\">Once the packages are installed, add the elements shown in listing 29.24 to the <code class=\"fm-code-in-text\">_Validation.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder, which provides a convenient way to introduce the validation alongside the existing jQuery code in the application.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The elements must be defined in the order in which they are shown.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.24 Adding elements in the _Validation.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;script type=\"text\/javascript\"&gt;\n    window.addEventListener(\"DOMContentLoaded\", () =&gt; {\n        document.querySelectorAll(\"input.input-validation-error\")\n            .forEach((elem) =&gt; { elem.classList.add(\"is-invalid\"); }\n        );\n    });\n&lt;\/script&gt;\n<b class=\"fm-bold\">&lt;script src=\"\/lib\/jquery\/jquery.min.js\"&gt;&lt;\/script&gt;<\/b>\n<b class=\"fm-bold\">&lt;script src=\"\/lib\/jquery-validate\/jquery.validate.min.js\"&gt;&lt;\/script&gt;<\/b>\n<b class=\"fm-bold\">&lt;script src=<\/b>\n  <b class=\"fm-bold\">\"\/lib\/jquery-validation-unobtrusive\/jquery.validate.unobtrusive.min.js\"&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/script&gt;<\/b><\/pre>\n<p class=\"body\">The ASP.NET Core form tag helpers add <code class=\"fm-code-in-text\">data-val*<\/code> attributes to <code class=\"fm-code-in-text\">input<\/code> elements that describe validation constraints for fields. Here are the attributes added to the <code class=\"fm-code-in-text\">input<\/code> element for the <code class=\"fm-code-in-text\">Name<\/code> field, for example:<\/p>\n<pre class=\"programlisting\">...\n&lt;input class=\"form-control input-validation-error is-invalid\" type=\"text\" \n  <b class=\"fm-bold\">data-val=\"true\" data-val-required=\"Please enter a value\"<\/b> id=\"Name\" \n  name=\"Name\" value=\"\"&gt;\n...<\/pre>\n<p class=\"body\">The unobtrusive validation JavaScript code looks for these attributes and performs validation in the browser when the user attempts to submit the form. The form won\u2019t be submitted, and an error will be displayed if there are validation problems. The data won\u2019t be sent to the application until there are no outstanding validation issues.<\/p>\n<p class=\"body\">The JavaScript code looks for elements with the <code class=\"fm-code-in-text\">data-val<\/code> attribute and performs local validation in the browser when the user submits the form, without sending an HTTP request to the server. You can see the effect by running the application and submitting the form while using the F12 tools to note that validation error messages are displayed even though no HTTP request is sent to the server.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Avoiding conflicts with browser validation<\/p>\n<p class=\"fm-sidebar-text\">Some of the current generation of browsers support simple client-side validation based on the attributes applied to <code class=\"fm-code-in-text1\">input<\/code> elements. The general idea is that, say, an <code class=\"fm-code-in-text1\">input<\/code> element to which the <code class=\"fm-code-in-text1\">required<\/code> attribute has been applied, for example, will cause the browser to display a validation error when the user tries to submit the form without providing a value.<\/p>\n<p class=\"fm-sidebar-text\">If you are generating form elements using tag helpers, as I have been doing in this chapter, then you won\u2019t have any problems with browser validation because the elements that are assigned <code class=\"fm-code-in-text1\">data<\/code> attributes are ignored by the browser.<\/p>\n<p class=\"fm-sidebar-text\">However, you may run into problems if you are unable to completely control the markup in your application, something that often happens when you are passing on content generated elsewhere. The result is that the jQuery validation and the browser validation can both operate on the form, which is just confusing to the user. To avoid this problem, ASP.NET Core adds the <code class=\"fm-code-in-text1\">novalidate<\/code> attribute to the <code class=\"fm-code-in-text1\">form<\/code> element to disable browser validation.<\/p>\n<\/div>\n<p class=\"body\">One of the nice client-side validation features is that the same attributes that specify validation rules are applied at the client <i class=\"fm-italics\">and<\/i> at the server. This means that data from browsers that do not support JavaScript are subject to the same validation as those that do, without requiring any additional effort.<\/p>\n<p class=\"body\">To test the client-side validation feature, restart ASP.NET Core, request http:\/\/localhost:5000\/controllers\/form or http:\/\/localhost:5000\/pages\/form, clear the Name field, and click the Submit button.<\/p>\n<p class=\"body\">The error message looks like the ones generated by server-side validation, but if you enter text into the field, you will see the error message disappear immediately as the JavaScript code responds to the user interaction, as shown in figure 29.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre316\" src=\"\/images\/proaspnetcore7\/000323.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.12 Performing client-side validation<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Extending client-side validation<\/p>\n<p class=\"fm-sidebar-text\">The client-side validation feature supports the built-in property-level attributes. The feature can be extended but requires fluency in JavaScript and requires working directly with the jQuery Validation package. See <a class=\"url\" href=\"https:\/\/jqueryvalidation.org\/documentation\">https:\/\/jqueryvalidation.org\/documentation<\/a> for details.<a id=\"calibre_link-2570\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">If you don\u2019t want to start writing JavaScript code, then you can follow the common pattern of using client-side validation for the built-in validation checks and server-side validation for custom validation.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-561\">29.7 Performing remote validation<\/h2>\n<p class=\"body\">Remote validation blurs the line between client- and server-side validation: the validation checks are enforced by the client-side JavaScript code, but the validation checking is performed by sending an asynchronous HTTP request to the application to test the value entered into the form by the user.<\/p>\n<p class=\"body\">A common example of remote validation is to check whether a username is available in applications when such names must be unique, the user submits the data, and the client-side validation is performed. As part of this process, an asynchronous HTTP request is made to the server to validate the username that has been requested. If the username has been taken, a validation error is displayed so that the user can enter another value.<\/p>\n<p class=\"body\">This may seem like regular server-side validation, but there are some benefits to this approach. First, only some properties will be remotely validated; the client-side validation benefits still apply to all the other data values that the user has entered. Second, the request is relatively lightweight and is focused on validation, rather than processing an entire model object.<\/p>\n<p class=\"body\">The third difference is that the remote validation is performed in the background. The user doesn\u2019t have to click the submit button and then wait for a new view to be rendered and returned. It makes for a more responsive user experience, especially when there is a slow network between the browser and the server.<\/p>\n<p class=\"body\">That said, remote validation is a compromise. It strikes a balance between client-side and server-side validation, but it does require requests to the application server, and it is not as quick to validate as normal client-side validation.<\/p>\n<p class=\"body\">For the example application, I am going to use remote validation to ensure the user enters existing key values for the <code class=\"fm-code-in-text\">CategoryId<\/code> and <code class=\"fm-code-in-text\">SupplierId<\/code> properties. The first step is to create a web service controller whose action methods will perform the validation checks. I added a class file named <code class=\"fm-code-in-text\">ValidationController.cs<\/code> to the <code class=\"fm-code-in-text\">Controllers<\/code> folder with the code shown in listing 29.25.<a id=\"calibre_link-2571\"><\/a><a id=\"calibre_link-2572\"><\/a><a id=\"calibre_link-1062\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.25 The contents of the ValidationController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"api\/[controller]\")]\n    public class ValidationController: ControllerBase {\n        private DataContext dataContext;\n                \n        public ValidationController(DataContext context) {\n            dataContext = context;\n        }\n                \n        [HttpGet(\"categorykey\")]\n        public bool CategoryKey(string categoryId) {\n            long keyVal;\n            return long.TryParse(categoryId, out keyVal)\n                &amp;&amp; dataContext.Categories.Find(keyVal) != null;\n        }\n                \n        [HttpGet(\"supplierkey\")]\n        public bool SupplierKey(string supplierId) {\n            long keyVal;\n            return long.TryParse(supplierId, out keyVal)\n                &amp;&amp; dataContext.Suppliers.Find(keyVal) != null;\n        }\n    }\n}<\/pre>\n<p class=\"body\">Validation action methods must define a parameter whose name matches the field they will validate, which allows the model binding process to extract the value to test from the request query string. The response from the action method must be JSON and can be only true or false, indicating whether a value is acceptable. The action methods in listing 29.25 receive candidate values and check they have been used as database keys for <code class=\"fm-code-in-text\">Category<\/code> or <code class=\"fm-code-in-text\">Supplier<\/code> objects.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> I could have taken advantage of model binding so that the parameter to the action methods would be converted to a <code class=\"fm-code-in-text1\">long<\/code> value, but doing so would mean that the validation method wouldn\u2019t be called if the user entered a value that cannot be converted to the <code class=\"fm-code-in-text1\">long<\/code> type. If the model binder cannot convert a value, then the MVC Framework is unable to invoke the action method and validation can\u2019t be performed. As a rule, the best approach to remote validation is to accept a <code class=\"fm-code-in-text1\">string<\/code> parameter in the action method and perform any type conversion, parsing, or model binding explicitly.<\/p>\n<p class=\"body\">To use the remote validation method, I apply the <code class=\"fm-code-in-text\">Remote<\/code> attribute to the <code class=\"fm-code-in-text\">CategoryId<\/code> and <code class=\"fm-code-in-text\">SupplierId<\/code> properties in the <code class=\"fm-code-in-text\">Product<\/code> class, as shown in listing 29.26.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.26 Using the remote attribute in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Text.Json.Serialization;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\nusing WebApp.Validation;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc;<\/b>\n\nnamespace WebApp.Models {\n\n    [PhraseAndPrice(Phrase = \"Small\", Price = \"100\")]\n    public class Product {\n        \n        public long ProductId { get; set; }\n                \n        [Required(ErrorMessage = \"Please enter a name\")]\n        public required string Name { get; set; }\n                \n        [Range(1, 999999, ErrorMessage = \"Please enter a positive price\")]\n        [Column(TypeName = \"decimal(8, 2)\")]\n        public decimal Price { get; set; }\n                \n        [PrimaryKey(ContextType = typeof(DataContext), \n            DataType = typeof(Category))]\n        <b class=\"fm-bold\">[Remote(\"CategoryKey\", \"Validation\",<\/b>\n            <b class=\"fm-bold\">ErrorMessage = \"Enter an existing key\")]<\/b>\n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        [PrimaryKey(ContextType = typeof(DataContext), \n            DataType = typeof(Supplier))]\n        <b class=\"fm-bold\">[Remote(\"SupplierKey\", \"Validation\",<\/b> \n            <b class=\"fm-bold\">ErrorMessage = \"Enter an existing key\")]<\/b>\n        public long SupplierId { get; set; }\n                \n        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">The arguments to the <code class=\"fm-code-in-text\">Remote<\/code> attribute specify the name of the validation controller and its action method. I have also used the optional <code class=\"fm-code-in-text\">ErrorMessage<\/code> argument to specify the error message that will be displayed when validation fails. To see the remote validation, restart ASP.NET Core, navigate to http:\/\/localhost:5000\/controllers\/form, enter an invalid key value, and submit the form. You will see an error message, and the value of the <code class=\"fm-code-in-text\">input<\/code> element will be validated after each key press, as shown in figure 29.13. (Only the values 1, 2, and 3 are valid for both the <code class=\"fm-code-in-text\">CategoryId<\/code> and <code class=\"fm-code-in-text\">SupplierId<\/code> fields.)<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre317\" src=\"\/images\/proaspnetcore7\/000324.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.13 Performing remote validation<\/p>\n<\/div>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> The validation action method will be called when the user first submits the form and again each time the data is edited. For text input elements, every keystroke will lead to a call to the server. For some applications, this can be a significant number of requests and must be accounted for when specifying the server capacity and bandwidth that an application requires in production. Also, you might choose <i class=\"fm-italics\">not<\/i> to use remote validation for properties that are expensive to validate (the example repeatedly queries the database for key values, which may not be sensible for all applications or databases).<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-562\">29.7.1 Performing remote validation in Razor Pages<\/h3>\n<p class=\"body\">Remote validation works in Razor Pages, but attention must be paid to the names used in the asynchronous HTTP request used to validate values. For the controller example in the previous section, the browser will send requests to URLs like this:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost:5000\/api\/Validation\/categorykey?<b class=\"fm-bold\">CategoryId<\/b>=1<\/pre>\n<p class=\"body\">But for the example Razor Page, the URL will be like this, reflecting the use of the page model:<\/p>\n<pre class=\"programlisting\">http:\/\/localhost:5000\/api\/Validation\/categorykey?<b class=\"fm-bold\">Product.CategoryId<\/b>=1<\/pre>\n<p class=\"body\">The way I prefer to address this difference is by adding parameters to the validation action methods that will accept both types of request, which is easy to do using the model binding features described in previous chapters, as shown in listing 29.27.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 29.27 Adding parameters in the ValidationController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [ApiController]\n    [Route(\"api\/[controller]\")]\n    public class ValidationController : ControllerBase {\n        private DataContext dataContext;\n                \n        public ValidationController(DataContext context) {\n            dataContext = context;\n        }\n                \n        [HttpGet(\"categorykey\")]\n        <b class=\"fm-bold\">public bool CategoryKey(string? categoryId,<\/b> \n                <b class=\"fm-bold\">[FromQuery] KeyTarget target) {<\/b>\n            long keyVal;\n            return long.TryParse(categoryId ?? target.CategoryId, \n                    out keyVal)\n                &amp;&amp; dataContext.Categories.Find(keyVal) != null;\n        }\n                \n        [HttpGet(\"supplierkey\")]\n        <b class=\"fm-bold\">public bool SupplierKey(string? supplierId,<\/b> \n                <b class=\"fm-bold\">[FromQuery] KeyTarget target) {<\/b>\n            long keyVal;\n            <b class=\"fm-bold\">return long.TryParse(supplierId ?? target.SupplierId,<\/b> \n                    <b class=\"fm-bold\">out keyVal)<\/b>\n                &amp;&amp; dataContext.Suppliers.Find(keyVal) != null;\n        }\n    }\n        \n    <b class=\"fm-bold\">[Bind(Prefix = \"Product\")]<\/b>\n    <b class=\"fm-bold\">public class KeyTarget {<\/b>\n        <b class=\"fm-bold\">public string? CategoryId { get; set; }<\/b>\n        <b class=\"fm-bold\">public string? SupplierId { get; set; }<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">KeyTarget<\/code> class is configured to bind to the <code class=\"fm-code-in-text\">Product<\/code> part of the request, with properties that will match the two types of remote validation request. Each action method has been given a <code class=\"fm-code-in-text\">KeyTarget<\/code> parameter, which is used if no value is received for existing parameters. This allows the same action method to accommodate both types of request, which you can see by restarting ASP.NET Core, navigating to http:\/\/localhost:5000\/pages\/form, entering a nonexistent key value, and clicking the Submit button, which will produce the response shown in figure 29.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre318\" src=\"\/images\/proaspnetcore7\/000325.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 29.14 Performing remote validation using a Razor Page<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2573\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">ASP.NET Core provides integrated support for validating data to ensure it can be used by the application.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Validation messages can be displayed for the entire model or individual fields.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Basic validation is performed automatically and can be complemented by explicit custom validation.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Validation rules can be specified using attributes applied to model classes.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Validation is usually performed when data is sent to the server, but there is support for client-side validation using a JavaScript library.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Validation can also be performed by using JavaScript code to send individual data values to an ASP.NET Core controller for inspection.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-563\">\n<div class=\"calibre1\" id=\"calibre_link-2574\">\n<h1 class=\"tochead\" id=\"calibre_link-2575\">30 Using filters<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2576\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Injecting logic into request pipelines<\/li>\n<li class=\"co-summary-bullet\">Understanding the different filter types and when each is executed<\/li>\n<li class=\"co-summary-bullet\">Creating and applying filters<\/li>\n<li class=\"co-summary-bullet\">Managing the filter lifecycle and execution order<\/li>\n<\/ul>\n<p class=\"body\"><i class=\"fm-italics\">Filters<\/i> inject extra logic into request processing. Filters are like middleware that is applied to a single endpoint, which can be an action or a page handler method, and they provide an elegant way to manage a specific set of requests. In this chapter, I explain how filters work, describe the different types of filters that ASP.NET Core supports, and demonstrate the use of custom filters and the filters provided by ASP.NET Core. Table 30.1 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 30.1 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2577\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Implementing a security policy<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use an authorization filter.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">16, 17<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Implementing a resource policy, such as caching<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a resource filter.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">18&ndash;20<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Altering the request or response for an action method<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use an action filter.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">21&ndash;24<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Altering the request or response for a page handler method<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a page filter.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">25&ndash;27<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Inspecting or altering the result produced by an endpoint<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a result filter.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">28&ndash;30<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Inspecting or altering uncaught exceptions<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use an exception filter.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">31, 32<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Altering the filter lifecycle<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a filter factory or define a service.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">33&ndash;36<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Applying filters throughout an application<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a global filter.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">37, 38<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Changing the order in which filters are applied<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Implement the <code class=\"fm-code-in-text1\">IOrderedFilter<\/code> interface.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">39&ndash;43<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-564\">30.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the WebApp project from chapter 29. To prepare for this chapter, open a new PowerShell command prompt, navigate to the <code class=\"fm-code-in-text\">WebApp<\/code> project folder, and run the command shown in listing 30.1 to remove the files that are no longer required.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.1 Removing files from the project<\/p>\n<pre class=\"programlisting\">Remove-Item -Path Controllers,Views,Pages -Recurse -Exclude _*,Shared<\/pre>\n<p class=\"body\">This command removes the controllers, views, and Razor Pages, leaving behind the shared layouts, data model, and configuration files.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"body\">Create the <code class=\"fm-code-in-text\">WebApp\/Controllers<\/code> folder and add a class file named <code class=\"fm-code-in-text\">HomeController.cs<\/code> to the <code class=\"fm-code-in-text\">Controllers<\/code> folder with the code shown in listing 30.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.2 The contents of the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">The action method renders a view called <code class=\"fm-code-in-text\">Message<\/code> and passes a string as the view data. I added a Razor view named <code class=\"fm-code-in-text\">Message.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder with the content shown in listing 30.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.3 The contents of the Message.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">@{ Layout = \"_SimpleLayout\"; }\n\n@if (Model is string) {\n    @Model\n} else if (Model is IDictionary&lt;string, string&gt;) {\n    var dict = Model as IDictionary&lt;string, string&gt;;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Value&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n        &lt;tbody&gt;\n            @foreach (var kvp in dict ??\n                   new Dictionary&lt;string, string&gt;()) {\n                &lt;tr&gt;&lt;td&gt;@kvp.Key&lt;\/td&gt;&lt;td&gt;@kvp.Value&lt;\/td&gt;&lt;\/tr&gt;\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n}<\/pre>\n<p class=\"body\">Add a Razor Page named <code class=\"fm-code-in-text\">Message.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder and add the content shown in listing 30.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.4 The contents of the Message.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/message\"\n@model MessageModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n\n@if (Model.Message is string) {\n    @Model.Message\n} else if (Model.Message is IDictionary&lt;string, string&gt;) {\n    var dict = Model.Message as IDictionary&lt;string, string&gt;;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Value&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n        &lt;tbody&gt;\n            @if (dict != null) {\n                foreach (var kvp in dict) {\n                    &lt;tr&gt;&lt;td&gt;@kvp.Key&lt;\/td&gt;&lt;td&gt;@kvp.Value&lt;\/td&gt;&lt;\/tr&gt;\n                }\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n}\n\n@functions {\n    public class MessageModel : PageModel {\n        public object Message { get; set; } \n            = \"This is the Message Razor Page\";\n    }\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-565\">30.1.1 Enabling HTTPS Connections<\/h3>\n<p class=\"body\">Some of the examples in this chapter require the use of SSL. Add the configuration entries shown in listing 30.5 to the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file in the <code class=\"fm-code-in-text\">Properties<\/code> folder to enable SSL and set the port to 44350.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.5 Enabling HTTPS in the launchSettings.json file in the Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http:\/\/localhost:5000\",\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"WebApp\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": false,\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000;https:\/\/localhost:44350\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<p class=\"body\">The .NET Core runtime includes a test certificate that is used for HTTPS requests. Run the commands shown in listing 30.6 in the <code class=\"fm-code-in-text\">WebApp<\/code> folder to regenerate and trust the test certificate.<a id=\"calibre_link-2578\"><\/a><a id=\"calibre_link-1009\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.6 Regenerating the development certificates<\/p>\n<pre class=\"programlisting\">dotnet dev-certs https --clean\n\ndotnet dev-certs https --trust<\/pre>\n<p class=\"body\">Click Yes to the prompts to delete the existing certificate that has already been trusted and click Yes to trust the new certificate, as shown in figure 30.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre319\" src=\"\/images\/proaspnetcore7\/000326.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.1 Regenerating the HTTPS certificate<\/p>\n<\/div>\n<p class=\"body\">Listing 30.7 replaces the contents of the <code class=\"fm-code-in-text\">Program.cs<\/code> file to use the default controller routes and remove some of the services and components used in earlier chapters.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.7 Configuring the platform in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-566\">30.1.2 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 30.8 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.8 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-567\">30.1.3 Running the example application<\/h3>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 30.9.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.9 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000 and https:\/\/localhost:44350. Both URLs will be handled by the <code class=\"fm-code-in-text\">Index<\/code> action defined by the <code class=\"fm-code-in-text\">Home<\/code> controller, producing the responses shown in figure 30.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre320\" src=\"\/images\/proaspnetcore7\/000327.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.2 Responses from the Home controller<\/p>\n<\/div>\n<p class=\"body\">Request http:\/\/localhost:5000\/pages\/message and https:\/\/localhost:44350\/pages\/message to see the response from the <code class=\"fm-code-in-text\">Message<\/code> Razor Page, delivered over HTTP and HTTPS, as shown in figure 30.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre321\" src=\"\/images\/proaspnetcore7\/000328.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.3 Responses from the Message Razor Page<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-568\">30.2 Using filters<\/h2>\n<p class=\"body\">Filters allow logic that would otherwise be applied in a middleware component or action method to be defined in a class where it can be easily reused.<a id=\"calibre_link-2579\"><\/a><a id=\"calibre_link-952\"><\/a><\/p>\n<p class=\"body\">Imagine that you want to enforce HTTPS requests for some action methods. In chapter 16, I showed you how this can be done in middleware by reading the <code class=\"fm-code-in-text\">IsHttps<\/code> property of the <code class=\"fm-code-in-text\">HttpRequest<\/code> object. The problem with this approach is that the middleware would have to understand the configuration of the routing system to know how to intercept requests for specific action methods. A more focused approach would be to read the <code class=\"fm-code-in-text\">HttpRequest.IsHttps<\/code> property within action methods, as shown in listing 30.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.10 Selective HTTPS in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            <b class=\"fm-bold\">if (Request.IsHttps) {<\/b>\n                return View(\"Message\",\n                    \"This is the Index action on the Home controller\");\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return new StatusCodeResult(<\/b>\n                    <b class=\"fm-bold\">StatusCodes.Status403Forbidden);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000. This method now requires HTTPS, and you will see an error response. Request https:\/\/localhost:44350, and you will see the message output. Figure 30.4 shows both responses.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Clear your browser\u2019s history if you don\u2019t get the results you expect from the examples in this section. Browsers will often refuse to send requests to servers that have previously generated HTTPS errors, which is a good security practice but can be frustrating during development.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre322\" src=\"\/images\/proaspnetcore7\/000329.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.4 Enforcing HTTPS in an action method<\/p>\n<\/div>\n<p class=\"body\">This approach works but has problems. The first problem is that the action method contains code that is more about implementing a security policy than about handling the request. A more serious problem is that including the HTTP-detecting code within the action method doesn\u2019t scale well and must be duplicated in every action method in the controller, as shown in listing 30.11.<a id=\"calibre_link-967\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.11 Adding actions in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            if (Request.IsHttps) {\n                return View(\"Message\",\n                    \"This is the Index action on the Home controller\");\n            } else {\n                return new StatusCodeResult(\n                    StatusCodes.Status403Forbidden);\n            }\n        }\n                \n        <b class=\"fm-bold\">public IActionResult Secure() {<\/b>\n            <b class=\"fm-bold\">if (Request.IsHttps) {<\/b>\n                <b class=\"fm-bold\">return View(\"Message\",<\/b>\n                    <b class=\"fm-bold\">\"This is the Secure action on the Home controller\");<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return new StatusCodeResult(<\/b>\n                    <b class=\"fm-bold\">StatusCodes.Status403Forbidden);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">I must remember to implement the same check in every action method in every controller for which I want to require HTTPS. The code to implement the security policy is a substantial part of the&mdash;admittedly simple&mdash;controller, which makes the controller harder to understand, and it is only a matter of time before I forget to add it to a new action method, creating a hole in my security policy.<\/p>\n<p class=\"body\">This is the type of problem that filters address. Listing 30.12 replaces my checks for HTTPS and implements a filter instead.<a id=\"calibre_link-2580\"><\/a><a id=\"calibre_link-2581\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.12 Applying a filter in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Controllers {\n\n    public class HomeController : Controller {\n        \n        <b class=\"fm-bold\">[RequireHttps]<\/b>\n        <b class=\"fm-bold\">public IActionResult Index() {<\/b>\n            <b class=\"fm-bold\">return View(\"Message\",<\/b>\n                <b class=\"fm-bold\">\"This is the Index action on the Home controller\");<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">[RequireHttps]<\/b>\n        <b class=\"fm-bold\">public IActionResult Secure() {<\/b>\n            <b class=\"fm-bold\">return View(\"Message\",<\/b>\n                <b class=\"fm-bold\">\"This is the Secure action on the Home controller\");<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">RequireHttps<\/code> attribute applies one of the built-in filters provided by ASP.NET Core. This filter restricts access to action methods so that only HTTPS requests are supported and allows me to remove the security code from each method and focus on handling the successful requests.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The <code class=\"fm-code-in-text1\">RequireHttps<\/code> filter doesn\u2019t work the same way as my custom code. For <code class=\"fm-code-in-text1\">GET<\/code> requests, the <code class=\"fm-code-in-text1\">RequireHttps<\/code> attribute redirects the client to the originally requested URL, but it does so by using the <code class=\"fm-code-in-text1\">https<\/code> scheme so that a request to http:\/\/localhost:5000 will be redirected to https:\/\/localhost:5000. This makes sense for most deployed applications but not during development because HTTP and HTTPS are on different local ports. The <code class=\"fm-code-in-text1\">RequireHttpsAttribute<\/code> class defines a protected method called <code class=\"fm-code-in-text1\">HandleNonHttpsRequest<\/code> that you can override to change the behavior. Alternatively, I re-create the original functionality from scratch in the \u201cUnderstanding Authorization Filters\u201d section.<\/p>\n<p class=\"body\">I must still remember to apply the <code class=\"fm-code-in-text\">RequireHttps<\/code> attribute to each action method, which means that I might forget. But filters have a useful trick: applying the attribute to a controller class has the same effect as applying it to each individual action method, as shown in listing 30.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.13 Filtering all actions in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Controllers {\n\n    <b class=\"fm-bold\">[RequireHttps]<\/b>\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n \n        public IActionResult Secure() {\n            return View(\"Message\",\n                \"This is the Secure action on the Home controller\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">Filters can be applied with differing levels of granularity. If you want to restrict access to some actions but not others, then you can apply the <code class=\"fm-code-in-text\">RequireHttps<\/code> attribute to just those methods. If you want to protect all the action methods, including any that you add to the controller in the future, then the <code class=\"fm-code-in-text\">RequireHttps<\/code> attribute can be applied to the class. If you want to apply a filter to every action in an application, then you can use <i class=\"fm-italics\">global filters<\/i>, which I describe later in this chapter.<\/p>\n<p class=\"body\">Filters can also be used in Razor Pages. To implement the HTTPS-only policy in the <code class=\"fm-code-in-text\">Message<\/code> Razor Pages, for example, I would have to add a handler method that inspects the connection, as shown in listing 30.14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.14 Checking connections in the Message.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/message\"\n@model MessageModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n\n@if (Model.Message is string) {\n    @Model.Message\n} else if (Model.Message is IDictionary&lt;string, string&gt;) {\n    var dict = Model.Message as IDictionary&lt;string, string&gt;;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Value&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n        &lt;tbody&gt;\n            @if (dict != null) {\n                foreach (var kvp in dict) {\n                    &lt;tr&gt;&lt;td&gt;@kvp.Key&lt;\/td&gt;&lt;td&gt;@kvp.Value&lt;\/td&gt;&lt;\/tr&gt;\n                }\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n}\n\n@functions {\n    public class MessageModel : PageModel {\n        \n        public object Message { get; set; } \n            = \"This is the Message Razor Page\";\n                        \n        <b class=\"fm-bold\">public IActionResult OnGet() {<\/b>\n            <b class=\"fm-bold\">if (!Request.IsHttps) {<\/b>\n                <b class=\"fm-bold\">return new StatusCodeResult(<\/b>\n                    <b class=\"fm-bold\">StatusCodes.Status403Forbidden);<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return Page();<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The handler method works, but it is awkward and presents the same problems encountered with action methods. When using filters in Razor Pages, the attribute can be applied to the handler method or, as shown in listing 30.15, to the entire class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.15 Applying a filter in the Message.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">...\n@functions {\n\n    <b class=\"fm-bold\">[RequireHttps]<\/b>\n        \n    public class MessageModel : PageModel {\n        \n        public object Message { get; set; } \n            = \"This is the Message Razor Page\";\n                        \n        <b class=\"fm-bold\">\/\/public IActionResult OnGet() {<\/b>\n        <b class=\"fm-bold\">\/\/    if (!Request.IsHttps) {<\/b>\n        <b class=\"fm-bold\">\/\/        return new StatusCodeResult(<\/b>\n        <b class=\"fm-bold\">\/\/            StatusCodes.Status403Forbidden);<\/b>\n        <b class=\"fm-bold\">\/\/    } else {<\/b>\n        <b class=\"fm-bold\">\/\/        return Page();<\/b>\n        <b class=\"fm-bold\">\/\/    }<\/b>\n        <b class=\"fm-bold\">\/\/}<\/b>\n    }\n}\n...<\/pre>\n<p class=\"body\">You will see a normal response if you request https:\/\/localhost:44350\/pages\/message. If you request the regular HTTP URL, http:\/\/localhost:5000\/pages\/messages, the filter will redirect the request, and you will see an error (as noted earlier, the <code class=\"fm-code-in-text\">RequireHttps<\/code> filter redirects the browser to a port that is not enabled in the example application).<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-569\">30.3 Understanding filters<\/h2>\n<p class=\"body\">ASP.NET Core supports different types of filters, each of which is intended for a different purpose. Table 30.2 describes the filter categories.<a id=\"calibre_link-2582\"><\/a><a id=\"calibre_link-2583\"><\/a><a id=\"calibre_link-2584\"><\/a><a id=\"calibre_link-2585\"><\/a><a id=\"calibre_link-2586\"><\/a><a id=\"calibre_link-2587\"><\/a><a id=\"calibre_link-2588\"><\/a><a id=\"calibre_link-2589\"><\/a><a id=\"calibre_link-953\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 30.2 The filter types<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2590\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Authorization filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This type of filter is used to apply the application\u2019s authorization policy.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Resource filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This type of filter is used to intercept requests, typically to implement features such as caching.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Action filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This type of filter is used to modify the request before it is received by an action method or to modify the action result after it has been produced. This type of filter can be applied only to controllers and actions.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Page filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This type of filter is used to modify the request before it is received by a Razor Page handler method or to modify the action result after it has been produced. This type of filter can be applied only to Razor Pages.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Result filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This type of filter is used to alter the action result before it is executed or to modify the result after execution.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Exception filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This type of filter is used to handle exceptions that occur during the execution of the action method or page handler.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Filters have their own pipeline and are executed in a specific order, as shown in figure 30.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre323\" src=\"\/images\/proaspnetcore7\/000330.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.5 The filter pipeline<\/p>\n<\/div>\n<p class=\"body\">Filters can short-circuit the filter pipeline to prevent a request from being forwarded to the next filter. For example, an authorization filter can short-circuit the pipeline and return an error response if the user is unauthenticated. The resource, action, and page filters are able to inspect the request before and after it has been handled by the endpoint, allowing these types of filter to short-circuit the pipeline; to alter the request before it is handled; or to alter the response. (I have simplified the flow of filters in figure 30.5. Page filters run before and after the model binding process, as described in the \u201cUnderstanding Page Filters\u201d section.)<a id=\"calibre_link-2591\"><\/a><\/p>\n<p class=\"body\">Each type of filter is implemented using interfaces defined by ASP.NET Core, which also provides base classes that make it easy to apply some types of filters as attributes. I describe each interface and the attribute classes in the sections that follow, but they are shown in table 30.3 for quick reference.<a id=\"calibre_link-2592\"><\/a><a id=\"calibre_link-2593\"><\/a><a id=\"calibre_link-2594\"><\/a><a id=\"calibre_link-2595\"><\/a><a id=\"calibre_link-956\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 30.3 The filter types, interfaces, and attribute base classes<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2596\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Filter Type<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Interfaces<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Attribute Class<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Authorization filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IAuthorizationFilter<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IAsyncAuthorizationFilter<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No attribute class is provided.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Resource filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IResourceFilter&nbsp;<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IAsyncResourceFilter<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No attribute class is provided.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Action filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IActionFilter<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IAsyncActionFilter<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ActionFilterAttribute<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Page filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IPageFilter<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IAsyncPageFilter<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">No attribute class is provided.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Result filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IResultFilter<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IAsyncResultFilter<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IAlwaysRunResultFilter<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IAsyncAlwaysRunResultFilter<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ResultFilterAttribute<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Exception Filters<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IExceptionFilter<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IAsyncExceptionFilter<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExceptionFilterAttribute<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-570\">30.4 Creating custom filters<\/h2>\n<p class=\"body\">Filters implement the <code class=\"fm-code-in-text\">IFilterMetadata<\/code> interface, which is in the <code class=\"fm-code-in-text\">Microsoft.AspNetCore.Mvc.Filters<\/code> namespace. Here is the interface:<\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n    public interface IFilterMetadata { }\n}<\/pre>\n<p class=\"body\">The interface is empty and doesn\u2019t require a filter to implement any specific behaviors. This is because each of the categories of filter described in the previous section works in a different way. Filters are provided with context data in the form of a <code class=\"fm-code-in-text\">FilterContext<\/code> object. For convenience, Table 30.4 describes the properties that <code class=\"fm-code-in-text\">FilterContext<\/code> provides.<a id=\"calibre_link-2597\"><\/a><a id=\"calibre_link-957\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 30.4 The FilterContext properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2598\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ActionDescriptor<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">ActionDescriptor<\/code> object, which describes the action method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpContext<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">HttpContext<\/code> object, which provides details of the HTTP request and the HTTP response that will be sent in return.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ModelState<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">ModelStateDictionary<\/code> object, which is used to validate data sent by the client.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RouteData<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">RouteData<\/code> object that describes the way that the routing system has processed the request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Filters<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a list of filters that have been applied to the action method, expressed as an <code class=\"fm-code-in-text1\">IList&lt;IFilterMetadata&gt;<\/code>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-571\">30.4.1 Understanding authorization filters<\/h3>\n<p class=\"body\">Authorization filters are used to implement an application\u2019s security policy. Authorization filters are executed before other types of filter and before the endpoint handles the request. Here is the definition of the <code class=\"fm-code-in-text\">IAuthorizationFilter<\/code> interface:<a id=\"calibre_link-2599\"><\/a><a id=\"calibre_link-2600\"><\/a><a id=\"calibre_link-2601\"><\/a><\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n\n    public interface IAuthorizationFilter : IFilterMetadata {\n        \n        void OnAuthorization(AuthorizationFilterContext context);\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnAuthorization<\/code> method is called to provide the filter with the opportunity to authorize the request. For asynchronous authorization filters, here is the definition of the <code class=\"fm-code-in-text\">IAsyncAuthorizationFilter<\/code> interface:<\/p>\n<pre class=\"programlisting\">using System.Threading.Tasks;\n\nnamespace Microsoft.AspNetCore.Mvc.Filters {\n\n    public interface IAsyncAuthorizationFilter : IFilterMetadata {\n        \n        Task OnAuthorizationAsync(AuthorizationFilterContext context);\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnAuthorizationAsync<\/code> method is called so that the filter can authorize the request. Whichever interface is used, the filter receives context data describing the request through an <code class=\"fm-code-in-text\">AuthorizationFilterContext<\/code> object, which is derived from the <code class=\"fm-code-in-text\">FilterContext<\/code> class and adds one important property, as described in table 30.5.<\/p>\n<p class=\"fm-table-caption\">Table 30.5 The AuthorizationFilterContext property<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2602\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Result<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">IActionResult<\/code> property is set by authorization filters when the request doesn\u2019t comply with the application\u2019s authorization policy. If this property is set, then ASP.NET Core executes the <code class=\"fm-code-in-text1\">IActionResult<\/code> instead of invoking the endpoint.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">To demonstrate how authorization filters work, I created a <code class=\"fm-code-in-text\">Filters<\/code> folder in the <code class=\"fm-code-in-text\">WebApp<\/code> folder, added a class file called <code class=\"fm-code-in-text\">HttpsOnlyAttribute.cs<\/code>, and used it to define the filter shown in listing 30.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.16 The contents of the HttpsOnlyAttribute.cs file in the Filters folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Filters;\n\nnamespace WebApp.Filters {\n    public class HttpsOnlyAttribute : Attribute, IAuthorizationFilter {\n        \n        public void OnAuthorization(AuthorizationFilterContext context) {\n            if (!context.HttpContext.Request.IsHttps) {\n                context.Result = \n                    new StatusCodeResult(StatusCodes.Status403Forbidden);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">An authorization filter does nothing if a request complies with the authorization policy, and inaction allows ASP.NET Core to move on to the next filter and, eventually, to execute the endpoint. If there is a problem, the filter sets the <code class=\"fm-code-in-text\">Result<\/code> property of the <code class=\"fm-code-in-text\">AuthorizationFilterContext<\/code> object that is passed to the <code class=\"fm-code-in-text\">OnAuthorization<\/code> method. This prevents further execution from happening and provides a result to return to the client. In the listing, the <code class=\"fm-code-in-text\">HttpsOnlyAttribute<\/code> class inspects the <code class=\"fm-code-in-text\">IsHttps<\/code> property of the <code class=\"fm-code-in-text\">HttpRequest<\/code> context object and sets the <code class=\"fm-code-in-text\">Result<\/code> property to interrupt execution if the request has been made without HTTPS. Authorization filters can be applied to controllers, action methods, and Razor Pages. Listing 30.17 applies the new filter to the <code class=\"fm-code-in-text\">Home<\/code> controller.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.17 Applying a filter in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\n<b class=\"fm-bold\">using WebApp.Filters;<\/b>\n\nnamespace WebApp.Controllers {\n\n    <b class=\"fm-bold\">\/\/[RequireHttps]<\/b>\n    <b class=\"fm-bold\">[HttpsOnly]<\/b>\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n \n        public IActionResult Secure() {\n            return View(\"Message\",\n                \"This is the Secure action on the Home controller\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">This filter re-creates the functionality that I included in the action methods in listing 30.11. This is less useful in real projects than doing a redirection like the built-in <code class=\"fm-code-in-text\">RequireHttps<\/code> filter because users won\u2019t understand the meaning of a 403 status code, but it does provide a useful example of how authorization filters work. Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the effect of the filter, as shown in figure 30.6. Request https:\/\/localhost:44350, and you will receive the response from the action method, also shown in the figure.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre324\" src=\"\/images\/proaspnetcore7\/000331.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.6 Applying a custom authorization filter<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-572\">30.4.2 Understanding resource filters<\/h3>\n<p class=\"body\">Resource filters are executed twice for each request: before the ASP.NET Core model binding process and again before the action result is processed to generate the result. Here is the definition of the <code class=\"fm-code-in-text\">IResourceFilter<\/code> interface:<a id=\"calibre_link-2603\"><\/a><a id=\"calibre_link-2604\"><\/a><a id=\"calibre_link-2605\"><\/a><a id=\"calibre_link-964\"><\/a><\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n    public interface IResourceFilter : IFilterMetadata {\n        \n        void OnResourceExecuting(ResourceExecutingContext context);\n                \n        void OnResourceExecuted(ResourceExecutedContext context);\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnResourceExecuting<\/code> method is called when a request is being processed, and the <code class=\"fm-code-in-text\">OnResourceExecuted<\/code> method is called after the endpoint has handled the request but before the action result is executed. For asynchronous resource filters, here is the definition of the <code class=\"fm-code-in-text\">IAsyncResourceFilter<\/code> interface:<\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n    public interface IAsyncResourceFilter : IFilterMetadata {\n        \n        Task OnResourceExecutionAsync(ResourceExecutingContext context,\n            ResourceExecutionDelegate next);\n    }\n}<\/pre>\n<p class=\"body\">This interface defines a single method that receives a context object and a delegate to invoke. The resource filter is able to inspect the request before invoking the delegate and inspect the response before it is executed. The <code class=\"fm-code-in-text\">OnResourceExecuting<\/code> method is provided with context using the <code class=\"fm-code-in-text\">ResourceExecutingContext<\/code> class, which defines the properties shown in table 30.6 in addition to those defined by the <code class=\"fm-code-in-text\">FilterContext<\/code> class.<a id=\"calibre_link-2606\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 30.6 The properties defined by the ResourceExecutingContext class<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2607\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Result<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">IActionResult<\/code> property is used to provide a result to short-circuit the pipeline.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ValueProviderFactories<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an <code class=\"fm-code-in-text1\">IList&lt;IValueProviderFactory&gt;<\/code>, which provides access to the objects that provide values for the model binding process.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\"><a id=\"calibre_link-913\"><\/a>The <code class=\"fm-code-in-text\">OnResourceExecuted<\/code> method is provided with context using the <code class=\"fm-code-in-text\">ResourceExecutedContext<\/code> class, which defines the properties shown in table 30.7, in addition to those defined by the <code class=\"fm-code-in-text\">FilterContext<\/code> class.<\/p>\n<p class=\"fm-table-caption\">Table 30.7 The properties defined by the ResourceExecutedContext class<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2608\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Result<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">IActionResult<\/code> property provides the action result that will be used to produce a response.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Canceled<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">bool<\/code> property is set to <code class=\"fm-code-in-text1\">true<\/code> if another filter has short-circuited the pipeline by assigning an action result to the <code class=\"fm-code-in-text1\">Result<\/code> property of the <code class=\"fm-code-in-text1\">ActionExecutingContext<\/code> object.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Exception<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is used to store an exception thrown during execution.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExceptionDispatchInfo<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns an <code class=\"fm-code-in-text1\">ExceptionDispatchInfo<\/code> object that contains the stack trace details of any exception thrown during execution.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExceptionHandled<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Setting this property to <code class=\"fm-code-in-text1\">true<\/code> indicates that the filter has handled the exception, which will not be propagated any further.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"fm-head2\">Creating a resource filter<\/p>\n<p class=\"body\">Resource filters are usually used where it is possible to short-circuit the pipeline and provide a response early, such as when implementing data caching. To create a simple caching filter, add a class file called <code class=\"fm-code-in-text\">SimpleCacheAttribute.cs<\/code> to the <code class=\"fm-code-in-text\">Filters<\/code> folder with the code shown in listing 30.18.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Filters and dependency injection<\/p>\n<p class=\"fm-sidebar-text\">Filters that are applied as attributes cannot declare dependencies in their constructors unless they implement the <code class=\"fm-code-in-text1\">IFilterFactory<\/code> interface and take responsibility for creating instances directly, as explained in the \"Creating Filter Factories\" section later in this chapter.<a id=\"calibre_link-2609\"><\/a><a id=\"calibre_link-2610\"><\/a><\/p>\n<\/div>\n<p class=\"fm-code-listing-caption\">Listing 30.18 The contents of the SimpleCacheAttribute.cs file in the Filters folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Filters;\n\nnamespace WebApp.Filters {\n\n    public class SimpleCacheAttribute : Attribute, IResourceFilter {\n        private Dictionary&lt;PathString, IActionResult&gt; CachedResponses\n            = new Dictionary&lt;PathString, IActionResult&gt;();\n                        \n        public void OnResourceExecuting(\n                ResourceExecutingContext context) {\n            PathString path = context.HttpContext.Request.Path;\n            if (CachedResponses.ContainsKey(path)) {\n                context.Result = CachedResponses[path];\n                CachedResponses.Remove(path);\n            }\n        }\n \n        public void OnResourceExecuted(ResourceExecutedContext context) {\n            if (context.Result != null) {\n                CachedResponses.Add(context.HttpContext.Request.Path, \n                    context.Result);\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">This filter isn\u2019t an especially useful cache, but it does show how a resource filter works. The <code class=\"fm-code-in-text\">OnResourceExecuting<\/code> method provides the filter with the opportunity to short-circuit the pipeline by setting the context object\u2019s <code class=\"fm-code-in-text\">Result<\/code> property to a previously cached action result. If a value is assigned to the <code class=\"fm-code-in-text\">Result<\/code> property, then the filter pipeline is short-circuited, and the action result is executed to produce the response for the client. Cached action results are used only once and then discarded from the cache. If no value is assigned to the <code class=\"fm-code-in-text\">Result<\/code> property, then the request passes to the next step in the pipeline, which may be another filter or the endpoint.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnResourceExecuted<\/code> method provides the filter with the action results that are produced when the pipeline is not short-circuited. In this case, the filter caches the action result so that it can be used for subsequent requests. Resource filters can be applied to controllers, action methods, and Razor Pages. Listing 30.19 applies the custom resource filter to the <code class=\"fm-code-in-text\">Message<\/code> Razor Page and adds a timestamp that will help determine when an action result is cached.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.19 Applying a resource filter in the Message.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/message\"\n@model MessageModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n<b class=\"fm-bold\">@using WebApp.Filters<\/b>\n\n@if (Model.Message is string) {\n    @Model.Message\n} else if (Model.Message is IDictionary&lt;string, string&gt;) {\n    var dict = Model.Message as IDictionary&lt;string, string&gt;;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Value&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n        &lt;tbody&gt;\n            @if (dict != null) {\n                foreach (var kvp in dict) {\n                    &lt;tr&gt;&lt;td&gt;@kvp.Key&lt;\/td&gt;&lt;td&gt;@kvp.Value&lt;\/td&gt;&lt;\/tr&gt;\n                }\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n}\n\n@functions {\n\n    [RequireHttps]\n    <b class=\"fm-bold\">[SimpleCache]<\/b>\n    public class MessageModel : PageModel {\n        \n        <b class=\"fm-bold\">public object Message { get; set; } =<\/b>\n            <b class=\"fm-bold\">DateTime.Now.ToLongTimeString()<\/b> \n                <b class=\"fm-bold\">+ \" This is the Message Razor Page\";<\/b>\n    }\n}<\/pre>\n<p class=\"body\">To see the effect of the resource filter, restart ASP.NET Core and request https:\/\/localhost:44350\/pages\/message. Since this is the first request for the path, there will be no cached result, and the request will be forwarded along the pipeline. As the response is processed, the resource filter will cache the action result for future use. Reload the browser to repeat the request, and you will see the same timestamp, indicating that the cached action result has been used. The cached item is removed when it is used, which means that reloading the browser will generate a response with a fresh timestamp, as shown in figure 30.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre325\" src=\"\/images\/proaspnetcore7\/000332.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.7 Using a resource filter<\/p>\n<\/div>\n<p class=\"fm-head2\">Creating an asynchronous resource filter<\/p>\n<p class=\"body\">The interface for asynchronous resource filters uses a single method that receives a delegate used to forward the request along the filter pipeline. Listing 30.20 reimplements the caching filter from the previous example so that it implements the <code class=\"fm-code-in-text\">IAsyncResourceFilter<\/code> interface.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.20 An asynchronous filter in the Filters\/SimpleCacheAttribute.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Filters;\n\nnamespace WebApp.Filters {\n\n    <b class=\"fm-bold\">public class SimpleCacheAttribute : Attribute, IAsyncResourceFilter {<\/b>\n        private Dictionary&lt;PathString, IActionResult&gt; CachedResponses\n            = new Dictionary&lt;PathString, IActionResult&gt;();\n                        \n        <b class=\"fm-bold\">public async Task OnResourceExecutionAsync(<\/b>\n                <b class=\"fm-bold\">ResourceExecutingContext context,<\/b>\n                <b class=\"fm-bold\">ResourceExecutionDelegate next) {<\/b>\n            <b class=\"fm-bold\">PathString path = context.HttpContext.Request.Path;<\/b>\n            <b class=\"fm-bold\">if (CachedResponses.ContainsKey(path)) {<\/b>\n                <b class=\"fm-bold\">context.Result = CachedResponses[path];<\/b>\n                <b class=\"fm-bold\">CachedResponses.Remove(path);<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">ResourceExecutedContext execContext = await next();<\/b>\n                <b class=\"fm-bold\">if (execContext.Result != null) {<\/b>\n                    <b class=\"fm-bold\">CachedResponses.Add(context.HttpContext.Request.Path,<\/b>\n                        <b class=\"fm-bold\">execContext.Result);<\/b>\n                <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnResourceExecutionAsync<\/code> method receives a <code class=\"fm-code-in-text\">ResourceExecutingContext<\/code> object, which is used to determine whether the pipeline can be short-circuited. If it cannot, the delegate is invoked without arguments and asynchronously produces a <code class=\"fm-code-in-text\">ResourceExecutedContext<\/code> object when the request has been handled and is making its way back along the pipeline. Restart ASP.NET Core and repeat the requests described in the previous section, and you will see the same caching behavior, as shown in figure 30.7.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> It is important not to confuse the two context objects. The action result produced by the endpoint is available only in the context object that is returned by the delegate.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-573\">30.4.3 Understanding action filters<\/h3>\n<p class=\"body\">Like resource filters, action filters are executed twice. The difference is that action filters are executed after the model binding process, whereas resource filters are executed before model binding. This means that resource filters can short-circuit the pipeline and minimize the work that ASP.NET Core does on the request. Action filters are used when model binding is required, which means they are used for tasks such as altering the model or enforcing validation. Action filters can be applied only to controllers and action methods, unlike resource filters, which can also be used with Razor Pages. (The Razor Pages equivalent to action filters is the page filter, described in the \"Understanding Page Filters\" section.) Here is the <code class=\"fm-code-in-text\">IActionFilter<\/code> interface:<a id=\"calibre_link-2611\"><\/a><a id=\"calibre_link-2612\"><\/a><a id=\"calibre_link-2613\"><\/a><a id=\"calibre_link-954\"><\/a><\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n\n    public interface IActionFilter : IFilterMetadata {\n        \n        void OnActionExecuting(ActionExecutingContext context);\n                \n        void OnActionExecuted(ActionExecutedContext context);\n    }\n}<\/pre>\n<p class=\"body\">When an action filter has been applied to an action method, the <code class=\"fm-code-in-text\">OnActionExecuting<\/code> method is called just before the action method is invoked, and the <code class=\"fm-code-in-text\">OnActionExecuted<\/code> method is called just after. Action filters are provided with context data through two different context classes: <code class=\"fm-code-in-text\">ActionExecutingContext<\/code> for the <code class=\"fm-code-in-text\">OnActionExecuting<\/code> method and <code class=\"fm-code-in-text\">ActionExecutedContext<\/code> for the <code class=\"fm-code-in-text\">OnActionExecuted<\/code> method.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ActionExecutingContext<\/code> class, which is used to describe an action that is about to be invoked, defines the properties described in table 30.8, in addition to the <code class=\"fm-code-in-text\">FilterContext<\/code> properties.<a id=\"calibre_link-2614\"><\/a><a id=\"calibre_link-955\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 30.8 The ActionExecutingContext properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2615\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Controller<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the controller whose action method is about to be invoked. (Details of the action method are available through the <code class=\"fm-code-in-text1\">ActionDescriptor<\/code> property inherited from the base classes.)<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ActionArguments<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a dictionary of the arguments that will be passed to the action method, indexed by name. The filter can insert, remove, or change the arguments.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Result<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">If the filter assigns an <code class=\"fm-code-in-text1\">IActionResult<\/code> to this property, then the pipeline will be short-circuited, and the action result will be used to generate the response to the client without invoking the action method.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ActionExecutedContext<\/code> class is used to represent an action that has been executed and defines the properties described in table 30.9, in addition to the <code class=\"fm-code-in-text\">FilterContext<\/code> properties.<\/p>\n<p class=\"fm-table-caption\">Table 30.9 The ActionExecutedContext properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2616\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Controller<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the <code class=\"fm-code-in-text1\">Controller<\/code> object whose action method will be invoked.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Canceled<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">bool<\/code> property is set to <code class=\"fm-code-in-text1\">true<\/code> if another action filter has short-circuited the pipeline by assigning an action result to the <code class=\"fm-code-in-text1\">Result<\/code> property of the <code class=\"fm-code-in-text1\">ActionExecutingContext<\/code> object.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Exception<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property contains any <code class=\"fm-code-in-text1\">Exception<\/code> that was thrown by the action method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExceptionDispatchInfo<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns an <code class=\"fm-code-in-text1\">ExceptionDispatchInfo<\/code> object that contains the stack trace details of any exception thrown by the action method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExceptionHandled<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Setting this property to <code class=\"fm-code-in-text1\">true<\/code> indicates that the filter has handled the exception, which will not be propagated any further.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Result<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the <code class=\"fm-code-in-text1\">IActionResult<\/code> produced by the action method. The filter can change or replace the action result if required.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Asynchronous action filters are implemented using the <code class=\"fm-code-in-text\">IAsyncActionFilter<\/code> interface.<\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n\n    public interface IAsyncActionFilter : IFilterMetadata {\n        \n        Task OnActionExecutionAsync(ActionExecutingContext context, \n            ActionExecutionDelegate next);\n    }\n}<\/pre>\n<p class=\"body\">This interface follows the same pattern as the <code class=\"fm-code-in-text\">IAsyncResourceFilter<\/code> interface described earlier in the chapter. The <code class=\"fm-code-in-text\">OnActionExecutionAsync<\/code> method is provided with an <code class=\"fm-code-in-text\">ActionExecutingContext<\/code> object and a delegate. The <code class=\"fm-code-in-text\">ActionExecutingContext<\/code> object describes the request before it is received by the action method. The filter can short-circuit the pipeline by assigning a value to the <code class=\"fm-code-in-text\">ActionExecutingContext.Result<\/code> property or pass it along by invoking the delegate. The delegate asynchronously produces an <code class=\"fm-code-in-text\">ActionExecutedContext<\/code> object that describes the result from the action method.<\/p>\n<p class=\"fm-head2\">Creating an action filter<\/p>\n<p class=\"body\">Add a class file called <code class=\"fm-code-in-text\">ChangeArgAttribute.cs<\/code> to the <code class=\"fm-code-in-text\">Filters<\/code> folder and use it to define the action filter shown in listing 30.21.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.21 The contents of the ChangeArgAttribute.cs file in the Filters folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.Filters;\n\nnamespace WebApp.Filters {\n    public class ChangeArgAttribute : Attribute, IAsyncActionFilter {\n        \n        public async Task OnActionExecutionAsync(\n                ActionExecutingContext context,\n                ActionExecutionDelegate next) {\n                                \n            if (context.ActionArguments.ContainsKey(\"message1\")) {\n                context.ActionArguments[\"message1\"] = \"New message\";\n            }\n            await next();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The filter looks for an action argument named <code class=\"fm-code-in-text\">message1<\/code> and changes the value that will be used to invoke the action method. The values that will be used for the action method arguments are determined by the model binding process. Listing 30.22 adds an action method to the <code class=\"fm-code-in-text\">Home<\/code> controller and applies the new filter.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.22 Applying a filter in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Filters;\n\nnamespace WebApp.Controllers {\n\n    [HttpsOnly]\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n \n        public IActionResult Secure() {\n            return View(\"Message\",\n                \"This is the Secure action on the Home controller\");\n        }\n                \n        <b class=\"fm-bold\">[ChangeArg]<\/b>\n        <b class=\"fm-bold\">public IActionResult Messages(string message1,<\/b> \n                <b class=\"fm-bold\">string message2 = \"None\") {<\/b>\n            <b class=\"fm-bold\">return View(\"Message\", $\"{message1}, {message2}\");<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request https:\/\/localhost:44350\/home\/messages?message1=hello&amp;message2=world. The model binding process will locate values for the parameters defined by the action method from the query string. One of those values is then modified by the action filter, producing the response shown in figure 30.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre326\" src=\"\/images\/proaspnetcore7\/000333.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.8 Using an action filter<\/p>\n<\/div>\n<p class=\"fm-head2\">Implementing an action filter using the attribute base class<\/p>\n<p class=\"body\">Action attributes can also be implemented by deriving from the <code class=\"fm-code-in-text\">ActionFilterAttribute<\/code> class, which extends <code class=\"fm-code-in-text\">Attribute<\/code> and inherits both the <code class=\"fm-code-in-text\">IActionFilter<\/code> and <code class=\"fm-code-in-text\">IAsyncActionFilter<\/code> interfaces so that implementation classes override just the methods they require. In listing 30.23, I have reimplemented the <code class=\"fm-code-in-text\">ChangeArg<\/code> filter so that it is derived from <code class=\"fm-code-in-text\">ActionFilterAttribute<\/code>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.23 Using a filter base class in the Filters\/ChangeArgsAttribute.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.Filters;\n\nnamespace WebApp.Filters {\n    <b class=\"fm-bold\">public class ChangeArgAttribute : ActionFilterAttribute {<\/b>\n        \n        <b class=\"fm-bold\">public override async Task OnActionExecutionAsync(<\/b>\n            <b class=\"fm-bold\">ActionExecutingContext context,<\/b> \n                <b class=\"fm-bold\">ActionExecutionDelegate next) {<\/b>\n                                \n            if (context.ActionArguments.ContainsKey(\"message1\")) {\n                context.ActionArguments[\"message1\"] = \"New message\";\n            }\n            await next();\n        }\n    }\n}<\/pre>\n<p class=\"body\">This attribute behaves in just the same way as the earlier implementation, and the use of the base class is a matter of preference. Restart ASP.NET Core and request https:\/\/localhost:44350\/home\/messages?message1=hello&amp;message2=world, and you will see the response shown in figure 30.8.<\/p>\n<p class=\"fm-head2\">Using the controller filter methods<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Controller<\/code> class, which is the base for controllers that render Razor views, implements the <code class=\"fm-code-in-text\">IActionFilter<\/code> and <code class=\"fm-code-in-text\">IAsyncActionFilter<\/code> interfaces, which means you can define functionality and apply it to the actions defined by a controller and any derived controllers. Listing 30.24 implements the <code class=\"fm-code-in-text\">ChangeArg<\/code> filter functionality directly in the <code class=\"fm-code-in-text\">HomeController<\/code> class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.24 Using action filter methods in the Controllers\/HomeController.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Filters;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc.Filters;<\/b>\n\nnamespace WebApp.Controllers {\n\n    [HttpsOnly]\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n \n        public IActionResult Secure() {\n            return View(\"Message\",\n                \"This is the Secure action on the Home controller\");\n        }\n                \n        <b class=\"fm-bold\">\/\/[ChangeArg]<\/b>\n        public IActionResult Messages(string message1, \n                string message2 = \"None\") {\n            return View(\"Message\", $\"{message1}, {message2}\");\n        }\n                \n        <b class=\"fm-bold\">public override void OnActionExecuting(<\/b>\n                <b class=\"fm-bold\">ActionExecutingContext context) {<\/b>\n            <b class=\"fm-bold\">if (context.ActionArguments.ContainsKey(\"message1\")) {<\/b>\n                <b class=\"fm-bold\">context.ActionArguments[\"message1\"] = \"New message\";<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Home<\/code> controller overrides the <code class=\"fm-code-in-text\">Controller<\/code> implementation of the <code class=\"fm-code-in-text\">OnActionExecuting<\/code> method and uses it to modify the arguments that will be passed to the execution method. Restart ASP.NET Core and request https:\/\/localhost:44350\/home\/messages?message1=hello&amp;message2=world, and you will see the response shown in figure 30.8.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-574\">30.4.4 Understanding page filters<\/h3>\n<p class=\"body\">Page filters are the Razor Page equivalent of action filters. Here is the <code class=\"fm-code-in-text\">IPageFilter<\/code> interface, which is implemented by synchronous page filters:<a id=\"calibre_link-2617\"><\/a><a id=\"calibre_link-2618\"><\/a><a id=\"calibre_link-2619\"><\/a><a id=\"calibre_link-963\"><\/a><\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n\n    public interface IPageFilter : IFilterMetadata {\n        \n        void OnPageHandlerSelected(PageHandlerSelectedContext context);\n                \n        void OnPageHandlerExecuting(PageHandlerExecutingContext context);\n                \n        void OnPageHandlerExecuted(PageHandlerExecutedContext context);\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnPageHandlerSelected<\/code> method is invoked after ASP.NET Core has selected the page handler method but before model binding has been performed, which means the arguments for the handler method have not been determined. This method receives context through the <code class=\"fm-code-in-text\">PageHandlerSelectedContext<\/code> class, which defines the properties shown in table 30.10, in addition to those defined by the <code class=\"fm-code-in-text\">FilterContext<\/code> class. This method cannot be used to short-circuit the pipeline, but it can alter the handler method that will receive the request.<a id=\"calibre_link-2620\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 30.10 The PageHandlerSelectedContext properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2621\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ActionDescriptor<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the description of the Razor Page.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HandlerMethod<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a <code class=\"fm-code-in-text1\">HandlerMethodDescriptor<\/code> object that describes the selected handler method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HandlerInstance<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the instance of the Razor Page that will handle the request.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnPageHandlerExecuting<\/code> method is called after the model binding process has completed but before the page handler method is invoked. This method receives context through the <code class=\"fm-code-in-text\">PageHandlerExecutingContext<\/code> class, which defines the properties shown in table 30.11, in addition to those defined by the <code class=\"fm-code-in-text\">PageHandlerSelectedContext<\/code> class.<\/p>\n<p class=\"fm-table-caption\">Table 30.11 The PageHandlerExecutingContext properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2622\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HandlerArguments<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a dictionary containing the page handler arguments, indexed by name.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Result<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The filter can short-circuit the pipeline by assigning an <code class=\"fm-code-in-text1\">IActionResult<\/code> object to this property.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnPageHandlerExecuted<\/code> method is called after the page handler method has been invoked but before the action result is processed to create a response. This method receives context through the <code class=\"fm-code-in-text\">PageHandlerExecutedContext<\/code> class, which defines the properties shown in table 30.12 in addition to the <code class=\"fm-code-in-text\">PageHandlerExecutingContext<\/code> properties.<\/p>\n<p class=\"fm-table-caption\">Table 30.12 The PageHandlerExecutedContext properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2623\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Canceled<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns <code class=\"fm-code-in-text1\">true<\/code> if another filter short-circuited the filter pipeline.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Exception<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an exception if one was thrown by the page handler method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExceptionHandled<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is set to <code class=\"fm-code-in-text1\">true<\/code> to indicate that an exception thrown by the page handler has been handled by the filter.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Result<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the action result that will be used to create a response for the client.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Asynchronous page filters are created by implementing the <code class=\"fm-code-in-text\">IAsyncPageFilter<\/code> interface, which is defined like this:<\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n    public interface IAsyncPageFilter : IFilterMetadata {\n        \n        Task OnPageHandlerSelectionAsync(\n            PageHandlerSelectedContext context);\n                        \n        Task OnPageHandlerExecutionAsync(\n            PageHandlerExecutingContext context, \n            PageHandlerExecutionDelegate next);\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnPageHandlerSelectionAsync<\/code> is called after the handler method is selected and is equivalent to the synchronous <code class=\"fm-code-in-text\">OnPageHandlerSelected<\/code> method. The <code class=\"fm-code-in-text\">OnPageHandlerExecutionAsync<\/code> is provided with a <code class=\"fm-code-in-text\">PageHandlerExecutingContext<\/code> object that allows it to short-circuit the pipeline and with a delegate that is invoked to pass on the request. The delegate produces a <code class=\"fm-code-in-text\">PageHandlerExecutedContext<\/code> object that can be used to inspect or alter the action result produced by the handler method.<\/p>\n<p class=\"fm-head2\">Creating a page filter<\/p>\n<p class=\"body\">To create a page filter, add a class file named <code class=\"fm-code-in-text\">ChangePageArgs.cs<\/code> to the <code class=\"fm-code-in-text\">Filters<\/code> folder and use it to define the class shown in listing 30.25.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.25 The contents of the ChangePageArgs.cs file in the Filters folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.Filters;\n\nnamespace WebApp.Filters {\n    public class ChangePageArgs : Attribute, IPageFilter {\n        \n        public void OnPageHandlerSelected(\n                PageHandlerSelectedContext context) {\n            \/\/ do nothing\n        }\n \n        public void OnPageHandlerExecuting(\n                PageHandlerExecutingContext context) {\n            if (context.HandlerArguments.ContainsKey(\"message1\")) {\n                context.HandlerArguments[\"message1\"] = \"New message\";\n            }\n        }\n \n        public void OnPageHandlerExecuted(\n                PageHandlerExecutedContext context) {\n            \/\/ do nothing\n        }\n    }\n}<\/pre>\n<p class=\"body\">The page filter in listing 30.25 performs the same task as the action filter I created in the previous section. In listing 30.26, I have modified the <code class=\"fm-code-in-text\">Message<\/code> Razor Page to define a handler method and have applied the page filter. Page filters can be applied to individual handler methods or, as in the listing, to the page model class, in which case the filter is used for all handler methods. (I also disabled the <code class=\"fm-code-in-text\">SimpleCache<\/code> filter in listing 30.26. Resource filters can work alongside page filters. I disabled this filter because caching responses makes some of the examples more difficult to follow.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.26 Using a page filter in the Message.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/message\"\n@model MessageModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using WebApp.Filters\n\n@if (Model.Message is string) {\n    @Model.Message\n} else if (Model.Message is IDictionary&lt;string, string&gt;) {\n    var dict = Model.Message as IDictionary&lt;string, string&gt;;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Value&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n        &lt;tbody&gt;\n            @if (dict != null) {\n                foreach (var kvp in dict) {\n                    &lt;tr&gt;&lt;td&gt;@kvp.Key&lt;\/td&gt;&lt;td&gt;@kvp.Value&lt;\/td&gt;&lt;\/tr&gt;\n                }\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n}\n\n@functions {\n\n    [RequireHttps]\n    <b class=\"fm-bold\">\/\/[SimpleCache]<\/b>\n    <b class=\"fm-bold\">[ChangePageArgs]<\/b>\n    public class MessageModel : PageModel {\n        \n        public object Message { get; set; } =\n            DateTime.Now.ToLongTimeString() \n                + \" This is the Message Razor Page\";\n                                \n        <b class=\"fm-bold\">public void OnGet(string message1, string message2) {<\/b>\n            <b class=\"fm-bold\">Message = $\"{message1}, {message2}\";<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request https:\/\/localhost:44350\/pages\/message?message1=hello&amp;message2=world. The page filter will replace the value of the <code class=\"fm-code-in-text\">message1<\/code> argument for the <code class=\"fm-code-in-text\">OnGet<\/code> handler method, which produces the response shown in figure 30.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre327\" src=\"\/images\/proaspnetcore7\/000334.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.9 Using a page filter<\/p>\n<\/div>\n<p class=\"fm-head2\">Using the page model filter methods<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">PageModel<\/code> class, which is used as the base for page model classes, implements the <code class=\"fm-code-in-text\">IPageFilter<\/code> and <code class=\"fm-code-in-text\">IAsyncPageFilter<\/code> interfaces, which means you can add filter functionality directly to a page model, as shown in listing 30.27.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.27 Using the filter methods in the Message.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/message\"\n@model MessageModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using WebApp.Filters\n<b class=\"fm-bold\">@using Microsoft.AspNetCore.Mvc.Filters<\/b>\n\n@if (Model.Message is string) {\n    @Model.Message\n} else if (Model.Message is IDictionary&lt;string, string&gt;) {\n    var dict = Model.Message as IDictionary&lt;string, string&gt;;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Value&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n        &lt;tbody&gt;\n            @if (dict != null) {\n                foreach (var kvp in dict) {\n                    &lt;tr&gt;&lt;td&gt;@kvp.Key&lt;\/td&gt;&lt;td&gt;@kvp.Value&lt;\/td&gt;&lt;\/tr&gt;\n                }\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n}\n\n@functions {\n\n    [RequireHttps]\n    \/\/[SimpleCache]\n    <b class=\"fm-bold\">\/\/[ChangePageArgs]<\/b>\n    public class MessageModel : PageModel {\n        \n        public object Message { get; set; } =\n            DateTime.Now.ToLongTimeString() \n                + \" This is the Message Razor Page\";\n                                \n        public void OnGet(string message1, string message2) {\n            Message = $\"{message1}, {message2}\";\n        }\n                \n        <b class=\"fm-bold\">public override void OnPageHandlerExecuting(<\/b>\n                <b class=\"fm-bold\">PageHandlerExecutingContext context) {<\/b>\n            <b class=\"fm-bold\">if (context.HandlerArguments.ContainsKey(\"message1\")) {<\/b>\n                <b class=\"fm-bold\">context.HandlerArguments[\"message1\"] = \"New message\";<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Request https:\/\/localhost:44350\/pages\/message?message1=hello&amp;message2=world. The method implemented by the page model class in listing 30.27 will produce the same result as shown in figure 30.9.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-575\">30.4.5 Understanding result filters<\/h3>\n<p class=\"body\">Result filters are executed before and after an action result is used to generate a response, allowing responses to be modified after they have been handled by the endpoint. Here is the definition of the <code class=\"fm-code-in-text\">IResultFilter<\/code> interface:<a id=\"calibre_link-2624\"><\/a><a id=\"calibre_link-2625\"><\/a><a id=\"calibre_link-2626\"><\/a><a id=\"calibre_link-2627\"><\/a><a id=\"calibre_link-2628\"><\/a><a id=\"calibre_link-962\"><\/a><\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n    public interface IResultFilter : IFilterMetadata {\n\n        void OnResultExecuting(ResultExecutingContext context);\n        void OnResultExecuted(ResultExecutedContext context);\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnResultExecuting<\/code> method is called after the endpoint has produced an action result. This method receives context through the <code class=\"fm-code-in-text\">ResultExecutingContext<\/code> class, which defines the properties described in table 30.13, in addition to those defined by the <code class=\"fm-code-in-text\">FilterContext<\/code> class.<a id=\"calibre_link-2629\"><\/a><a id=\"calibre_link-969\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 30.13 The ResultExecutingContext class properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2630\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Controller<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the object that contains the endpoint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Cancel<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Setting this property to <code class=\"fm-code-in-text1\">true<\/code> will short-circuit the result filter pipeline.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Result<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the action result produced by the endpoint.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnResultExecuted<\/code> method is called after the action result has been executed to generate the response for the client. This method receives context through the <code class=\"fm-code-in-text\">ResultExecutedContext<\/code> class, which defines the properties shown in table 30.14, in addition to those it inherits from the <code class=\"fm-code-in-text\">FilterContext<\/code> class.<\/p>\n<p class=\"fm-table-caption\">Table 30.14 The ResultExecutedContext class<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2631\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Canceled<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns <code class=\"fm-code-in-text1\">true<\/code> if another filter short-circuited the filter pipeline.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Controller<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the object that contains the endpoint.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Exception<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns an exception if one was thrown by the page handler method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExceptionHandled<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property is set to <code class=\"fm-code-in-text1\">true<\/code> to indicate that an exception thrown by the page handler has been handled by the filter.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Result<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the action result that will be used to create a response for the client. This property is read-only.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Asynchronous result filters implement the <code class=\"fm-code-in-text\">IAsyncResultFilter<\/code> interface, which is defined like this:<\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n \n    public interface IAsyncResultFilter : IFilterMetadata {\n        \n        Task OnResultExecutionAsync(ResultExecutingContext context, \n            ResultExecutionDelegate next);\n    }\n}<\/pre>\n<p class=\"body\">This interface follows the pattern established by the other filter types. The <code class=\"fm-code-in-text\">OnResultExecutionAsync<\/code> method is invoked with a context object whose <code class=\"fm-code-in-text\">Result<\/code> property can be used to alter the response and a delegate that will forward the response along the pipeline.<\/p>\n<p class=\"fm-head2\">Understanding always-run result filters<\/p>\n<p class=\"body\">Filters that implement the <code class=\"fm-code-in-text\">IResultFilter<\/code> and <code class=\"fm-code-in-text\">IAsyncResultFilter<\/code> interfaces are used only when a request is handled normally by the endpoint. They are not used if another filter short-circuits the pipeline or if there is an exception. Filters that need to inspect or alter the response, even when the pipeline is short-circuited, can implement the <code class=\"fm-code-in-text\">IAlwaysRunResultFilter<\/code> or <code class=\"fm-code-in-text\">IAsyncAlwaysRunResultFilter<\/code> interface. These interfaces derived from <code class=\"fm-code-in-text\">IResultFilter<\/code> and <code class=\"fm-code-in-text\">IAsyncResultFilter<\/code> but define no new features. Instead, ASP.NET Core detects the always-run interfaces and always applies the filters.<a id=\"calibre_link-2632\"><\/a><a id=\"calibre_link-968\"><\/a><\/p>\n<p class=\"fm-head2\">Creating a result filter<\/p>\n<p class=\"body\">Add a class file named <code class=\"fm-code-in-text\">ResultDiagnosticsAttribute.cs<\/code> to the <code class=\"fm-code-in-text\">Filters<\/code> folder and use it to define the filter shown in listing 30.28.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.28 The contents of the ResultDiagnosticsAttribute.cs file in the Filters folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Filters;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\n\nnamespace WebApp.Filters {\n\n    public class ResultDiagnosticsAttribute : \n            Attribute, IAsyncResultFilter {\n                        \n        public async Task OnResultExecutionAsync(\n                ResultExecutingContext context, \n                ResultExecutionDelegate next) {\n                                \n            if (context.HttpContext.Request.Query.ContainsKey(\"diag\")) {\n                Dictionary&lt;string, string?&gt; diagData =\n                    new Dictionary&lt;string, string?&gt; {\n                        {\"Result type\", context.Result.GetType().Name }\n                    };\n                if (context.Result is ViewResult vr) {\n                    diagData[\"View Name\"] = vr.ViewName;\n                    diagData[\"Model Type\"] \n                        = vr.ViewData?.Model?.GetType().Name;\n                    diagData[\"Model Data\"] \n                        = vr.ViewData?.Model?.ToString();\n                } else if (context.Result is PageResult pr) {\n                    diagData[\"Model Type\"] = pr.Model.GetType().Name;\n                    diagData[\"Model Data\"] \n                        = pr.ViewData?.Model?.ToString();\n                }\n                context.Result = new ViewResult() {\n                    ViewName = \"\/Views\/Shared\/Message.cshtml\",\n                    ViewData = new ViewDataDictionary(\n                                       new EmptyModelMetadataProvider(),\n                                       new ModelStateDictionary()) {\n                        Model = diagData\n                    }\n                };\n            }\n            await next();\n        }\n    }\n}<\/pre>\n<p class=\"body\">This filter examines the request to see whether it contains a query string parameter named <code class=\"fm-code-in-text\">diag<\/code>. If it does, then the filter creates a result that displays diagnostic information instead of the output produced by the endpoint. The filter in listing 30.28 will work with the actions defined by the <code class=\"fm-code-in-text\">Home<\/code> controller or the <code class=\"fm-code-in-text\">Message<\/code> Razor Page. Listing 30.29 applies the result filter to the <code class=\"fm-code-in-text\">Home<\/code> controller.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Notice that I use a fully qualified name for the view when I create the action result in listing 30.28. This avoids a problem with filters applied to Razor Pages, where ASP.NET Core tries to execute the new result as a Razor Page and throws an exception about the model type.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.29 Applying a result filter in the Controllers\/HomeController.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Filters;\nusing Microsoft.AspNetCore.Mvc.Filters;\n\nnamespace WebApp.Controllers {\n\n    [HttpsOnly]\n    <b class=\"fm-bold\">[ResultDiagnostics]<\/b>\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n \n        public IActionResult Secure() {\n            return View(\"Message\",\n                \"This is the Secure action on the Home controller\");\n        }\n                \n        \/\/[ChangeArg]\n        public IActionResult Messages(string message1, \n                string message2 = \"None\") {\n            return View(\"Message\", $\"{message1}, {message2}\");\n        }\n \n        public override void OnActionExecuting(\n                ActionExecutingContext context) {\n            if (context.ActionArguments.ContainsKey(\"message1\")) {\n                context.ActionArguments[\"message1\"] = \"New message\";\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request https:\/\/localhost:44350\/?diag. The query string parameter will be detected by the filter, which will generate the diagnostic information shown in figure 30.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre328\" src=\"\/images\/proaspnetcore7\/000335.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.10 Using a result filter<\/p>\n<\/div>\n<p class=\"fm-head2\">Implementing a result filter using the attribute base class<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ResultFilterAttribute<\/code> class is derived from <code class=\"fm-code-in-text\">Attribute<\/code> and implements the <code class=\"fm-code-in-text\">IResultFilter<\/code> and <code class=\"fm-code-in-text\">IAsyncResultFilter<\/code> interfaces and can be used as the base class for result filters, as shown in listing 30.30. There is no attribute base class for the always-run interfaces.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.30 Using the base class in the Filters\/ResultDiagnosticsAttribute.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Filters;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\n\nnamespace WebApp.Filters {\n\n    <b class=\"fm-bold\">public class ResultDiagnosticsAttribute : ResultFilterAttribute {<\/b>\n        \n        <b class=\"fm-bold\">public override async Task OnResultExecutionAsync(<\/b>\n                <b class=\"fm-bold\">ResultExecutingContext context,<\/b>\n                <b class=\"fm-bold\">ResultExecutionDelegate next) {<\/b>\n                                \n            if (context.HttpContext.Request.Query.ContainsKey(\"diag\")) {\n                Dictionary&lt;string, string?&gt; diagData =\n                    new Dictionary&lt;string, string?&gt; {\n                        {\"Result type\", context.Result.GetType().Name }\n                    };\n                if (context.Result is ViewResult vr) {\n                    diagData[\"View Name\"] = vr.ViewName;\n                    diagData[\"Model Type\"] \n                        = vr.ViewData?.Model?.GetType().Name;\n                    diagData[\"Model Data\"] \n                        = vr.ViewData?.Model?.ToString();\n                } else if (context.Result is PageResult pr) {\n                    diagData[\"Model Type\"] = pr.Model.GetType().Name;\n                    diagData[\"Model Data\"] \n                        = pr.ViewData?.Model?.ToString();\n                }\n                context.Result = new ViewResult() {\n                    ViewName = \"\/Views\/Shared\/Message.cshtml\",\n                    ViewData = new ViewDataDictionary(\n                                       new EmptyModelMetadataProvider(),\n                                       new ModelStateDictionary()) {\n                        Model = diagData\n                    }\n                };\n            }\n            await next();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request https:\/\/localhost:44350\/?diag. The filter will produce the output shown in figure 30.10.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-576\">30.4.6 Understanding exception filters<\/h3>\n<p class=\"body\">Exception filters allow you to respond to exceptions without having to write <code class=\"fm-code-in-text\">try...catch<\/code> blocks in every action method. Exception filters can be applied to controller classes, action methods, page model classes, or handler methods. They are invoked when an exception is not handled by the endpoint or by the action, page, and result filters that have been applied to the endpoint. (Action, page, and result filters can deal with an unhandled exception by setting the <code class=\"fm-code-in-text\">ExceptionHandled<\/code> property of their context objects to <code class=\"fm-code-in-text\">true<\/code>.) Exception filters implement the <code class=\"fm-code-in-text\">IExceptionFilter<\/code> interface, which is defined as follows:<a id=\"calibre_link-2633\"><\/a><a id=\"calibre_link-2634\"><\/a><a id=\"calibre_link-2635\"><\/a><a id=\"calibre_link-958\"><\/a><\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n\n    public interface IExceptionFilter : IFilterMetadata {\n        \n        void OnException(ExceptionContext context);\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnException<\/code> method is called if an unhandled exception is encountered. The <code class=\"fm-code-in-text\">IAsyncExceptionFilter<\/code> interface can be used to create asynchronous exception filters. Here is the definition of the asynchronous interface:<\/p>\n<pre class=\"programlisting\">using System.Threading.Tasks;\n\nnamespace Microsoft.AspNetCore.Mvc.Filters {\n\n    public interface IAsyncExceptionFilter : IFilterMetadata {\n        \n        Task OnExceptionAsync(ExceptionContext context);\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OnExceptionAsync<\/code> method is the asynchronous counterpart to the <code class=\"fm-code-in-text\">OnException<\/code> method from the <code class=\"fm-code-in-text\">IExceptionFilter<\/code> interface and is called when there is an unhandled exception. For both interfaces, context data is provided through the <code class=\"fm-code-in-text\">ExceptionContext<\/code> class, which is derived from <code class=\"fm-code-in-text\">FilterContext<\/code> and defines the additional properties shown in table 30.15.<a id=\"calibre_link-2636\"><\/a><a id=\"calibre_link-959\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 30.15 The ExceptionContext properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2637\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Exception<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property contains any <code class=\"fm-code-in-text1\">Exception<\/code> that was thrown.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ExceptionHandled<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">bool<\/code> property is used to indicate if the exception has been handled.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Result<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property sets the <code class=\"fm-code-in-text1\">IActionResult<\/code> that will be used to generate the response.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-577\">30.4.7 Creating an exception filter<\/h3>\n<p class=\"body\">Exception filters can be created by implementing one of the filter interfaces or by deriving from the <code class=\"fm-code-in-text\">ExceptionFilterAttribute<\/code> class, which is derived from <code class=\"fm-code-in-text\">Attribute<\/code> and implements both the <code class=\"fm-code-in-text\">IExceptionFilter<\/code> and <code class=\"fm-code-in-text\">IAsyncException<\/code> filters. The most common use for an exception filter is to present a custom error page for a specific exception type to provide the user with more useful information than the standard error-handling capabilities can provide.<\/p>\n<p class=\"body\">To create an exception filter, add a class file named <code class=\"fm-code-in-text\">RangeExceptionAttribute.cs<\/code> to the <code class=\"fm-code-in-text\">Filters<\/code> folder with the code shown in listing 30.31.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.31 The contents of the RangeExceptionAttribute.cs file in the Filters folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Filters;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\n\nnamespace WebApp.Filters {\n    public class RangeExceptionAttribute : ExceptionFilterAttribute {\n\n        public override void OnException(ExceptionContext context) {\n            if (context.Exception is ArgumentOutOfRangeException) {\n                context.Result = new ViewResult() {\n                    ViewName = \"\/Views\/Shared\/Message.cshtml\",\n                    ViewData = new ViewDataDictionary(\n                        new EmptyModelMetadataProvider(),\n                        new ModelStateDictionary()) {\n                        Model = @\"The data received by the\n                                application cannot be processed\"\n                    }\n                };\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">This filter uses the <code class=\"fm-code-in-text\">ExceptionContext<\/code> object to get the type of the unhandled exception and, if the type is <code class=\"fm-code-in-text\">ArgumentOutOfRangeException<\/code>, creates an action result that displays a message to the user. Listing 30.32 adds an action method to the <code class=\"fm-code-in-text\">Home<\/code> controller to which I have applied the exception filter.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.32 Applying an exception filter in the Controllers\/HomeController.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Filters;\nusing Microsoft.AspNetCore.Mvc.Filters;\n\nnamespace WebApp.Controllers {\n\n    [HttpsOnly]\n    [ResultDiagnostics]\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n \n        public IActionResult Secure() {\n            return View(\"Message\",\n                \"This is the Secure action on the Home controller\");\n        }\n                \n        \/\/[ChangeArg]\n        public IActionResult Messages(string message1, \n                string message2 = \"None\") {\n            return View(\"Message\", $\"{message1}, {message2}\");\n        }\n \n        public override void OnActionExecuting(\n                ActionExecutingContext context) {\n            if (context.ActionArguments.ContainsKey(\"message1\")) {\n                context.ActionArguments[\"message1\"] = \"New message\";\n            }\n        }\n                \n        <b class=\"fm-bold\">[RangeException]<\/b>\n        <b class=\"fm-bold\">public ViewResult GenerateException(int? id) {<\/b>\n            <b class=\"fm-bold\">if (id == null) {<\/b>\n                <b class=\"fm-bold\">throw new ArgumentNullException(nameof(id));<\/b>\n            <b class=\"fm-bold\">} else if (id &gt; 10) {<\/b>\n                <b class=\"fm-bold\">throw new ArgumentOutOfRangeException(nameof(id));<\/b>\n            <b class=\"fm-bold\">} else {<\/b>\n                <b class=\"fm-bold\">return View(\"Message\", $\"The value is {id}\");<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">GenerateException<\/code> action method relies on the default routing pattern to receive a nullable <code class=\"fm-code-in-text\">int<\/code> value from the request URL. The action method throws an <code class=\"fm-code-in-text\">ArgumentNullException<\/code> if there is no matching URL segment and throws an <code class=\"fm-code-in-text\">ArgumentOutOfRangeException<\/code> if its value is greater than 10. If there is a value and it is in range, then the action method returns a <code class=\"fm-code-in-text\">ViewResult<\/code>.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request https:\/\/localhost:44350\/Home\/GenerateException\/100. The final segment will exceed the range expected by the action method, which will throw the exception type that is handled by the filter, producing the result shown in figure 30.11. If you request <code class=\"fm-code-in-text\">\/Home\/GenerateException<\/code>, then the exception thrown by the action method won\u2019t be handled by the filter, and the default error handling will be used.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre329\" src=\"\/images\/proaspnetcore7\/000336.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.11 Using an exception filter<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-578\">30.5 Managing the filter lifecycle<\/h2>\n<p class=\"body\">By default, ASP.NET Core manages the filter objects it creates and will reuse them for subsequent requests. This isn\u2019t always the desired behavior, and in the sections that follow, I describe different ways to take control of how filters are created. To create a filter that will show the lifecycle, add a class file called <code class=\"fm-code-in-text\">GuidResponseAttribute.cs<\/code> to the <code class=\"fm-code-in-text\">Filters<\/code> folder, and use it to define the filter shown in listing 30.33.<a id=\"calibre_link-2638\"><\/a><a id=\"calibre_link-966\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.33 The contents of the GuidResponseAttribute.cs file in the Filters folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Filters;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\n\nnamespace WebApp.Filters {\n\n    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,\n         AllowMultiple = true)]\n    public class GuidResponseAttribute \n            : Attribute, IAsyncAlwaysRunResultFilter {\n        private int counter = 0;\n        private string guid = Guid.NewGuid().ToString();\n                \n        public async Task OnResultExecutionAsync(\n            ResultExecutingContext context,\n            ResultExecutionDelegate next) {\n                        \n            Dictionary&lt;string, string&gt; resultData;\n            if (context.Result is ViewResult vr\n                &amp;&amp; vr.ViewData.Model is Dictionary&lt;string, string&gt; data) {\n                resultData = data;\n            } else {\n                resultData = new Dictionary&lt;string, string&gt;();\n                context.Result = new ViewResult() {\n                    ViewName = \"\/Views\/Shared\/Message.cshtml\",\n                    ViewData = new ViewDataDictionary(\n                                        new EmptyModelMetadataProvider(),\n                                        new ModelStateDictionary()) {\n                        Model = resultData\n                    }\n                };\n            }\n            while (resultData.ContainsKey($\"Counter_{counter}\")) {\n                counter++;\n            }\n            resultData[$\"Counter_{counter}\"] = guid;\n            await next();\n        }\n    }\n}<\/pre>\n<p class=\"body\">This result filter replaces the action result produced by the endpoint with one that will render the <code class=\"fm-code-in-text\">Message<\/code> view and display a unique GUID value. The filter is configured so that it can be applied more than once to the same target and will add a new message if a filter earlier in the pipeline has created a suitable result. Listing 30.34 applies the filter twice to the <code class=\"fm-code-in-text\">Home<\/code> controller. (I have also removed all but one of the action methods for brevity.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.34 Applying a filter in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Filters;\n\nnamespace WebApp.Controllers {\n\n    [HttpsOnly]\n    [ResultDiagnostics]\n    <b class=\"fm-bold\">[GuidResponse]<\/b>\n    <b class=\"fm-bold\">[GuidResponse]<\/b>\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">To confirm that the filter is being reused, restart ASP.NET Core and request https:\/\/localhost:44350\/?diag. The response will contain GUID values from the two <code class=\"fm-code-in-text\">GuidResponse<\/code> filter attributes. Two instances of the filter have been created to handle the request. Reload the browser, and you will see the same GUID values displayed, indicating that the filter objects created to handle the first request have been reused (Figure 30.12).<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre330\" src=\"\/images\/proaspnetcore7\/000337.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.12 Demonstrating filter reuse<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-579\">30.5.1 Creating filter factories<\/h3>\n<p class=\"body\">Filters can implement the <code class=\"fm-code-in-text\">IFilterFactory<\/code> interface to take responsibility for creating instances of filters and specify whether those instances can be reused. The <code class=\"fm-code-in-text\">IFilterFactory<\/code> interface defines the members described in table 30.16.<a id=\"calibre_link-2639\"><\/a><a id=\"calibre_link-2640\"><\/a><a id=\"calibre_link-2641\"><\/a><a id=\"calibre_link-2642\"><\/a><a id=\"calibre_link-960\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 30.16 The IFilterFactory members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2643\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsReusable<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">bool<\/code> property indicates whether instances of the filter can be reused.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">CreateInstance(serviceProvider)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is invoked to create new instances of the filter and is provided with an <code class=\"fm-code-in-text1\">IServiceProvider<\/code> object.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Listing 30.35 implements the <code class=\"fm-code-in-text\">IFilterFactory<\/code> interface and returns <code class=\"fm-code-in-text\">false<\/code> for the <code class=\"fm-code-in-text\">IsReusable<\/code> property, which prevents the filter from being reused.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.35 Implementing an interface in the Filters\/GuidResponseAttribute.cs file<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Filters;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\n\nnamespace WebApp.Filters {\n\n    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,\n         AllowMultiple = true)]\n                 \n    <b class=\"fm-bold\">public class GuidResponseAttribute : Attribute,<\/b>\n            <b class=\"fm-bold\">IAsyncAlwaysRunResultFilter, IFilterFactory {<\/b>\n        private int counter = 0;\n        private string guid = Guid.NewGuid().ToString();\n                \n        <b class=\"fm-bold\">public bool IsReusable =&gt; false;<\/b>\n                \n        <b class=\"fm-bold\">public IFilterMetadata CreateInstance(<\/b>\n                <b class=\"fm-bold\">IServiceProvider serviceProvider) {<\/b>\n            <b class=\"fm-bold\">return ActivatorUtilities<\/b>\n                <b class=\"fm-bold\">.GetServiceOrCreateInstance<\/b>\n                    <b class=\"fm-bold\">&lt;GuidResponseAttribute&gt;(serviceProvider);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        public async Task OnResultExecutionAsync(\n                ResultExecutingContext context,\n                ResultExecutionDelegate next) {\n                                \n            Dictionary&lt;string, string&gt; resultData;\n            if (context.Result is ViewResult vr\n                &amp;&amp; vr.ViewData.Model is Dictionary&lt;string, string&gt; data) {\n                resultData = data;\n            } else {\n                resultData = new Dictionary&lt;string, string&gt;();\n                context.Result = new ViewResult() {\n                    ViewName = \"\/Views\/Shared\/Message.cshtml\",\n                    ViewData = new ViewDataDictionary(\n                                        new EmptyModelMetadataProvider(),\n                                        new ModelStateDictionary()) {\n                        Model = resultData\n                    }\n                };\n            }\n            while (resultData.ContainsKey($\"Counter_{counter}\")) {\n                counter++;\n            }\n            resultData[$\"Counter_{counter}\"] = guid;\n            await next();\n        }\n    }\n}<\/pre>\n<p class=\"body\">I create new filter objects using the <code class=\"fm-code-in-text\">GetServiceOrCreateInstance<\/code> method, defined by the <code class=\"fm-code-in-text\">ActivatorUtilities<\/code> class in the <code class=\"fm-code-in-text\">Microsoft.Extensions.DependencyInjection<\/code> namespace. Although you can use the <code class=\"fm-code-in-text\">new<\/code> keyword to create a filter, this approach will resolve any dependencies on services that are declared through the filter\u2019s constructor.<\/p>\n<p class=\"body\">To see the effect of implementing the <code class=\"fm-code-in-text\">IFilterFactory<\/code> interface, restart ASP.NET Core and request https:\/\/localhost:44350\/?diag. Reload the browser, and each time the request is handled, new filters will be created, and new GUIDs will be displayed, as shown in figure 30.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre331\" src=\"\/images\/proaspnetcore7\/000338.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.13 Preventing filter reuse<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-580\">30.5.2 Using dependency injection scopes to manage filter lifecycles<\/h3>\n<p class=\"body\">Filters can be registered as services, which allows their lifecycle to be controlled through dependency injection, which I described in chapter 14. Listing 30.36 registers the <code class=\"fm-code-in-text\">GuidResponse<\/code> filter as a scoped service.<a id=\"calibre_link-2644\"><\/a><a id=\"calibre_link-2645\"><\/a><a id=\"calibre_link-2646\"><\/a><a id=\"calibre_link-914\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.36 Creating a filter service in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n<b class=\"fm-bold\">using WebApp.Filters;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\n\n<b class=\"fm-bold\">builder.Services.AddScoped&lt;GuidResponseAttribute&gt;();<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">By default, ASP.NET Core creates a scope for each request, which means that a single instance of the filter will be created for each request. To see the effect, restart ASP.NET Core and request https:\/\/localhost:44350\/?diag. Both attributes applied to the <code class=\"fm-code-in-text\">Home<\/code> controller are processed using the same instance of the filter, which means that both GUIDs in the response are the same. Reload the browser; a new scope will be created, and a new filter object will be used, as shown in figure 30.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre332\" src=\"\/images\/proaspnetcore7\/000339.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.14 Using dependency injection to manage filters<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Using filters as services without the IFilterFactory interface<\/p>\n<p class=\"fm-sidebar-text\">The change in lifecycle took effect immediately in this example because I used the <code class=\"fm-code-in-text1\">ActivatorUtilities.GetServiceOrCreateInstance<\/code> method to create the filter object when I implemented the <code class=\"fm-code-in-text1\">IFilterFactory<\/code> interface. This method will check to see whether there is a service available for the requested type before invoking its constructor. If you want to use filters as services without implementing <code class=\"fm-code-in-text1\">IFilterFactory<\/code> and using <code class=\"fm-code-in-text1\">ActivatorUtilities<\/code>, you can apply the filter using the <code class=\"fm-code-in-text1\">ServiceFilter<\/code> attribute, like this:<a id=\"calibre_link-2647\"><\/a><a id=\"calibre_link-961\"><\/a><\/p>\n<pre class=\"programlisting\">...\n[ServiceFilter(typeof(GuidResponseAttribute))]\n...<\/pre>\n<p class=\"fm-sidebar-text\">ASP.NET Core will create the filter object from the service and apply it to the request. Filters that are applied in this way do not have to be derived from the <code class=\"fm-code-in-text1\">Attribute<\/code> class.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-581\">30.6 Creating global filters<\/h2>\n<p class=\"body\">Global filters are applied to every request that ASP.NET Core handles, which means they don\u2019t have to be applied to individual controllers or Razor Pages. Any filter can be used as a global filter; however, action filters will be applied to requests only where the endpoint is an action method, and page filters will be applied to requests only where the endpoint is a Razor Page.<\/p>\n<p class=\"body\">Global filters are set up using the options pattern in the <code class=\"fm-code-in-text\">Program.cs<\/code> file, as shown in listing 30.37.<a id=\"calibre_link-2648\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.37 Creating a global filter in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing WebApp.Filters;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Mvc;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\n\nbuilder.Services.AddScoped&lt;GuidResponseAttribute&gt;();\n<b class=\"fm-bold\">builder.Services.Configure&lt;MvcOptions&gt;(opts =&gt;<\/b>\n    <b class=\"fm-bold\">opts.Filters.Add&lt;HttpsOnlyAttribute&gt;());<\/b>\n        \nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">MvcOptions.Filters<\/code> property returns a collection to which filters are added to apply them globally, either using the <code class=\"fm-code-in-text\">Add&lt;T&gt;<\/code> method or using the <code class=\"fm-code-in-text\">AddService&lt;T&gt;<\/code> method for filters that are also services. There is also an <code class=\"fm-code-in-text\">Add<\/code> method without a generic type argument that can be used to register a specific object as a global filter.<\/p>\n<p class=\"body\">The statement in listing 30.37 registers the <code class=\"fm-code-in-text\">HttpsOnly<\/code> filter I created earlier in the chapter, which means that it no longer needs to be applied directly to individual controllers or Razor Pages, so listing 30.38 removes the filter from the <code class=\"fm-code-in-text\">Home<\/code> controller.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Notice that I have disabled the <code class=\"fm-code-in-text1\">GuidResponse<\/code> filter in listing 30.38. This is an always-run result filter and will replace the result generated by the global filter.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.38 Removing a filter in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Filters;\n\nnamespace WebApp.Controllers {\n\n    <b class=\"fm-bold\">\/\/[HttpsOnly]<\/b>\n    [ResultDiagnostics]\n    <b class=\"fm-bold\">\/\/[GuidResponse]<\/b>\n    <b class=\"fm-bold\">\/\/[GuidResponse]<\/b>\n    public class HomeController : Controller {\n        \n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000 to confirm that the HTTPS-only policy is being applied even though the attribute is no longer used to decorate the controller. The global authorization filter will short-circuit the filter pipeline and produce the response shown in figure 30.15.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre333\" src=\"\/images\/proaspnetcore7\/000340.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.15 Using a global filter<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-582\">30.7 Understanding and changing filter order<\/h2>\n<p class=\"body\">Filters run in a specific sequence: authorization, resource, action, or page, and then result. But if there are multiple filters of a given type, then the order in which they are applied is driven by the scope through which the filters have been applied.<\/p>\n<p class=\"body\">To demonstrate how this works, add a class file named <code class=\"fm-code-in-text\">MessageAttribute.cs<\/code> to the <code class=\"fm-code-in-text\">Filters<\/code> folder and use it to define the filter shown in listing 30.39.<a id=\"calibre_link-2649\"><\/a><a id=\"calibre_link-2650\"><\/a><a id=\"calibre_link-965\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.39 The contents of the MessageAttribute.cs file in the Filters folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Filters;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\n\nnamespace WebApp.Filters {\n\n    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,\n        AllowMultiple = true)]\n    public class MessageAttribute \n            : Attribute, IAsyncAlwaysRunResultFilter {\n        private int counter = 0;\n        private string msg;\n                \n        public MessageAttribute(string message) =&gt; msg = message;\n                \n        public async Task OnResultExecutionAsync(\n                ResultExecutingContext context,\n               ResultExecutionDelegate next) {\n            Dictionary&lt;string, string&gt; resultData;\n            if (context.Result is ViewResult vr\n                &amp;&amp; vr.ViewData.Model is Dictionary&lt;string, string&gt; data) {\n                resultData = data;\n            } else {\n                resultData = new Dictionary&lt;string, string&gt;();\n                context.Result = new ViewResult() {\n                    ViewName = \"\/Views\/Shared\/Message.cshtml\",\n                    ViewData = new ViewDataDictionary(\n                                        new EmptyModelMetadataProvider(),\n                                        new ModelStateDictionary()) {\n                        Model = resultData\n                    }\n                };\n            }\n            while (resultData.ContainsKey($\"Message_{counter}\")) {\n                counter++;\n            }\n            resultData[$\"Message_{counter}\"] = msg;\n            await next();\n        }\n    }\n}<\/pre>\n<p class=\"body\">This result filter uses techniques shown in earlier examples to replace the result from the endpoint and allows multiple filters to build up a series of messages that will be displayed to the user. Listing 30.40 applies several instances of the <code class=\"fm-code-in-text\">Message<\/code> filter to the <code class=\"fm-code-in-text\">Home<\/code> controller.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.40 Applying a filter in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Filters;\n\nnamespace WebApp.Controllers {\n\n    <b class=\"fm-bold\">[Message(\"This is the controller-scoped filter\")]<\/b>\n    public class HomeController : Controller {\n        \n        <b class=\"fm-bold\">[Message(\"This is the first action-scoped filter\")]<\/b>\n        <b class=\"fm-bold\">[Message(\"This is the second action-scoped filter\")]<\/b>\n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">Listing 30.41 registers the <code class=\"fm-code-in-text\">Message<\/code> filter globally.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.41 Creating a global filter in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing WebApp.Filters;\nusing Microsoft.AspNetCore.Mvc;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\n\nbuilder.Services.AddScoped&lt;GuidResponseAttribute&gt;();\n<b class=\"fm-bold\">builder.Services.Configure&lt;MvcOptions&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.Filters.Add&lt;HttpsOnlyAttribute&gt;();<\/b>\n    <b class=\"fm-bold\">opts.Filters.Add(new MessageAttribute(<\/b>\n        <b class=\"fm-bold\">\"This is the globally-scoped filter\"));<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapDefaultControllerRoute();\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">There are four instances of the same filter. To see the order in which they are applied, restart ASP.NET Core and request https:\/\/localhost:44350, which will produce the response shown in figure 30.16.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre334\" src=\"\/images\/proaspnetcore7\/000341.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.16 Applying the same filter in different scopes<\/p>\n<\/div>\n<p class=\"body\">By default, ASP.NET Core runs global filters, then filters applied to controllers or page model classes, and finally filters applied to action or handler methods.<\/p>\n<p class=\"body\">The default order can be changed by implementing the <code class=\"fm-code-in-text\">IOrderedFilter<\/code> interface, which ASP.NET Core looks for when it is working out how to sequence filters. Here is the definition of the interface:<\/p>\n<pre class=\"programlisting\">namespace Microsoft.AspNetCore.Mvc.Filters {\n\n    public interface IOrderedFilter : IFilterMetadata {\n        int Order { get; }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Order<\/code> property returns an <code class=\"fm-code-in-text\">int<\/code> value, and filters with low values are applied before those with higher <code class=\"fm-code-in-text\">Order<\/code> values. In listing 30.42, I have implemented the interface in the <code class=\"fm-code-in-text\">Message<\/code> filter and defined a constructor argument that will allow the value for the <code class=\"fm-code-in-text\">Order<\/code> property to be specified when the filter is applied.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.42 Adding ordering in the MessageAttribute.cs file in the Filters folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.Filters;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\nusing Microsoft.AspNetCore.Mvc.ViewFeatures;\n\nnamespace WebApp.Filters {\n\n    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,\n        AllowMultiple = true)]\n    <b class=\"fm-bold\">public class MessageAttribute : Attribute,<\/b> \n            <b class=\"fm-bold\">IAsyncAlwaysRunResultFilter,<\/b>\n            <b class=\"fm-bold\">IOrderedFilter {<\/b>\n                        \n        private int counter = 0;\n        private string msg;\n                \n        public MessageAttribute(string message) =&gt; msg = message;\n                \n        <b class=\"fm-bold\">public int Order { get; set; }<\/b>\n                \n        public async Task OnResultExecutionAsync(\n                ResultExecutingContext context,\n               ResultExecutionDelegate next) {\n            Dictionary&lt;string, string&gt; resultData;\n            if (context.Result is ViewResult vr\n                &amp;&amp; vr.ViewData.Model is Dictionary&lt;string, string&gt; data) {\n                resultData = data;\n            } else {\n                resultData = new Dictionary&lt;string, string&gt;();\n                context.Result = new ViewResult() {\n                    ViewName = \"\/Views\/Shared\/Message.cshtml\",\n                    ViewData = new ViewDataDictionary(\n                                        new EmptyModelMetadataProvider(),\n                                        new ModelStateDictionary()) {\n                        Model = resultData\n                    }\n                };\n            }\n            while (resultData.ContainsKey($\"Message_{counter}\")) {\n                counter++;\n            }\n            resultData[$\"Message_{counter}\"] = msg;\n            await next();\n        }\n    }\n}<\/pre>\n<p class=\"body\">In listing 30.43, I have used the constructor argument to change the order in which the filters are applied.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 30.43 Setting filter order in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing WebApp.Filters;\n\nnamespace WebApp.Controllers {\n\n    <b class=\"fm-bold\">[Message(\"This is the controller-scoped filter\", Order = 10)]<\/b>\n    public class HomeController : Controller {\n        \n        \n        <b class=\"fm-bold\">[Message(\"This is the first action-scoped filter\", Order = 1)]<\/b>\n        <b class=\"fm-bold\">[Message(\"This is the second action-scoped filter\", Order = -1)]<\/b>\n        public IActionResult Index() {\n            return View(\"Message\",\n                \"This is the Index action on the Home controller\");\n        }\n    }\n}<\/pre>\n<p class=\"body\"><code class=\"fm-code-in-text\">Order<\/code> values can be negative, which is a helpful way of ensuring that a filter is applied before any global filters with the default order (although you can also set the order when creating global filters, too). Restart ASP.NET Core and request https:\/\/localhost:44350 to see the new filter order, which is shown in figure 30.17.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre335\" src=\"\/images\/proaspnetcore7\/000342.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 30.17 Changing filter order<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2651\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Filters allow request processing logic to be defined in a class where it can be applied to specific endpoints and easily reused.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Filters have a well-defined lifecycle and can be used to perform the same tasks as middleware components.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">There are six types of filters: authorization, resource, action, page, result, and exception.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Authorization filters are used to implement a security policy.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Resource filters are executed before the model-binding process.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Action and page filters are executed after the model-binding process.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Result filters are executed before and after a result is used to generate a response.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Exception filters are executed when an exception is thrown.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-583\">\n<div class=\"calibre1\" id=\"calibre_link-2652\">\n<h1 class=\"tochead\" id=\"calibre_link-2653\">31 Creating form applications<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2654\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using ASP.NET Core form features with Entity Framework Core to create, read, update, and delete data<\/li>\n<li class=\"co-summary-bullet\">Managing the use of related data in Entity Framework Core results<\/li>\n<\/ul>\n<p class=\"body\">The previous chapters have focused on individual features that deal with one aspect of HTML forms, and it can sometimes be difficult to see how they fit together to perform common tasks. In this chapter, I go through the process of creating controllers, views, and Razor Pages that support an application with create, read, update, and delete (CRUD) functionality. There are no new features described in this chapter, and the objective is to demonstrate how features such as tag helpers, model binding, and model validation can be used in conjunction with Entity Framework Core.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-584\">31.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the WebApp project from chapter 30. To prepare for this chapter, replace the contents of the <code class=\"fm-code-in-text\">HomeController.cs<\/code> file in the <code class=\"fm-code-in-text\">Controllers<\/code> folder with those shown in listing 31.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.1 The contents of the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class HomeController : Controller {\n        private DataContext context;\n        private IEnumerable&lt;Category&gt; Categories =&gt; context.Categories;\n        private IEnumerable&lt;Supplier&gt; Suppliers =&gt; context.Suppliers;\n\n        public HomeController(DataContext data) {\n            context = data;\n        }\n \n        public IActionResult Index() {\n            return View(context.Products.\n                Include(p =&gt; p.Category).Include(p =&gt; p.Supplier));\n        }\n    }\n}<\/pre>\n<p class=\"body\">Create the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder and add to it a Razor View file named <code class=\"fm-code-in-text\">Index.cshtml<\/code> files, with the content shown in listing 31.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.2 The contents of the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model IEnumerable&lt;Product&gt;\n@{\n    Layout = \"_SimpleLayout\";\n}\n \n&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;Products&lt;\/h4&gt;\n&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;\n            &lt;th&gt;Name&lt;\/th&gt;\n            &lt;th&gt;Price&lt;\/th&gt;\n            &lt;th&gt;Category&lt;\/th&gt;\n            &lt;th&gt;&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (Product p in Model ?? Enumerable.Empty&lt;Product&gt;()) {\n            &lt;tr&gt;\n                &lt;td&gt;@p.ProductId&lt;\/td&gt;\n                &lt;td&gt;@p.Name&lt;\/td&gt;\n                &lt;td&gt;@p.Price&lt;\/td&gt;\n                &lt;td&gt;@p.Category?.Name&lt;\/td&gt;\n                &lt;td class=\"text-center\"&gt;\n                    &lt;a asp-action=\"Details\" asp-route-id=\"@p.ProductId\"\n                            class=\"btn btn-sm btn-info\"&gt;\n                        Details\n                    &lt;\/a&gt;\n                    &lt;a asp-action=\"Edit\" asp-route-id=\"@p.ProductId\"\n                            class=\"btn btn-sm btn-warning\"&gt;\n                        Edit\n                    &lt;\/a&gt;\n                    &lt;a asp-action=\"Delete\" asp-route-id=\"@p.ProductId\"\n                            class=\"btn btn-sm btn-danger\"&gt;\n                        Delete\n                    &lt;\/a&gt;\n                &lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n&lt;a asp-action=\"Create\" class=\"btn btn-primary\"&gt;Create&lt;\/a&gt;<\/pre>\n<p class=\"body\">Next, update the <code class=\"fm-code-in-text\">Product<\/code> class as shown in listing 31.3 to change the validation constraints to remove the model-level checking and disable remote validation.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.3 Changing validation in the Product.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Text.Json.Serialization;\nusing Microsoft.AspNetCore.Mvc.ModelBinding;\nusing WebApp.Validation;\nusing Microsoft.AspNetCore.Mvc;\n\nnamespace WebApp.Models {\n\n    <b class=\"fm-bold\">\/\/[PhraseAndPrice(Phrase = \"Small\", Price = \"100\")]<\/b>\n    public class Product {\n\n        public long ProductId { get; set; }\n                \n        [Required(ErrorMessage = \"Please enter a name\")]\n        public required string Name { get; set; }\n                \n        [Range(1, 999999, ErrorMessage = \"Please enter a positive price\")]\n        [Column(TypeName = \"decimal(8, 2)\")]\n        public decimal Price { get; set; }\n                \n        [PrimaryKey(ContextType = typeof(DataContext), \n            DataType = typeof(Category))]\n        <b class=\"fm-bold\">\/\/[Remote(\"CategoryKey\", \"Validation\",<\/b>\n        <b class=\"fm-bold\">\/\/    ErrorMessage = \"Enter an existing key\")]<\/b>\n                \n        public long CategoryId { get; set; }\n        public Category? Category { get; set; }\n                \n        [PrimaryKey(ContextType = typeof(DataContext), \n            DataType = typeof(Supplier))]\n        <b class=\"fm-bold\">\/\/[Remote(\"SupplierKey\", \"Validation\",<\/b> \n        <b class=\"fm-bold\">\/\/    ErrorMessage = \"Enter an existing key\")]<\/b>\n        public long SupplierId { get; set; }\n                \n        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n        public Supplier? Supplier { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Finally, disable the global filters in the <code class=\"fm-code-in-text\">Program.cs<\/code> file, as shown in listing 31.4. This listing also defines a route that makes it obvious when a URL targets a controller.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.4 Disabling filters in the Program.cs file in the WebApp folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\nusing WebApp.Filters;\n<b class=\"fm-bold\">\/\/using Microsoft.AspNetCore.Mvc;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:ProductConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddScoped&lt;GuidResponseAttribute&gt;();\n<b class=\"fm-bold\">\/\/builder.Services.Configure&lt;MvcOptions&gt;(opts =&gt; {<\/b>\n<b class=\"fm-bold\">\/\/    opts.Filters.Add&lt;HttpsOnlyAttribute&gt;();<\/b>\n<b class=\"fm-bold\">\/\/    opts.Filters.Add(new MessageAttribute(<\/b>\n<b class=\"fm-bold\">\/\/        \"This is the globally-scoped filter\"));<\/b>\n<b class=\"fm-bold\">\/\/});<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\napp.MapDefaultControllerRoute();\n<b class=\"fm-bold\">app.MapControllerRoute(\"forms\",<\/b> \n    <b class=\"fm-bold\">\"controllers\/{controller=Home}\/{action=Index}\/{id?}\");<\/b>\napp.MapRazorPages();\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-585\">31.1.1 Dropping the database<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">WebApp.csproj<\/code> file, and run the command shown in listing 31.5 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.5 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-586\">31.1.2 Running the example application<\/h3>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 31.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.6 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/controllers, which will display a list of products, as shown in figure 31.1. There are anchor elements styled to appear as buttons, but these will not work until later when I add the features to create, edit, and delete objects.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre336\" src=\"\/images\/proaspnetcore7\/000343.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 31.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-587\">31.2 Creating an MVC forms application<\/h2>\n<p class=\"body\">In the sections that follow, I show you how to perform the core data operations using MVC controllers and views. Later in the chapter, I create the same functionality using Razor Pages.<a id=\"calibre_link-2655\"><\/a><a id=\"calibre_link-970\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-588\">31.2.1 Preparing the view model and the view<\/h3>\n<p class=\"body\">I am going to define a single form that will be used for multiple operations, configured through its view model class. To create the view model class, add a class file named <code class=\"fm-code-in-text\">ProductViewModel.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and add the code shown in listing 31.7.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.7 The contents of the ProductViewModel.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n\n    public class ProductViewModel {\n        public Product Product { get; set; } \n            = new () { Name = string.Empty };\n\n        public string Action { get; set; } = \"Create\";\n                \n        public bool ReadOnly { get; set; } = false;\n                \n        public string Theme { get; set; } = \"primary\";\n                \n        public bool ShowAction { get; set; } = true;\n                \n        public IEnumerable&lt;Category&gt; Categories { get; set; }\n            = Enumerable.Empty&lt;Category&gt;();\n                        \n        public IEnumerable&lt;Supplier&gt; Suppliers { get; set; }\n            = Enumerable.Empty&lt;Supplier&gt;();\n    }\n}<\/pre>\n<p class=\"body\">This class will allow the controller to pass data and display settings to its view. The <code class=\"fm-code-in-text\">Product<\/code> property provides the data to display, and the <code class=\"fm-code-in-text\">Categories<\/code> and <code class=\"fm-code-in-text\">Suppliers<\/code> properties provide access to the <code class=\"fm-code-in-text\">Category<\/code> and <code class=\"fm-code-in-text\">Suppliers<\/code> objects when they are required. The other properties configure aspects of how the content is presented to the user: the <code class=\"fm-code-in-text\">Action<\/code> property specifies the name of the action method for the current task, the <code class=\"fm-code-in-text\">ReadOnly<\/code> property specifies whether the user can edit the data, the <code class=\"fm-code-in-text\">Theme<\/code> property specifies the Bootstrap theme for the content, and the <code class=\"fm-code-in-text\">ShowAction<\/code> property is used to control the visibility of the button that submits the form.<\/p>\n<p class=\"body\">To create the view that will allow the user to interact with the application\u2019s data, add a Razor View named <code class=\"fm-code-in-text\">ProductEditor.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder with the content shown in listing 31.8.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.8 The contents of the ProductEditor.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model ProductViewModel\n@{\n    Layout = \"_SimpleLayout\";\n}\n\n&lt;partial name=\"_Validation\" \/&gt;\n\n&lt;h5 class=\"bg-@Model?.Theme text-white text-center p-2\"&gt;\n    @Model?.Action\n&lt;\/h5&gt;\n\n&lt;form asp-action=\"@Model?.Action\" method=\"post\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Product.ProductId\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" \n            asp-for=\"Product.ProductId\" readonly \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Product.Name\"&gt;&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"Product.Name\" class=\"text-danger\"&gt;\n            &lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;input class=\"form-control\" asp-for=\"Product.Name\"\n               readonly=\"@Model?.ReadOnly\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Product.Price\"&gt;&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"Product.Price\" class=\"text-danger\"&gt;\n            &lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;input class=\"form-control\" asp-for=\"Product.Price\"\n               readonly=\"@Model?.ReadOnly\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Product.CategoryId\"&gt;Category&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"Product.CategoryId\" \n                class=\"text-danger\"&gt;&lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;select asp-for=\"Product.CategoryId\" class=\"form-control\"\n                disabled=\"@Model?.ReadOnly\"\n                asp-items=\"@(new SelectList(Model?.Categories,\n                    \"CategoryId\", \"Name\"))\"&gt;\n            &lt;option value=\"\" disabled selected&gt;Choose a Category&lt;\/option&gt;\n        &lt;\/select&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Product.SupplierId\"&gt;Supplier&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"Product.SupplierId\" \n                class=\"text-danger\"&gt;&lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;select asp-for=\"Product.SupplierId\" class=\"form-control\"\n                disabled=\"@Model?.ReadOnly\"\n                asp-items=\"@(new SelectList(Model?.Suppliers,\n                    \"SupplierId\", \"Name\"))\"&gt;\n            &lt;option value=\"\" disabled selected&gt;Choose a Supplier&lt;\/option&gt;\n        &lt;\/select&gt;\n    &lt;\/div&gt;\n    @if (Model?.ShowAction == true) {\n        &lt;button class=\"btn btn-@Model?.Theme mt-2\" type=\"submit\"&gt;\n            @Model?.Action\n        &lt;\/button&gt;\n    }\n    &lt;a class=\"btn btn-secondary mt-2\" asp-action=\"Index\"&gt;Back&lt;\/a&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">This view can look complicated, but it combines only the features you have seen in earlier chapters and will become clearer once you see it in action. The model for this view is a <code class=\"fm-code-in-text\">ProductViewModel<\/code> object, which provides both the data that is displayed to the user and some direction about how that data should be presented.<\/p>\n<p class=\"body\">For each of the properties defined by the <code class=\"fm-code-in-text\">Product<\/code> class, the view contains a set of elements: a <code class=\"fm-code-in-text\">label<\/code> element that describes the property, an <code class=\"fm-code-in-text\">input<\/code> or <code class=\"fm-code-in-text\">select<\/code> element that allows the value to be edited, and a <code class=\"fm-code-in-text\">span<\/code> element that will display validation messages. Each of the elements is configured with the <code class=\"fm-code-in-text\">asp-for<\/code> attribute, which ensures tag helpers will transform the elements for each property. There are <code class=\"fm-code-in-text\">div<\/code> elements to define the view structure, and all the elements are members of Bootstrap CSS classes to style the form.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-589\">31.2.2 Reading data<\/h3>\n<p class=\"body\">The simplest operation is reading data from the database and presenting it to the user. In most applications, this will allow the user to see additional details that are not present in the list view. Each task performed by the application will require a different set of <code class=\"fm-code-in-text\">ProductViewModel<\/code> properties. To manage these combinations, add a class file named <code class=\"fm-code-in-text\">ViewModelFactory.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder with the code shown in listing 31.9.<a id=\"calibre_link-2656\"><\/a><a id=\"calibre_link-996\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.9 The contents of the ViewModelFactory.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n\n    public static class ViewModelFactory {\n        \n        public static ProductViewModel Details(Product p) {\n            return new ProductViewModel {\n                Product = p, Action = \"Details\",\n                ReadOnly = true, Theme = \"info\", ShowAction = false,\n                Categories = p == null || p.Category == null \n                    ? Enumerable.Empty&lt;Category&gt;()\n                    : new List&lt;Category&gt; { p.Category },\n                Suppliers = p == null || p.Supplier == null \n                    ? Enumerable.Empty&lt;Supplier&gt;()\n                    : new List&lt;Supplier&gt; { p.Supplier },\n            };\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Details<\/code> method produces a <code class=\"fm-code-in-text\">ProductViewModel<\/code> object configured for viewing an object. When the user views the details, the category and supplier details will be read-only, which means that I need to provide only the current category and supplier information.<\/p>\n<p class=\"body\">Next, add an action method to the <code class=\"fm-code-in-text\">Home<\/code> controller that uses the <code class=\"fm-code-in-text\">ViewModelFactory.Details<\/code> method to create a <code class=\"fm-code-in-text\">ProductViewModel<\/code> object and display it to the user with the <code class=\"fm-code-in-text\">ProductEditor<\/code> view, as shown in listing 31.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.10 Adding an action in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class HomeController : Controller {\n        private DataContext context;\n        private IEnumerable&lt;Category&gt; Categories =&gt; context.Categories;\n        private IEnumerable&lt;Supplier&gt; Suppliers =&gt; context.Suppliers;\n\n        public HomeController(DataContext data) {\n            context = data;\n        }\n \n        public IActionResult Index() {\n            return View(context.Products.\n                Include(p =&gt; p.Category).Include(p =&gt; p.Supplier));\n        }\n                \n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; Details(long id) {<\/b>\n            <b class=\"fm-bold\">Product? p = await context.Products.<\/b>\n                <b class=\"fm-bold\">Include(p =&gt; p.Category).Include(p =&gt; p.Supplier)<\/b>\n                <b class=\"fm-bold\">.FirstOrDefaultAsync(p =&gt; p.ProductId == id)<\/b> \n                    <b class=\"fm-bold\">?? new () { Name = string.Empty };<\/b>\n            <b class=\"fm-bold\">ProductViewModel model = ViewModelFactory.Details(p);<\/b>\n            <b class=\"fm-bold\">return View(\"ProductEditor\", model);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The action method uses the <code class=\"fm-code-in-text\">id<\/code> parameter, which will be model bound from the routing data, to query the database and passes the <code class=\"fm-code-in-text\">Product<\/code> object to the <code class=\"fm-code-in-text\">ViewModelFactory.Details<\/code> method. Most of the operations are going to require the <code class=\"fm-code-in-text\">Category<\/code> and <code class=\"fm-code-in-text\">Supplier<\/code> data, so I have added properties that provide direct access to the data.<\/p>\n<p class=\"body\">To test the details feature, restart ASP.NET Core and request http:\/\/localhost:5000\/controllers. Click one of the Details buttons, and you will see the selected object presented in read-only form using the <code class=\"fm-code-in-text\">ProductEditor<\/code> view, as shown in figure 31.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre337\" src=\"\/images\/proaspnetcore7\/000344.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 31.2 Viewing data<\/p>\n<\/div>\n<p class=\"body\">If the user navigates to a URL that doesn\u2019t correspond to an object in the database, such as http:\/\/localhost:5000\/controllers\/Home\/Details\/100, for example, then an empty form will be displayed.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-590\">31.2.3 Creating data<\/h3>\n<p class=\"body\">Creating data relies on model binding to get the form data from the request and relies on validation to ensure the data can be stored in the database. The first step is to add a factory method that will create the view model object for creating data, as shown in listing 31.11.<a id=\"calibre_link-2657\"><\/a><a id=\"calibre_link-972\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.11 Adding a method in the ViewModelFactory.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n\n    public static class ViewModelFactory {\n \n        public static ProductViewModel Details(Product p) {\n            return new ProductViewModel {\n                Product = p, Action = \"Details\",\n                ReadOnly = true, Theme = \"info\", ShowAction = false,\n                Categories = p == null || p.Category == null\n                    ? Enumerable.Empty&lt;Category&gt;()\n                    : new List&lt;Category&gt; { p.Category },\n                Suppliers = p == null || p.Supplier == null\n                    ? Enumerable.Empty&lt;Supplier&gt;()\n                    : new List&lt;Supplier&gt; { p.Supplier },\n            };\n        }\n \n        <b class=\"fm-bold\">public static ProductViewModel Create(Product product,<\/b>\n                <b class=\"fm-bold\">IEnumerable&lt;Category&gt; categories,<\/b> \n                <b class=\"fm-bold\">IEnumerable&lt;Supplier&gt; suppliers) {<\/b>\n            <b class=\"fm-bold\">return new ProductViewModel {<\/b>\n                <b class=\"fm-bold\">Product = product, Categories = categories,<\/b> \n                <b class=\"fm-bold\">Suppliers = suppliers<\/b>\n            <b class=\"fm-bold\">};<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The defaults I used for the <code class=\"fm-code-in-text\">ProductViewModel<\/code> properties were set for creating data, so the <code class=\"fm-code-in-text\">Create<\/code> method in listing 31.11 sets only the <code class=\"fm-code-in-text\">Product<\/code>, <code class=\"fm-code-in-text\">Categories<\/code>, and <code class=\"fm-code-in-text\">Suppliers<\/code> properties. Listing 31.12 adds the action methods that will create data to the <code class=\"fm-code-in-text\">Home<\/code> controller.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.12 Adding actions in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class HomeController : Controller {\n        private DataContext context;\n        private IEnumerable&lt;Category&gt; Categories =&gt; context.Categories;\n        private IEnumerable&lt;Supplier&gt; Suppliers =&gt; context.Suppliers;\n\n        public HomeController(DataContext data) {\n            context = data;\n        }\n \n        public IActionResult Index() {\n            return View(context.Products.\n                Include(p =&gt; p.Category).Include(p =&gt; p.Supplier));\n        }\n \n        public async Task&lt;IActionResult&gt; Details(long id) {\n            Product? p = await context.Products.\n                Include(p =&gt; p.Category).Include(p =&gt; p.Supplier)\n                .FirstOrDefaultAsync(p =&gt; p.ProductId == id) \n                    ?? new () { Name = string.Empty };\n            ProductViewModel model = ViewModelFactory.Details(p);\n            return View(\"ProductEditor\", model);\n        }\n \n        <b class=\"fm-bold\">public IActionResult Create() {<\/b>\n            <b class=\"fm-bold\">return View(\"ProductEditor\",<\/b>\n                <b class=\"fm-bold\">ViewModelFactory.Create(new () { Name = string.Empty },<\/b> \n                    <b class=\"fm-bold\">Categories, Suppliers));<\/b>\n        <b class=\"fm-bold\">}<\/b>\n\n        <b class=\"fm-bold\">[HttpPost]<\/b>\n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; Create(<\/b>\n                <b class=\"fm-bold\">[FromForm] Product product) {<\/b>\n            <b class=\"fm-bold\">if (ModelState.IsValid) {<\/b>\n                <b class=\"fm-bold\">product.ProductId = default;<\/b>\n                <b class=\"fm-bold\">product.Category = default;<\/b>\n                <b class=\"fm-bold\">product.Supplier = default;<\/b>\n                <b class=\"fm-bold\">context.Products.Add(product);<\/b>\n                <b class=\"fm-bold\">await context.SaveChangesAsync();<\/b>\n                <b class=\"fm-bold\">return RedirectToAction(nameof(Index));<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return View(\"ProductEditor\",<\/b>\n                <b class=\"fm-bold\">ViewModelFactory.Create(product, Categories, Suppliers));<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">There are two <code class=\"fm-code-in-text\">Create<\/code> methods, which are differentiated by the <code class=\"fm-code-in-text\">HttpPost<\/code> attribute and method parameters. HTTP GET requests will be handled by the first method, which selects the <code class=\"fm-code-in-text\">ProductEditor<\/code> view and provides it with a <code class=\"fm-code-in-text\">ProductViewModel<\/code> object. When the user submits the form, it will be received by the second method, which relies on model binding to receive the data and model validation to ensure the data is valid.<a id=\"calibre_link-2658\"><\/a><a id=\"calibre_link-2659\"><\/a><a id=\"calibre_link-946\"><\/a><\/p>\n<p class=\"body\">If the data passes validation, then I prepare the object for storage in the database by resetting three properties, like this:<\/p>\n<pre class=\"programlisting\">...\nproduct.ProductId = default;\nproduct.Category = default;\nproduct.Supplier = default;\n...<\/pre>\n<p class=\"body\">Entity Framework Core configures the database so that primary keys are allocated by the database server when new data is stored. If you attempt to store an object and provide a <code class=\"fm-code-in-text\">ProductId<\/code> value other than zero, then an exception will be thrown.<\/p>\n<p class=\"body\">I reset the <code class=\"fm-code-in-text\">Category<\/code> and <code class=\"fm-code-in-text\">Supplier<\/code> properties to prevent Entity Framework Core from trying to deal with related data when storing an object. Entity Framework Core is capable of processing related data, but it can produce unexpected outcomes. (I show you how to create related data in the \u201cCreating New Related Data Objects\u201d section, later in this chapter.)<\/p>\n<p class=\"body\">Notice I call the <code class=\"fm-code-in-text\">View<\/code> method with arguments when validation fails, like this:<a id=\"calibre_link-2660\"><\/a><a id=\"calibre_link-2661\"><\/a><a id=\"calibre_link-1064\"><\/a><\/p>\n<pre class=\"programlisting\">...\nreturn View(\"ProductEditor\", \n    <b class=\"fm-bold\">ViewModelFactory.Create(product, Categories, Suppliers)<\/b>);\n...<\/pre>\n<p class=\"body\">I do this because the view model object expected by the view isn\u2019t the same data type that I have extracted from the request using model binding. Instead, I create a new view model object that incorporates the model bound data and passes this to the <code class=\"fm-code-in-text\">View<\/code> method.<\/p>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000\/controllers, and click Create. Fill out the form and click the Create button to submit the data. The new object will be stored in the database and displayed when the browser is redirected to the <code class=\"fm-code-in-text\">Index<\/code> action, as shown in figure 31.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre338\" src=\"\/images\/proaspnetcore7\/000345.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 31.3 Creating a new object<\/p>\n<\/div>\n<p class=\"body\">Notice that <code class=\"fm-code-in-text\">select<\/code> elements allow the user to select the values for the <code class=\"fm-code-in-text\">CategoryId<\/code> and <code class=\"fm-code-in-text\">SupplierId<\/code> properties, using the category and supplier names, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;select asp-for=\"Product.SupplierId\" class=\"form-control\" \n        disabled=\"@Model?.ReadOnly\"\n        asp-items=\"@(new SelectList(Model?.Suppliers, \n            \"SupplierId\", \"Name\"))\"&gt;\n    &lt;option value=\"\" disabled selected&gt;Choose a Supplier&lt;\/option&gt;\n&lt;\/select&gt;\n...<\/pre>\n<p class=\"body\">In chapter 30, I used <code class=\"fm-code-in-text\">input<\/code> elements to allow the value of these properties to be set directly, but that was because I wanted to demonstrate different types of validation. In real applications, it is a good idea to provide the user with restricted choices when the application already has the data it expects the user to choose from. Making the user enter a valid primary key, for example, makes no sense in a real project because the application can easily provide the user with a list of those keys to choose from, as shown in figure 31.4.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> I show you different techniques for creating related data in the \u201cCreating new related data objects\u201d section.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre339\" src=\"\/images\/proaspnetcore7\/000346.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 31.4 Presenting the user with a choice<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-591\">31.2.4 Editing data<\/h3>\n<p class=\"body\">The process for editing data is similar to creating data. The first step is to add a new method to the view model factory that will configure the way the data is presented to the user, as shown in listing 31.13.<a id=\"calibre_link-2662\"><\/a><a id=\"calibre_link-982\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.13 Adding a method in the ViewModelFactory.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n\n    public static class ViewModelFactory {\n \n        public static ProductViewModel Details(Product p) {\n            return new ProductViewModel {\n                Product = p, Action = \"Details\",\n                ReadOnly = true, Theme = \"info\", ShowAction = false,\n                Categories = p == null || p.Category == null\n                    ? Enumerable.Empty&lt;Category&gt;()\n                    : new List&lt;Category&gt; { p.Category },\n                Suppliers = p == null || p.Supplier == null\n                    ? Enumerable.Empty&lt;Supplier&gt;()\n                    : new List&lt;Supplier&gt; { p.Supplier },\n            };\n        }\n \n        public static ProductViewModel Create(Product product,\n                IEnumerable&lt;Category&gt; categories, \n                IEnumerable&lt;Supplier&gt; suppliers) {\n            return new ProductViewModel {\n                Product = product, Categories = categories, \n                Suppliers = suppliers\n            };\n        }\n \n        <b class=\"fm-bold\">public static ProductViewModel Edit(Product product,<\/b>\n                <b class=\"fm-bold\">IEnumerable&lt;Category&gt; categories,<\/b> \n                <b class=\"fm-bold\">IEnumerable&lt;Supplier&gt; suppliers) {<\/b>\n            <b class=\"fm-bold\">return new ProductViewModel {<\/b>\n                <b class=\"fm-bold\">Product = product, Categories = categories,<\/b> \n                <b class=\"fm-bold\">Suppliers = suppliers,<\/b>\n                <b class=\"fm-bold\">Theme = \"warning\", Action = \"Edit\"<\/b>\n            <b class=\"fm-bold\">};<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The next step is to add the action methods to the <code class=\"fm-code-in-text\">Home<\/code> controller that will display the current properties of a <code class=\"fm-code-in-text\">Product<\/code> object to the user and receive the changes the user makes, as shown in listing 31.14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.14 Adding action methods in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class HomeController : Controller {\n        private DataContext context;\n                \n        private IEnumerable&lt;Category&gt; Categories =&gt; context.Categories;\n        private IEnumerable&lt;Supplier&gt; Suppliers =&gt; context.Suppliers;\n\n        public HomeController(DataContext data) {\n            context = data;\n        }\n \n        \/\/ ...other action methods omitted for brevity...\n \n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; Edit(long id) {<\/b>\n            <b class=\"fm-bold\">Product? p = await context.Products.FindAsync(id);<\/b>\n            <b class=\"fm-bold\">if (p != null) {<\/b>\n                <b class=\"fm-bold\">ProductViewModel model =<\/b> \n                    <b class=\"fm-bold\">ViewModelFactory.Edit(p, Categories, Suppliers);<\/b>\n                <b class=\"fm-bold\">return View(\"ProductEditor\", model);<\/b>\n            <b class=\"fm-bold\">}<\/b> \n            <b class=\"fm-bold\">return NotFound();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n\n        <b class=\"fm-bold\">[HttpPost]<\/b>\n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; Edit(<\/b>\n                <b class=\"fm-bold\">[FromForm] Product product) {<\/b>\n            <b class=\"fm-bold\">if (ModelState.IsValid) {<\/b>\n                <b class=\"fm-bold\">product.Category = default;<\/b>\n                <b class=\"fm-bold\">product.Supplier = default;<\/b>\n                <b class=\"fm-bold\">context.Products.Update(product);<\/b>\n                <b class=\"fm-bold\">await context.SaveChangesAsync();<\/b>\n                <b class=\"fm-bold\">return RedirectToAction(nameof(Index));<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return View(\"ProductEditor\",<\/b>\n                <b class=\"fm-bold\">ViewModelFactory.Edit(product, Categories, Suppliers));<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">To see the editing feature at work, restart ASP.NET Core, navigate to http:\/\/localhost:5000\/controllers, and click one of the Edit buttons. Change one or more property values and submit the form. The changes will be stored in the database and reflected in the list displayed when the browser is redirected to the <code class=\"fm-code-in-text\">Index<\/code> action, as shown in figure 31.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre340\" src=\"\/images\/proaspnetcore7\/000347.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 31.5 Editing a product<\/p>\n<\/div>\n<p class=\"body\"><a id=\"calibre_link-936\"><\/a>Notice that the <code class=\"fm-code-in-text\">ProductId<\/code> property cannot be changed. Attempting to change the primary key of an object should be avoided because it interferes with the Entity Framework Core understanding of the identity of its objects. If you can\u2019t avoid changing the primary key, then the safest approach is to delete the existing object and store a new one.<a id=\"calibre_link-2663\"><\/a><a id=\"calibre_link-2664\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-592\">31.2.5 Deleting data<\/h3>\n<p class=\"body\">The final basic operation is removing objects from the database. By now the pattern will be clear, and the first step is to add a method to create a view model object to determine how the data is presented to the user, as shown in listing 31.15.<a id=\"calibre_link-2665\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.15 Adding a method in the ViewModelFactory.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace WebApp.Models {\n\n    public static class ViewModelFactory {\n \n        \/\/ ...<i class=\"fm-italics\">other action methods omitted for brevity<\/i>...\n \n        <b class=\"fm-bold\">public static ProductViewModel Delete(Product p,<\/b>\n                <b class=\"fm-bold\">IEnumerable&lt;Category&gt; categories,<\/b> \n                <b class=\"fm-bold\">IEnumerable&lt;Supplier&gt; suppliers) {<\/b>\n            <b class=\"fm-bold\">return new ProductViewModel {<\/b>\n                <b class=\"fm-bold\">Product = p, Action = \"Delete\",<\/b>\n                <b class=\"fm-bold\">ReadOnly = true, Theme = \"danger\",<\/b>\n                <b class=\"fm-bold\">Categories = categories, Suppliers = suppliers<\/b>\n            <b class=\"fm-bold\">};<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Listing 31.16 adds the action methods to the <code class=\"fm-code-in-text\">Home<\/code> controller that will respond to the GET request by displaying the selected object and the POST request to remove that object from the database.<a id=\"calibre_link-980\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.16 Adding action methods in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing WebApp.Models;\n\nnamespace WebApp.Controllers {\n\n    [AutoValidateAntiforgeryToken]\n    public class HomeController : Controller {\n        private DataContext context;\n\n        private IEnumerable&lt;Category&gt; Categories =&gt; context.Categories;\n        private IEnumerable&lt;Supplier&gt; Suppliers =&gt; context.Suppliers;\n\n        public HomeController(DataContext data) {\n            context = data;\n        }\n \n        \/\/ ...<i class=\"calibre341\">other action methods omitted for brevity<\/i>...\n \n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; Delete(long id) {<\/b>\n            <b class=\"fm-bold\">Product? p = await context.Products.FindAsync(id);<\/b>\n            <b class=\"fm-bold\">if (p != null) {<\/b>\n                <b class=\"fm-bold\">ProductViewModel model = ViewModelFactory.Delete(<\/b>\n                    <b class=\"fm-bold\">p, Categories, Suppliers);<\/b>\n                <b class=\"fm-bold\">return View(\"ProductEditor\", model);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return NotFound();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n\n        <b class=\"fm-bold\">[HttpPost]<\/b>\n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; Delete(Product product) {<\/b>\n            <b class=\"fm-bold\">context.Products.Remove(product);<\/b>\n            <b class=\"fm-bold\">await context.SaveChangesAsync();<\/b>\n            <b class=\"fm-bold\">return RedirectToAction(nameof(Index));<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The model binding process creates a <code class=\"fm-code-in-text\">Product<\/code> object from the form data, which is passed to Entity Framework Core to remove from the database. Once the data has been removed from the database, the browser is redirected to the <code class=\"fm-code-in-text\">Index<\/code> action, as shown in figure 31.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre342\" src=\"\/images\/proaspnetcore7\/000348.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 31.6 Deleting data<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-593\">31.3 Creating a Razor Pages forms application<\/h2>\n<p class=\"body\">Working with Razor Forms relies on similar techniques as the controller examples, albeit broken up into smaller chunks of functionality. As you will see, the main difficulty is preserving the modular nature of Razor Pages without duplicating code and markup. The first step is to create the Razor Page that will display the list of <code class=\"fm-code-in-text\">Product<\/code> objects and provide the links to the other operations. Add a Razor Page named <code class=\"fm-code-in-text\">Index.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 31.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.17 The contents of the Index.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/{id:long?}\"\n@model IndexModel\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using Microsoft.EntityFrameworkCore\n\n&lt;div class=\"m-2\"&gt;\n    &lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;Products&lt;\/h4&gt;\n    &lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n        &lt;thead&gt;\n            &lt;tr&gt;\n                &lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Price&lt;\/th&gt;\n                &lt;th&gt;Category&lt;\/th&gt;&lt;th&gt;&lt;\/th&gt;\n            &lt;\/tr&gt;\n        &lt;\/thead&gt;\n        &lt;tbody&gt;\n            @foreach (Product p in Model.Products) {\n                &lt;tr&gt;\n                    &lt;td&gt;@p.ProductId&lt;\/td&gt;\n                    &lt;td&gt;@p.Name&lt;\/td&gt;\n                    &lt;td&gt;@p.Price&lt;\/td&gt;\n                    &lt;td&gt;@p.Category?.Name&lt;\/td&gt;\n                    &lt;td class=\"text-center\"&gt;\n                        &lt;a asp-page=\"Details\" asp-route-id=\"@p.ProductId\"\n                           class=\"btn btn-sm btn-info\"&gt;Details&lt;\/a&gt;\n                        &lt;a asp-page=\"Edit\" asp-route-id=\"@p.ProductId\"\n                           class=\"btn btn-sm btn-warning\"&gt;Edit&lt;\/a&gt;\n                        &lt;a asp-page=\"Delete\" asp-route-id=\"@p.ProductId\"\n                           class=\"btn btn-sm btn-danger\"&gt;Delete&lt;\/a&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n    &lt;a asp-page=\"Create\" class=\"btn btn-primary\"&gt;Create&lt;\/a&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class IndexModel: PageModel {\n        private DataContext context;\n\n        public IndexModel(DataContext dbContext) {\n            context = dbContext;\n        }\n \n        public IEnumerable&lt;Product&gt; Products { get; set; }\n            = Enumerable.Empty&lt;Product&gt;();\n        public void OnGetAsync(long id = 1) {\n            Products = context.Products\n                .Include(p =&gt; p.Category).Include(p =&gt; p.Supplier);\n        }\n    }\n}<\/pre>\n<p class=\"body\">This view part of the page displays a table populated with the details of the <code class=\"fm-code-in-text\">Product<\/code> objects obtained from the database by the page model. Use a browser to request http:\/\/localhost:5000\/pages, and you will see the response shown in figure 31.7. Alongside the details of the <code class=\"fm-code-in-text\">Product<\/code> objects, the page displays anchor elements that navigate to other Razor Pages, which I define in the sections that follow.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre343\" src=\"\/images\/proaspnetcore7\/000349.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 31.7 Listing data using a Razor Page<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-594\">31.3.1 Creating common functionality<\/h3>\n<p class=\"body\">I don\u2019t want to duplicate the same HTML form and supporting code in each of the pages required by the example application. Instead, I am going to define a partial view that defines the HTML form and a base class that defines the common code required by the page model classes. For the partial view, add a Razor View named <code class=\"fm-code-in-text\">_ProductEditor.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 31.18.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Using multiple page handler methods<\/p>\n<p class=\"fm-sidebar-text\">The <code class=\"fm-code-in-text1\">asp-page-handler<\/code> attribute can be used to specify the name of a handler method, which allows a Razor Page to be used for more than one operation. I don\u2019t like this feature because the result is too close to a standard MVC controller and undermines the self-contained and modular aspects of Razor Page development that I like.<\/p>\n<p class=\"fm-sidebar-text\">The approach I prefer is, of course, the one that I have taken in this chapter, which is to consolidate common content in partial views and a shared base class. Either approach works, and I recommend you try both to see which suits you and your project.<a id=\"calibre_link-2666\"><\/a><a id=\"calibre_link-1103\"><\/a><\/p>\n<\/div>\n<p class=\"fm-code-listing-caption\">Listing 31.18 The contents of the _ProductEditor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@model ProductViewModel\n\n&lt;partial name=\"_Validation\" \/&gt;\n\n&lt;h5 class=\"bg-@Model?.Theme text-white text-center p-2\"&gt;@Model?.Action&lt;\/h5&gt;\n\n&lt;form asp-page=\"@Model?.Action\" method=\"post\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Product.ProductId\"&gt;&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"Product.ProductId\" \n            readonly \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Product.Name\"&gt;&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"Product.Name\" class=\"text-danger\"&gt;\n            &lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;input class=\"form-control\" asp-for=\"Product.Name\"\n               readonly=\"@Model?.ReadOnly\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Product.Price\"&gt;&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"Product.Price\" class=\"text-danger\"&gt;\n            &lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;input class=\"form-control\" asp-for=\"Product.Price\"\n               readonly=\"@Model?.ReadOnly\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Product.CategoryId\"&gt;Category&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"Product.CategoryId\" \n                class=\"text-danger\"&gt;\n            &lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;select asp-for=\"Product.CategoryId\" class=\"form-control\"\n                disabled=\"@Model?.ReadOnly\"\n                asp-items=\"@(new SelectList(Model?.Categories,\n                    \"CategoryId\", \"Name\"))\"&gt;\n            &lt;option value=\"\" disabled selected&gt;Choose a Category&lt;\/option&gt;\n        &lt;\/select&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label asp-for=\"Product.SupplierId\"&gt;Supplier&lt;\/label&gt;\n        &lt;div&gt;\n            &lt;span asp-validation-for=\"Product.SupplierId\" \n                class=\"text-danger\"&gt;\n            &lt;\/span&gt;\n        &lt;\/div&gt;\n        &lt;select asp-for=\"Product.SupplierId\" class=\"form-control\"\n                disabled=\"@Model?.ReadOnly\"\n                asp-items=\"@(new SelectList(Model?.Suppliers,\n                    \"SupplierId\", \"Name\"))\"&gt;\n            &lt;option value=\"\" disabled selected&gt;Choose a Supplier&lt;\/option&gt;\n        &lt;\/select&gt;\n    &lt;\/div&gt;\n    @if (Model?.ShowAction == true) {\n        &lt;button class=\"btn btn-@Model.Theme mt-2\" type=\"submit\"&gt;\n            @Model.Action\n        &lt;\/button&gt;\n    }\n    &lt;a class=\"btn btn-secondary mt-2\" asp-page=\"Index\"&gt;Back&lt;\/a&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">The partial view uses the <code class=\"fm-code-in-text\">ProductViewModel<\/code> class as its model type and relies on the built-in tag helpers to present <code class=\"fm-code-in-text\">input<\/code> and <code class=\"fm-code-in-text\">select<\/code> elements for the properties defined by the <code class=\"fm-code-in-text\">Product<\/code> class. This is the same content used earlier in the chapter, except with the <code class=\"fm-code-in-text\">asp-action<\/code> attribute replaced with <code class=\"fm-code-in-text\">asp-page<\/code> to specify the target for the <code class=\"fm-code-in-text\">form<\/code> and <code class=\"fm-code-in-text\">anchor<\/code> elements.<\/p>\n<p class=\"body\">To define the page model base class, add a class file named <code class=\"fm-code-in-text\">EditorPageModel.cs<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder and use it to define the class shown in listing 31.19.<a id=\"calibre_link-2667\"><\/a><a id=\"calibre_link-1096\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.19 The contents of the EditorPageModel.cs file in the Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.RazorPages;\nusing WebApp.Models;\n\nnamespace WebApp.Pages {\n\n    public class EditorPageModel : PageModel {\n\n        public EditorPageModel(DataContext dbContext) {\n            DataContext = dbContext;\n        }\n \n        public DataContext DataContext { get; set; }\n                \n        public IEnumerable&lt;Category&gt; Categories =&gt; DataContext.Categories;\n        public IEnumerable&lt;Supplier&gt; Suppliers =&gt; DataContext.Suppliers;\n                \n        public ProductViewModel? ViewModel { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">The properties defined by this class are simple, but they will help simplify the page model classes of the Razor Pages that handle each operation.<\/p>\n<p class=\"body\">All the Razor Pages required for this example depend on the same namespaces. Add the expressions shown in listing 31.20 to the <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Pages<\/code> folder to avoid duplicate expressions in the individual pages.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Make sure you alter the <code class=\"fm-code-in-text1\">_ViewImports.cshtml<\/code> file in the <code class=\"fm-code-in-text1\">Pages<\/code> folder and not the file with the same name in the <code class=\"fm-code-in-text1\">Views<\/code> folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.20 Adding namespaces in the _ViewImports.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@namespace WebApp.Pages\n@using WebApp.Models\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n@addTagHelper *, WebApp\n<b class=\"fm-bold\">@using Microsoft.AspNetCore.Mvc.RazorPages<\/b>\n<b class=\"fm-bold\">@using Microsoft.EntityFrameworkCore<\/b>\n<b class=\"fm-bold\">@using System.Text.Json<\/b>\n<b class=\"fm-bold\">@using Microsoft.AspNetCore.Http<\/b><\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-595\">31.3.2 Defining pages for the CRUD operations<\/h3>\n<p class=\"body\">With the partial view and shared base class in place, the pages that handle individual operations are simple. Add a Razor Page named <code class=\"fm-code-in-text\">Details.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the code and content shown in listing 31.21.<a id=\"calibre_link-2668\"><\/a><a id=\"calibre_link-973\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.21 The contents of the Details.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/details\/{id}\"\n@model DetailsModel\n\n&lt;div class=\"m-2\"&gt;\n    &lt;partial name=\"_ProductEditor\" model=\"@Model.ViewModel\" \/&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class DetailsModel : EditorPageModel {\n        \n        public DetailsModel(DataContext dbContext) : base(dbContext) { }\n                \n        public async Task OnGetAsync(long id) {\n            Product? p = await DataContext.Products.\n                Include(p =&gt; p.Category).Include(p =&gt; p.Supplier)\n                .FirstOrDefaultAsync(p =&gt; p.ProductId == id);\n            ViewModel = ViewModelFactory.Details(p \n                    ?? new () { Name = string.Empty});\n        }\n    }\n}<\/pre>\n<p class=\"body\">The constructor receives an Entity Framework Core context object, which it passes to the base class. The handler method responds to requests by querying the database and using the response to create a <code class=\"fm-code-in-text\">ProductViewModel<\/code> object using the <code class=\"fm-code-in-text\">ViewModelFactory<\/code> class.<\/p>\n<p class=\"body\">Add a Razor Page named <code class=\"fm-code-in-text\">Create.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the code and content shown in listing 31.22.<a id=\"calibre_link-2669\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Using a partial view means that the <code class=\"fm-code-in-text1\">asp-for<\/code> attributes set element names without an additional prefix. This allows me to use the <code class=\"fm-code-in-text1\">FromForm<\/code> attribute for model binding without using the <code class=\"fm-code-in-text1\">Name<\/code> argument.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.22 The contents of the Create.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/create\"\n@model CreateModel\n\n&lt;div class=\"m-2\"&gt;\n    &lt;partial name=\"_ProductEditor\" model=\"@Model.ViewModel\" \/&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class CreateModel : EditorPageModel {\n        \n        public CreateModel(DataContext dbContext) : base(dbContext) { }\n                \n        public void OnGet() {\n            ViewModel = ViewModelFactory.Create(\n                new () { Name = string.Empty }, Categories, Suppliers);\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync(\n                [FromForm] Product product) {\n            if (ModelState.IsValid) {\n                product.ProductId = default;\n                product.Category = default;\n                product.Supplier = default;\n                DataContext.Products.Add(product);\n                await DataContext.SaveChangesAsync();\n                return RedirectToPage(nameof(Index));\n            }\n            ViewModel = ViewModelFactory.Create(product, \n                Categories, Suppliers);\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Add a Razor Page named <code class=\"fm-code-in-text\">Edit.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the code and content shown in listing 31.23.<a id=\"calibre_link-2670\"><\/a><a id=\"calibre_link-983\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.23 The contents of the Edit.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/edit\/{id}\"\n@model EditModel\n\n&lt;div class=\"m-2\"&gt;\n    &lt;partial name=\"_ProductEditor\" model=\"@Model.ViewModel\" \/&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class EditModel : EditorPageModel {\n        \n        public EditModel(DataContext dbContext) : base(dbContext) { }\n                \n        public async Task OnGetAsync(long id) {\n            Product p = await this.DataContext.Products.FindAsync(id)\n                ?? new () { Name = string.Empty };\n            ViewModel = ViewModelFactory.Edit(p, Categories, Suppliers);\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync(\n                [FromForm] Product product) {\n            if (ModelState.IsValid) {\n                product.Category = default;\n                product.Supplier = default;\n                DataContext.Products.Update(product);\n                await DataContext.SaveChangesAsync();\n                return RedirectToPage(nameof(Index));\n            }\n            ViewModel = ViewModelFactory.Edit(product, \n                Categories, Suppliers);\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Add a Razor Page named <code class=\"fm-code-in-text\">Delete.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the code and content shown in listing 31.24.<a id=\"calibre_link-2671\"><\/a><a id=\"calibre_link-981\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.24 The contents of the Delete.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/delete\/{id}\"\n@model DeleteModel\n\n&lt;div class=\"m-2\"&gt;\n    &lt;partial name=\"_ProductEditor\" model=\"@Model.ViewModel\" \/&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class DeleteModel : EditorPageModel {\n        \n        public DeleteModel(DataContext dbContext) : base(dbContext) { }\n                \n        public async Task OnGetAsync(long id) {\n            ViewModel = ViewModelFactory.Delete(\n                await DataContext.Products.FindAsync(id) \n                    ?? new () { Name = string.Empty },\n                Categories, Suppliers);\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync(\n                [FromForm] Product product) {\n            DataContext.Products.Remove(product);\n            await DataContext.SaveChangesAsync();\n            return RedirectToPage(nameof(Index));\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and navigate to http:\/\/localhost:5000\/pages, and you will be able to click the links to view, create, edit, and remove data, as shown in figure 31.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre344\" src=\"\/images\/proaspnetcore7\/000350.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 31.8 Using Razor Pages<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-596\">31.4 Creating new related data objects<\/h2>\n<p class=\"body\">Some applications will need to allow the user to create new related data so that, for example, a new <code class=\"fm-code-in-text\">Category<\/code> can be created along with a <code class=\"fm-code-in-text\">Product<\/code> in that <code class=\"fm-code-in-text\">Category<\/code>. There are two ways to approach this problem, as described in the sections that follow.<a id=\"calibre_link-2672\"><\/a><a id=\"calibre_link-2673\"><\/a><a id=\"calibre_link-940\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-597\">31.4.1 Providing the related data in the same request<\/h3>\n<p class=\"body\">The first approach is to ask the user to provide the data required to create the related data in the same form. For the example application, this means collecting details for a <code class=\"fm-code-in-text\">Category<\/code> object in the same form that the user enters the values for the <code class=\"fm-code-in-text\">Product<\/code> object.<\/p>\n<p class=\"body\">This can be a useful approach for simple data types, where only a small amount of data is required to create the related object but is not well suited for types with many properties.<\/p>\n<p class=\"body\">I prefer to define the HTML elements for the related data type in their own partial view. Add a Razor View named <code class=\"fm-code-in-text\">_CategoryEditor.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 31.25.<a id=\"calibre_link-2674\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.25 The contents of the _CategoryEditor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@model Product\n\n&lt;script type=\"text\/javascript\"&gt;\n    window.addEventListener(\"DOMContentLoaded\", () =&gt; {\n        function setVisibility(visible) {\n            document.getElementById(\"categoryGroup\").hidden = !visible;\n            input = document.getElementById(\"categoryInput\")\n            if (visible) {\n                input.removeAttribute(\"disabled\");\n            } else {\n                input.setAttribute(\"disabled\", \"disabled\");\n            }                \n        }\n        setVisibility(false);\n        document.querySelector(\"select[name='Product.CategoryId']\")\n            .addEventListener(\"change\", (event) =&gt; \n                setVisibility(event.target.value === \"-1\")\n            );\n    });\n&lt;\/script&gt;\n\n&lt;div class=\"form-group bg-info mt-2 p-1\" id=\"categoryGroup\"&gt;\n    @{ #pragma warning disable CS8602 } \n    &lt;label class=\"text-white\" asp-for=\"Category.Name\"&gt;\n        New Category Name\n    &lt;\/label&gt;\n    &lt;input class=\"form-control\" asp-for=\"Category.Name\" value=\"\" \n        id=\"categoryInput\" \/&gt;\n    @{ #pragma warning restore CS8602 } \n&lt;\/div&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Category<\/code> type requires only one property, which the user will provide using a standard <code class=\"fm-code-in-text\">input<\/code> element. The <code class=\"fm-code-in-text\">script<\/code> element in the partial view contains JavaScript code that hides the new elements until the user selects an <code class=\"fm-code-in-text\">option<\/code> element that sets a value of <code class=\"fm-code-in-text\">-1<\/code> for the <code class=\"fm-code-in-text\">Product.CategoryId<\/code> property. (Using JavaScript is entirely optional, but it helps to emphasize the purpose of the new elements.)<\/p>\n<p class=\"body\">Listing 31.26 adds the partial view to the editor, along with the <code class=\"fm-code-in-text\">option<\/code> element that will display the elements for creating a new <code class=\"fm-code-in-text\">Category<\/code> object.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.26 Adding elements in the _ProductEditor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"form-group\"&gt;\n    &lt;label asp-for=\"Product.CategoryId\"&gt;Category&lt;\/label&gt;\n    &lt;div&gt;\n        &lt;span asp-validation-for=\"Product.CategoryId\" \n            class=\"text-danger\"&gt;\n        &lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;select asp-for=\"Product.CategoryId\" class=\"form-control\"\n            disabled=\"@Model?.ReadOnly\"\n            asp-items=\"@(new SelectList(Model?.Categories,\n                \"CategoryId\", \"Name\"))\"&gt;\n        <b class=\"fm-bold\">&lt;option value=\"-1\"&gt;Create New Category...&lt;\/option&gt;<\/b>\n        &lt;option value=\"\" disabled selected&gt;Choose a Category&lt;\/option&gt;\n    &lt;\/select&gt;\n&lt;\/div&gt;\n<b class=\"fm-bold\">&lt;partial name=\"_CategoryEditor\" for=\"Product\" \/&gt;<\/b>\n&lt;div class=\"form-group\"&gt;\n    &lt;label asp-for=\"Product.SupplierId\"&gt;Supplier&lt;\/label&gt;\n    &lt;div&gt;\n        &lt;span asp-validation-for=\"Product.SupplierId\" \n            class=\"text-danger\"&gt;\n        &lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;select asp-for=\"Product.SupplierId\" class=\"form-control\"\n            disabled=\"@Model?.ReadOnly\"\n            asp-items=\"@(new SelectList(Model?.Suppliers,\n                \"SupplierId\", \"Name\"))\"&gt;\n        &lt;option value=\"\" disabled selected&gt;Choose a Supplier&lt;\/option&gt;\n    &lt;\/select&gt;\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">I need the new functionality in multiple pages, so to avoid code duplication, I have added a method that handles the related data to the page model base class, as shown in listing 31.27.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.27 Adding a method in the EditorPageModel.cs file in the Pages folder<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.RazorPages;\nusing WebApp.Models;\n\nnamespace WebApp.Pages {\n\n    public class EditorPageModel : PageModel {\n        \n        public EditorPageModel(DataContext dbContext) {\n            DataContext = dbContext;\n        }\n \n        public DataContext DataContext { get; set; }\n                \n        public IEnumerable&lt;Category&gt; Categories =&gt; DataContext.Categories;\n        public IEnumerable&lt;Supplier&gt; Suppliers =&gt; DataContext.Suppliers;\n                \n        public ProductViewModel? ViewModel { get; set; }\n\n        <b class=\"fm-bold\">protected async Task CheckNewCategory(Product product) {<\/b>\n            <b class=\"fm-bold\">if (product.CategoryId == -1<\/b>\n                    <b class=\"fm-bold\">&amp;&amp; !string.IsNullOrEmpty(product.Category?.Name)) {<\/b>\n                <b class=\"fm-bold\">DataContext.Categories.Add(product.Category);<\/b>\n                <b class=\"fm-bold\">await DataContext.SaveChangesAsync();<\/b>\n                <b class=\"fm-bold\">product.CategoryId = product.Category.CategoryId;<\/b>\n                <b class=\"fm-bold\">ModelState.Clear();<\/b>\n                <b class=\"fm-bold\">TryValidateModel(product);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The new code creates a <code class=\"fm-code-in-text\">Category<\/code> object using the data received from the user and stores it in the database. The database server assigns a primary key to the new object, which Entity Framework Core uses to update the <code class=\"fm-code-in-text\">Category<\/code> object. This allows me to update the <code class=\"fm-code-in-text\">CategoryId<\/code> property of the <code class=\"fm-code-in-text\">Product<\/code> object and then re-validate the model data, knowing that the value assigned to the <code class=\"fm-code-in-text\">CategoryId<\/code> property will pass validation because it corresponds to the newly allocated key. To integrate the new functionality into the <code class=\"fm-code-in-text\">Create<\/code> page, add the statement shown in listing 31.28.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.28 Adding a statement in the Create.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/create\"\n@model CreateModel\n\n&lt;div class=\"m-2\"&gt;\n    &lt;partial name=\"_ProductEditor\" model=\"@Model.ViewModel\" \/&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class CreateModel : EditorPageModel {\n        \n        public CreateModel(DataContext dbContext) : base(dbContext) { }\n                \n        public void OnGet() {\n            ViewModel = ViewModelFactory.Create(\n                new () { Name = string.Empty }, Categories, Suppliers);\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync(\n                [FromForm] Product product) {\n            <b class=\"fm-bold\">await CheckNewCategory(product);<\/b>\n            if (ModelState.IsValid) {\n                product.ProductId = default;\n                product.Category = default;\n                product.Supplier = default;\n                DataContext.Products.Add(product);\n                await DataContext.SaveChangesAsync();\n                return RedirectToPage(nameof(Index));\n            }\n            ViewModel = ViewModelFactory.Create(product, \n                Categories, Suppliers);\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Add the same statement to the handler method in the <code class=\"fm-code-in-text\">Edit<\/code> page, as shown in listing 31.29.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.29 Adding a statement in the Edit.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/edit\/{id}\"\n@model EditModel\n\n&lt;div class=\"m-2\"&gt;\n    &lt;partial name=\"_ProductEditor\" model=\"@Model.ViewModel\" \/&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class EditModel : EditorPageModel {\n        \n        public EditModel(DataContext dbContext) : base(dbContext) { }\n                \n        public async Task OnGetAsync(long id) {\n            Product p = await this.DataContext.Products.FindAsync(id)\n                ?? new () { Name = string.Empty };\n            ViewModel = ViewModelFactory.Edit(p, Categories, Suppliers);\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync(\n                [FromForm] Product product) {\n            <b class=\"fm-bold\">await CheckNewCategory(product);<\/b>\n            if (ModelState.IsValid) {\n                product.Category = default;\n                product.Supplier = default;\n                DataContext.Products.Update(product);\n                await DataContext.SaveChangesAsync();\n                return RedirectToPage(nameof(Index));\n            }\n            ViewModel = ViewModelFactory.Edit(product, \n                Categories, Suppliers);\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core so the page model base class is recompiled and use a browser to request http:\/\/localhost:5000\/pages\/edit\/1. Click the Category <code class=\"fm-code-in-text\">select<\/code> element and choose Create New Category from the list of options. Enter a new category name into the input element and click the Edit button. When the request is processed, a new <code class=\"fm-code-in-text\">Category<\/code> object will be stored in the database and associated with the <code class=\"fm-code-in-text\">Product<\/code> object, as shown in figure 31.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre345\" src=\"\/images\/proaspnetcore7\/000351.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 31.9 Creating related data<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-598\">31.4.2 Breaking out to create new data<\/h3>\n<p class=\"body\">For related data types that have their own complex creation process, adding elements to the main form can be overwhelming to the user; a better approach is to navigate away from the main form to another controller or page, let the user create the new object, and then return to complete the original task. I will demonstrate this technique for the creation of <code class=\"fm-code-in-text\">Supplier<\/code> objects, even though the <code class=\"fm-code-in-text\">Supplier<\/code> type is simple and requires only two values from the user.<a id=\"calibre_link-2675\"><\/a><a id=\"calibre_link-974\"><\/a><\/p>\n<p class=\"body\">To create a form that will let the user create <code class=\"fm-code-in-text\">Supplier<\/code> objects, add a Razor Page named <code class=\"fm-code-in-text\">SupplierBreakOut.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 31.30.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.30 The contents of the SupplierBreakOut.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/supplier\"\n@model SupplierPageModel\n\n&lt;div class=\"m-2\"&gt;\n    &lt;h5 class=\"bg-secondary text-white text-center p-2\"&gt;New Supplier&lt;\/h5&gt; \n    &lt;form asp-page=\"SupplierBreakOut\" method=\"post\"&gt;\n        &lt;div class=\"form-group\"&gt;\n            @{ #pragma warning disable CS8602 } \n            &lt;label asp-for=\"Supplier.Name\"&gt;&lt;\/label&gt;\n            &lt;input class=\"form-control\" asp-for=\"Supplier.Name\"  \/&gt;\n            @{ #pragma warning restore CS8602 } \n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            @{ #pragma warning disable CS8602 } \n            &lt;label asp-for=\"Supplier.City\"&gt;&lt;\/label&gt;\n            &lt;input class=\"form-control\" asp-for=\"Supplier.City\"  \/&gt;\n            @{ #pragma warning restore CS8602 } \n        &lt;\/div&gt;\n        &lt;button class=\"btn btn-secondary mt-2\" type=\"submit\"&gt;\n            Create\n        &lt;\/button&gt;\n        &lt;a class=\"btn btn-outline-secondary mt-2\" \n                asp-page=\"@Model.ReturnPage\" \n                asp-route-id=\"@Model.ProductId\"&gt;\n            Cancel\n        &lt;\/a&gt;\n    &lt;\/form&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class SupplierPageModel: PageModel {\n        private DataContext context;\n\n        public SupplierPageModel(DataContext dbContext) {\n            context = dbContext;\n        }\n\n        [BindProperty]\n        public Supplier? Supplier { get; set; }\n                \n        public string? ReturnPage { get; set; }\n        public string? ProductId { get; set; }\n                \n        public void OnGet([FromQuery(Name=\"Product\")] Product product, \n                string returnPage) {\n            TempData[\"product\"] = Serialize(product);\n            TempData[\"returnAction\"] =  ReturnPage = returnPage;\n            TempData[\"productId\"] = ProductId \n                = product.ProductId.ToString();\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync() {\n            if (ModelState.IsValid &amp;&amp; Supplier != null) {\n                context.Suppliers.Add(Supplier);\n                await context.SaveChangesAsync();\n                Product? product \n                    = Deserialize(TempData[\"product\"] as string);\n                if (product != null) {\n                    product.SupplierId = Supplier.SupplierId;\n                    TempData[\"product\"] = Serialize(product);\n                    string? id = TempData[\"productId\"] as string;\n                    return RedirectToPage(TempData[\"returnAction\"] \n                        as string, new { id = id });\n                }\n            }\n            return Page();\n        }\n                \n        private string Serialize(Product p) =&gt; \n            JsonSerializer.Serialize(p);\n        private Product? Deserialize(string? json) =&gt;\n            json == null ? null \n                : JsonSerializer.Deserialize&lt;Product&gt;(json);\n    }\n}<\/pre>\n<p class=\"body\">The user will navigate to this page using a GET request that will contain the details of the <code class=\"fm-code-in-text\">Product<\/code> the user has provided and the name of the page that the user should be returned to. This data is stored using the temp data feature.<\/p>\n<p class=\"body\">This page presents the user with a form containing fields for the <code class=\"fm-code-in-text\">Name<\/code> and <code class=\"fm-code-in-text\">City<\/code> properties required to create a new <code class=\"fm-code-in-text\">Supplier<\/code> object. When the form is submitted, the POST handler method stores a new <code class=\"fm-code-in-text\">Supplier<\/code> object and uses the key assigned by the database server to update the <code class=\"fm-code-in-text\">Product<\/code> object, which is then stored as temp data again. The user is redirected back to the page from which they arrived.<\/p>\n<p class=\"body\">Listing 31.31 adds elements to the <code class=\"fm-code-in-text\">_ProductEditor<\/code> partial view that will allow the user to navigate to the new page.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.31 Adding elements in the _ProductEditor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"form-group\"&gt;\n    &lt;label asp-for=\"Product.SupplierId\"&gt;\n        Supplier\n        <b class=\"fm-bold\">@if (Model?.ReadOnly == false) {<\/b>\n            <b class=\"fm-bold\">&lt;input type=\"hidden\" name=\"returnPage\"<\/b> \n                <b class=\"fm-bold\">value=\"@Model?.Action\" \/&gt;<\/b>\n            <b class=\"fm-bold\">&lt;button class=\"btn btn-sm btn-outline-primary ml-3 my-1\"<\/b>\n                <b class=\"fm-bold\">asp-page=\"SupplierBreakOut\" formmethod=\"get\"<\/b> \n                    <b class=\"fm-bold\">formnovalidate&gt;<\/b>\n                <b class=\"fm-bold\">Create New Supplier<\/b>\n            <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    &lt;\/label&gt;\n    &lt;div&gt;\n        &lt;span asp-validation-for=\"Product.SupplierId\" \n            class=\"text-danger\"&gt;\n        &lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;select asp-for=\"Product.SupplierId\" class=\"form-control\"\n            disabled=\"@Model?.ReadOnly\"\n            asp-items=\"@(new SelectList(Model?.Suppliers,\n                \"SupplierId\", \"Name\"))\"&gt;\n        &lt;option value=\"\" disabled selected&gt;Choose a Supplier&lt;\/option&gt;\n    &lt;\/select&gt;\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">The new elements add a hidden <code class=\"fm-code-in-text\">input<\/code> element that captures the page to return to and a <code class=\"fm-code-in-text\">button<\/code> element that submits the form data to the <code class=\"fm-code-in-text\">SupplierBreakOut<\/code> page using a GET request, which means the form values will be encoded in the query string (and is the reason I used the <code class=\"fm-code-in-text\">FromQuery<\/code> attribute in listing 31.30). Listing 31.32 shows the change required to the <code class=\"fm-code-in-text\">Create<\/code> page to add support for retrieving the temp data and using it to populate the <code class=\"fm-code-in-text\">Product<\/code> form.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.32 Retrieving data in the Create.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/create\"\n@model CreateModel\n\n&lt;div class=\"m-2\"&gt;\n    &lt;partial name=\"_ProductEditor\" model=\"@Model.ViewModel\" \/&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class CreateModel : EditorPageModel {\n        \n        public CreateModel(DataContext dbContext) : base(dbContext) { }\n                \n        public void OnGet() {\n            <b class=\"fm-bold\">Product p = TempData.ContainsKey(\"product\")<\/b>\n                <b class=\"fm-bold\">? JsonSerializer.Deserialize&lt;Product&gt;(<\/b>\n                        <b class=\"fm-bold\">(TempData[\"product\"] as string)!)!<\/b>\n                <b class=\"fm-bold\">: new () { Name = string.Empty };<\/b>\n            <b class=\"fm-bold\">ViewModel = ViewModelFactory.Create(p, Categories, Suppliers);<\/b>\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync(\n                [FromForm] Product product) {\n            await CheckNewCategory(product);\n            if (ModelState.IsValid) {\n                product.ProductId = default;\n                product.Category = default;\n                product.Supplier = default;\n                DataContext.Products.Add(product);\n                await DataContext.SaveChangesAsync();\n                return RedirectToPage(nameof(Index));\n            }\n            ViewModel = ViewModelFactory.Create(product, \n                Categories, Suppliers);\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">A similar change is required in the <code class=\"fm-code-in-text\">Edit<\/code> page, as shown in listing 31.33. (The other pages do not require a change since the breakout is required only when the user is able to create or edit <code class=\"fm-code-in-text\">Product<\/code> data.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 31.33 Retrieving data in the Edit.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/edit\/{id}\"\n@model EditModel\n\n&lt;div class=\"m-2\"&gt;\n    &lt;partial name=\"_ProductEditor\" model=\"@Model.ViewModel\" \/&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class EditModel : EditorPageModel {\n        \n        public EditModel(DataContext dbContext) : base(dbContext) { }\n                \n        public async Task OnGetAsync(long id) {\n            <b class=\"fm-bold\">Product? p = TempData.ContainsKey(\"product\")<\/b>\n                <b class=\"fm-bold\">? JsonSerializer.Deserialize&lt;Product&gt;(<\/b>\n                    <b class=\"fm-bold\">(TempData[\"product\"] as string)!)<\/b>\n                <b class=\"fm-bold\">: await this.DataContext.Products.FindAsync(id);<\/b>\n            <b class=\"fm-bold\">ViewModel = ViewModelFactory.Edit(p<\/b> \n                    <b class=\"fm-bold\">?? new () { Name = string.Empty },<\/b>\n                <b class=\"fm-bold\">Categories, Suppliers);<\/b>\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync(\n                [FromForm] Product product) {\n            await CheckNewCategory(product);\n            if (ModelState.IsValid) {\n                product.Category = default;\n                product.Supplier = default;\n                DataContext.Products.Update(product);\n                await DataContext.SaveChangesAsync();\n                return RedirectToPage(nameof(Index));\n            }\n            ViewModel = ViewModelFactory.Edit(product, \n                Categories, Suppliers);\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The effect is that the user is presented with a Create New Supplier button, which sends the browser to a form that can be used to create a <code class=\"fm-code-in-text\">Supplier<\/code> object. Once the <code class=\"fm-code-in-text\">Supplier<\/code> has been stored in the database, the browser is sent back to the originating page, and the form is populated with the data the user had entered, and the <code class=\"fm-code-in-text\">Supplier<\/code> select element is set to the newly created object, as shown in figure 31.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre346\" src=\"\/images\/proaspnetcore7\/000352.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 31.10 Breaking out to create related data<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2676\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The ASP.NET Core form-handling features can be combined with Entity Framework Core to perform CRUD operations.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Forms can be created using Razor Views and Razor Pages.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Care must be taken to ensure consistency when creating related data.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-599\">\n<div class=\"calibre1\" id=\"calibre_link-2677\">\n<h1 class=\"tochead\" id=\"calibre_link-2678\">Part 4.<\/h1>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-600\">\n<div class=\"calibre1\" id=\"calibre_link-2679\">\n<h1 class=\"tochead\" id=\"calibre_link-2680\"><a id=\"calibre_link-2681\"><\/a>32 Creating the example project<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2682\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Creating an ASP.NET Core project<\/li>\n<li class=\"co-summary-bullet\">Creating a simple data model<\/li>\n<li class=\"co-summary-bullet\">Adding Entity Framework Core to the ASP.NET Core project<\/li>\n<li class=\"co-summary-bullet\">Creating and applying an Entity Framework Core migration<\/li>\n<li class=\"co-summary-bullet\">Adding the Bootstrap CSS package to the project<\/li>\n<li class=\"co-summary-bullet\">Defining a simple request pipeline and services configuration<\/li>\n<li class=\"co-summary-bullet\">Creating an MVC controller and Razor View<\/li>\n<li class=\"co-summary-bullet\">Creating a Razor Page<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, you will create the example project used throughout this part of the book. The project contains a data model that is displayed using simple controllers and Razor Pages.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-601\">32.1 Creating the project<\/h2>\n<p class=\"body\">Open a new PowerShell command prompt from the Windows Start menu and run the commands shown in listing 32.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.1 Creating the project<\/p>\n<pre class=\"programlisting\">dotnet new globaljson --sdk-version 7.0.100 --output Advanced\ndotnet new web --no-https --output <a id=\"calibre_link-2683\"><\/a>Advanced --framework net7.0\ndotnet new sln -o Advanced\ndotnet sln Advanced add Advanced<\/pre>\n<p class=\"body\">If you are using Visual Studio, open the <code class=\"fm-code-in-text\">Advanced.sln<\/code> file in the <code class=\"fm-code-in-text\">Advanced<\/code> folder. If you are using Visual Studio Code, open the <code class=\"fm-code-in-text\">Advanced<\/code> folder. Click the Yes button when prompted to add the assets required for building and debugging the project, as shown in figure 32.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre347\" src=\"\/images\/proaspnetcore7\/000353.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 32.1 Adding project assets<\/p>\n<\/div>\n<p class=\"body\">Open the <code class=\"fm-code-in-text\">launchSettings.json<\/code> file in the <code class=\"fm-code-in-text\">WebApp\/Properties<\/code> folder and change the HTTP port and disable automatic browser launching, as shown in listing 32.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.2 Setting the HTTP port in the launchSettings.json file in the Properties folder<\/p>\n<pre class=\"programlisting\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"WebApp\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      <b class=\"fm-bold\">\"launchBrowser\": false,<\/b>\n      <b class=\"fm-bold\">\"applicationUrl\": \"http:\/\/localhost:5000\",<\/b>\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-602\">32.1.1 Adding NuGet packages to the project<\/h3>\n<p class=\"body\">The data model will use Entity Framework Core to store and query data in a SQL Server LocalDB database. To add the NuGet packages for Entity Framework Core, use a PowerShell command prompt to run the commands shown in listing 32.3 in the <code class=\"fm-code-in-text\">Advanced<\/code> project folder.<a id=\"calibre_link-2684\"><\/a><a id=\"calibre_link-873\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.3 Adding packages to the project<\/p>\n<pre class=\"programlisting\">dotnet add package Microsoft.EntityFrameworkCore.Design --version 7.0.0 \ndotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 7.0.0<\/pre>\n<p class=\"body\">If you have not followed the examples in earlier chapters, you will need to install the global tool package that is used to create and manage Entity Framework Core migrations. Run the commands shown in listing 32.4 to remove any existing version of the package and install the version required for this book.<a id=\"calibre_link-2685\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.4 Installing a global tool package<\/p>\n<pre class=\"programlisting\">dotnet tool uninstall --global dotnet-ef\ndotnet tool install --global dotnet-ef --version 7.0.0<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-603\">32.2 Adding a data model<\/h2>\n<p class=\"body\">The data model for this application will consist of three classes, representing people, the department in which they work, and their location. Create a <code class=\"fm-code-in-text\">Models<\/code> folder and add to it a class file named <code class=\"fm-code-in-text\">Person.cs<\/code> with the code in listing 32.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.5 The contents of the Person.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace Advanced.Models {\n\n    public class Person {\n        \n        public long PersonId { get; set; }\n        public string Firstname { get; set; } = String.Empty;\n        public string Surname { get; set; } = String.Empty;\n        public long DepartmentId { get; set; }\n        public long LocationId { get; set; }\n                \n        public Department? Department { get; set; }\n        public Location? Location { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Add a class file named <code class=\"fm-code-in-text\">Department.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the class shown in listing 32.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.6 The contents of the Department.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace Advanced.Models {\n    public class Department {\n        \n        public long Departmentid { get; set; }\n        public string Name { get; set; } = String.Empty;\n                \n        public IEnumerable&lt;Person&gt;? People { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Add a class file named <code class=\"fm-code-in-text\">Location.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and use it to define the class shown in listing 32.7.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.7 The contents of the Location.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">namespace Advanced.Models {\n    public class Location {\n        \n        public long LocationId { get; set; }\n        public string City { get; set; } = string.Empty;\n        public string State { get; set; } = String.Empty;\n                \n        public IEnumerable&lt;Person&gt;? People { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">Each of the three data model classes defines a key property whose value will be allocated by the database when new objects are stored and defines foreign key properties that define the relationships between the classes. These are supplemented by navigation properties that will be used with the Entity Framework Core <code class=\"fm-code-in-text\">Include<\/code> method to incorporate related data into queries.<\/p>\n<p class=\"body\">To create the Entity Framework Core context class that will provide access to the database, add a file called <code class=\"fm-code-in-text\">DataContext.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and add the code shown in listing 32.8.<a id=\"calibre_link-2686\"><\/a><a id=\"calibre_link-939\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.8 The contents of the DataContext.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n\nnamespace Advanced.Models {\n    public class DataContext : DbContext {\n        \n        public DataContext(DbContextOptions&lt;DataContext&gt; opts)\n            : base(opts) { }\n                        \n        public DbSet&lt;Person&gt; People =&gt; Set&lt;Person&gt;();\n        public DbSet&lt;Department&gt; Departments =&gt; Set&lt;Department&gt;();\n        public DbSet&lt;Location&gt; Locations =&gt; Set&lt;Location&gt;();\n    }\n}<\/pre>\n<p class=\"body\">The context class defines properties that will be used to query the database for <code class=\"fm-code-in-text\">Person<\/code>, <code class=\"fm-code-in-text\">Department<\/code>, and <code class=\"fm-code-in-text\">Location<\/code> data.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-604\">32.2.1 Preparing the seed data<\/h3>\n<p class=\"body\">Add a class called <code class=\"fm-code-in-text\">SeedData.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder and add the code shown in listing 32.9 to define the seed data that will be used to populate the database.<a id=\"calibre_link-2687\"><\/a><a id=\"calibre_link-947\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.9 The contents of the SeedData.cs file in the Models folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\n\nnamespace Advanced.Models {\n    public static class SeedData {\n        \n        public static void SeedDatabase(DataContext context) {\n            context.Database.Migrate();\n            if (context.People.Count() == 0 \n                    &amp;&amp; context.Departments.Count() == 0 \n                    &amp;&amp; context.Locations.Count() == 0) {\n                                        \n                Department d1 = new () { Name = \"Sales\" };\n                Department d2 = new () { Name = \"Development\" };\n                Department d3 = new () { Name = \"Support\" };\n                Department d4 = new () { Name = \"Facilities\" };\n                                \n                context.Departments.AddRange(d1, d2, d3, d4);\n                context.SaveChanges();\n                                \n                Location l1 = new () { City = \"Oakland\", State = \"CA\" };\n                Location l2 = new () { City = \"San Jose\", State = \"CA\" };\n                Location l3 = new () { City = \"New York\", State = \"NY\" };\n                context.Locations.AddRange(l1, l2, l3);\n                                \n                context.People.AddRange(\n                    new Person {\n                        Firstname = \"Francesca\", Surname = \"Jacobs\",\n                        Department = d2, Location = l1\n                    },\n                    new Person {\n                        Firstname = \"Charles\", Surname = \"Fuentes\",\n                        Department = d2, Location = l3\n                    },\n                    new Person {\n                        Firstname = \"Bright\", Surname = \"Becker\",\n                        Department = d4, Location = l1\n                    },\n                    new Person {\n                        Firstname = \"Murphy\", Surname = \"Lara\",\n                        Department = d1, Location = l3\n                    },\n                    new Person {\n                        Firstname = \"Beasley\", Surname = \"Hoffman\",\n                        Department = d4, Location = l3\n                    },\n                    new Person {\n                        Firstname = \"Marks\", Surname = \"Hays\",\n                        Department = d4, Location = l1\n                    },\n                    new Person {\n                        Firstname = \"Underwood\", Surname = \"Trujillo\",\n                        Department = d2, Location = l1\n                    },\n                    new Person {\n                        Firstname = \"Randall\", Surname = \"Lloyd\",\n                        Department = d3, Location = l2\n                    },\n                    new Person {\n                        Firstname = \"Guzman\", Surname = \"Case\",\n                        Department = d2, Location = l2\n                    });\n                context.SaveChanges();\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The static <code class=\"fm-code-in-text\">SeedDatabase<\/code> method ensures that all pending migrations have been applied to the database. If the database is empty, it is seeded with data. Entity Framework Core will take care of mapping the objects into the tables in the database, and the key properties will be assigned automatically when the data is stored.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-605\">32.2.2 Configuring Entity Framework Core<\/h3>\n<p class=\"body\">Make the changes to the <code class=\"fm-code-in-text\">Program.cs<\/code> file shown in listing 32.10, which configure Entity Framework Core and set up the <code class=\"fm-code-in-text\">DataContext<\/code> services that will be used throughout this part of the book to access the database.<a id=\"calibre_link-2688\"><\/a><a id=\"calibre_link-2689\"><\/a><a id=\"calibre_link-933\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.10 Configuring the application in the Program.cs file in the Advanced folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.UseSqlServer(<\/b>\n        <b class=\"fm-bold\">builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);<\/b>\n    <b class=\"fm-bold\">opts.EnableSensitiveDataLogging(true);<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\n\n<b class=\"fm-bold\">var context = app.Services.CreateScope().ServiceProvider<\/b>\n    <b class=\"fm-bold\">.GetRequiredService&lt;DataContext&gt;();<\/b>\n<b class=\"fm-bold\">SeedData.SeedDatabase(context);<\/b>\n\napp.Run();<\/pre>\n<p class=\"body\">To define the connection string that will be used for the application\u2019s data, add the configuration settings shown in listing 32.11 in the <code class=\"fm-code-in-text\">appsettings.json<\/code> file. The connection string should be entered on a single line.<a id=\"calibre_link-2690\"><\/a><a id=\"calibre_link-848\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.11 Defining a connection string in the Advanced\/appsettings.json file<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"<b class=\"fm-bold\">,<\/b>\n      <b class=\"fm-bold\">\"Microsoft.EntityFrameworkCore\": \"Information\"<\/b>\n    }\n  },\n  \"AllowedHosts\": \"*\"<b class=\"fm-bold\">,<\/b>\n  <b class=\"fm-bold\">\"ConnectionStrings\": {<\/b>\n    <b class=\"fm-bold\">\"PeopleConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=People;\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>MultipleActiveResultSets=True\"<\/b>\n  <b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">}<\/b><\/pre>\n<p class=\"body\">In addition to the connection string, listing 32.11 increases the logging detail for Entity Framework Core so that the SQL queries sent to the database are logged.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-606\">32.2.3 Creating and applying the migration<\/h3>\n<p class=\"body\">To create the migration that will set up the database schema, use a PowerShell command prompt to run the command shown in listing 32.12 in the <code class=\"fm-code-in-text\">Advanced<\/code> project folder.<a id=\"calibre_link-2691\"><\/a><a id=\"calibre_link-2692\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.12 Creating an Entity Framework Core migration<\/p>\n<pre class=\"programlisting\">dotnet ef migrations add Initial<\/pre>\n<p class=\"body\">Once the migration has been created, apply it to the database using the command shown in listing 32.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.13 Applying the migration to the database<\/p>\n<pre class=\"programlisting\">dotnet ef database update<\/pre>\n<p class=\"body\">The logging messages displayed by the application will show the SQL commands that are sent to the database.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> If you need to reset the database, then run the <code class=\"fm-code-in-text1\">dotnet ef database drop --force<\/code> command and then the command in listing 32.13.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-607\">32.3 Adding the Bootstrap CSS framework<\/h2>\n<p class=\"body\">Following the pattern established in earlier chapters, I will use the Bootstrap CSS framework to style the HTML elements produced by the example application. To install the Bootstrap package, run the commands shown in listing 32.14 in the <code class=\"fm-code-in-text\">Advanced<\/code> project folder. These commands rely on the Library Manager package.<a id=\"calibre_link-2693\"><\/a><a id=\"calibre_link-2694\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.14 Installing the Bootstrap CSS framework<\/p>\n<pre class=\"programlisting\">libman init -p cdnjs\nlibman install bootstrap@5.2.3 -d wwwroot\/lib\/bootstrap<\/pre>\n<p class=\"body\">If you are using Visual Studio, you can install client-side packages by right-clicking the Advanced project item in the Solution Explorer and selecting Add &gt; Client-Side Library from the pop-up menu.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-608\">32.4 Configuring the services and middleware<\/h2>\n<p class=\"body\">The example application in this part of the book will respond to requests using both MVC controllers and Razor Pages. Add the statements shown in listing 32.15 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file to configure the services and middleware the application will use.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.15 Configuring the application in the Program.cs file in the Advanced folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n<b class=\"fm-bold\">builder.Services.AddControllersWithViews();<\/b>\n<b class=\"fm-bold\">builder.Services.AddRazorPages();<\/b>\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nvar app = builder.Build();\n\n<b class=\"fm-bold\">\/\/app.MapGet(\"\/\", () =&gt; \"Hello World!\");<\/b>\n\n<b class=\"fm-bold\">app.UseStaticFiles();<\/b>\n\n<b class=\"fm-bold\">app.MapControllers();<\/b>\n<b class=\"fm-bold\">app.MapControllerRoute(\"controllers\",<\/b>\n    <b class=\"fm-bold\">\"controllers\/{controller=Home}\/{action=Index}\/{id?}\");<\/b>\n<b class=\"fm-bold\">app.MapRazorPages();<\/b>\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">In addition to mapping the controller route, I have added a route that matches URL paths that begin with <code class=\"fm-code-in-text\">controllers<\/code>, which will make it easier to follow the examples in later chapters as they switch between controllers and Razor Pages. This is the same convention I adopted in earlier chapters, and I will route URL paths beginning with <code class=\"fm-code-in-text\">\/pages<\/code> to Razor Pages.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-609\">32.5 Creating a controller and view<\/h2>\n<p class=\"body\">To display the application\u2019s data using a controller, create a folder named <code class=\"fm-code-in-text\">Controllers<\/code> in the <code class=\"fm-code-in-text\">Advanced<\/code> project folder and add to it a class file named <code class=\"fm-code-in-text\">HomeController.cs<\/code>, with the content shown in listing 32.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.16 The contents of the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Advanced.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace Advanced.Controllers {\n    public class HomeController : Controller {\n        private DataContext context;\n\n        public HomeController(DataContext dbContext) {\n            context = dbContext;\n        }\n \n        public IActionResult Index([FromQuery] string selectedCity) {\n            return View(new PeopleListViewModel {\n                People = context.People\n                    .Include(p =&gt; p.Department).Include(p =&gt; p.Location),\n                Cities = context.Locations.Select(l =&gt; l.City).Distinct(),\n                SelectedCity = selectedCity\n            });\n        }\n    }\n \n    public class PeopleListViewModel {\n\n        public IEnumerable&lt;Person&gt; People { get; set; } \n            = Enumerable.Empty&lt;Person&gt;();\n                        \n        public IEnumerable&lt;string&gt; Cities { get; set; } \n            = Enumerable.Empty&lt;string&gt;();\n                        \n        public string SelectedCity { get; set; } = String.Empty;\n                \n        public string GetClass(string? city) =&gt;\n\n            SelectedCity == city ? \"bg-info text-white\" : \"\";\n    }\n}<\/pre>\n<p class=\"body\">To provide the controller with a view, create the <code class=\"fm-code-in-text\">Views\/Home<\/code> folder and add to it a Razor View named <code class=\"fm-code-in-text\">Index.cshtml<\/code> with the content shown in listing 32.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.17 The contents of the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model PeopleListViewModel\n\n&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;People&lt;\/h4&gt;\n\n&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;\n            &lt;th&gt;Name&lt;\/th&gt;\n            &lt;th&gt;Dept&lt;\/th&gt;\n            &lt;th&gt;Location&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (Person p in Model.People) {\n            &lt;tr class=\"@Model.GetClass(p.Location?.City)\"&gt;\n                &lt;td&gt;@p.PersonId&lt;\/td&gt;\n                &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n                &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n                &lt;td&gt;@p.Location?.City, @p.Location?.State&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n&lt;form asp-action=\"Index\" method=\"get\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label for=\"selectedCity\"&gt;City&lt;\/label&gt;\n        &lt;select name=\"selectedCity\" class=\"form-control\"&gt;\n            &lt;option disabled selected&gt;Select City&lt;\/option&gt;\n            @foreach (string city in Model.Cities) {\n                &lt;option selected=\"@(city == Model.SelectedCity)\"&gt;\n                    @city\n                &lt;\/option&gt;\n            }\n        &lt;\/select&gt;\n    &lt;\/div&gt;\n    &lt;button class=\"btn btn-primary mt-2\" type=\"submit\"&gt;Select&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n<p class=\"body\">To enable tag helpers and add the namespaces that will be available by default in views, add a Razor View Imports file named <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views<\/code> folder with the content shown in listing 32.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.18 The contents of the _ViewImports.cshtml file in the Views folder<\/p>\n<pre class=\"programlisting\">@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n@using Advanced.Models\n@using Advanced.Controllers<\/pre>\n<p class=\"body\">To specify the default layout for controller views, add a Razor View Start start file named <code class=\"fm-code-in-text\">_ViewStart.cshtml<\/code> to the <code class=\"fm-code-in-text\">Views<\/code> folder with the content shown in listing 32.19.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.19 The contents of the _ViewStart.cshtml file in the Views folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = \"_Layout\";\n}<\/pre>\n<p class=\"body\">To create the layout, create the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder and add to it a Razor Layout named <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> with the content shown in listing 32.20.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.20 The contents of the _Layout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-610\">32.6 Creating a Razor Page<\/h2>\n<p class=\"body\">To display the application\u2019s data using a Razor Page, create the <code class=\"fm-code-in-text\">Pages<\/code> folder and add to it a Razor Page named <code class=\"fm-code-in-text\">Index.cshtml<\/code> with the content shown in listing 32.21.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.21 The contents of the Index.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\"\n@model IndexModel\n\n&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;People&lt;\/h4&gt;\n\n&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;\n            &lt;th&gt;Name&lt;\/th&gt;\n            &lt;th&gt;Dept&lt;\/th&gt;\n            &lt;th&gt;Location&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (Person p in Model.People) {\n            &lt;tr class=\"@Model.GetClass(p.Location?.City)\"&gt;\n                &lt;td&gt;@p.PersonId&lt;\/td&gt;\n                &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n                &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n                &lt;td&gt;@p.Location?.City, @p.Location?.State&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n&lt;form asp-page=\"Index\" method=\"get\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label for=\"selectedCity\"&gt;City&lt;\/label&gt;\n        &lt;select name=\"selectedCity\" class=\"form-control\"&gt;\n            &lt;option disabled selected&gt;Select City&lt;\/option&gt;\n            @foreach (string city in Model.Cities) {\n                &lt;option selected=\"@(city == Model.SelectedCity)\"&gt;\n                    @city\n                &lt;\/option&gt;\n            }\n        &lt;\/select&gt;\n    &lt;\/div&gt;\n    &lt;button class=\"btn btn-primary mt-2\" type=\"submit\"&gt;Select&lt;\/button&gt;\n&lt;\/form&gt;\n\n@functions {\n\n    public class IndexModel : PageModel {\n        private DataContext context;\n\n        public IndexModel(DataContext dbContext) {\n            context = dbContext;\n        }\n \n        public IEnumerable&lt;Person&gt; People { get; set; } \n            = Enumerable.Empty&lt;Person&gt;();\n                        \n        public IEnumerable&lt;string&gt; Cities { get; set; } \n            = Enumerable.Empty&lt;string&gt;();\n                        \n        [FromQuery]\n        public string SelectedCity { get; set; } = String.Empty;\n                \n        public void OnGet() {\n            People = context.People.Include(p =&gt; p.Department)\n                .Include(p =&gt; p.Location);\n            Cities = context.Locations.Select(l =&gt; l.City).Distinct();\n        }\n \n        public string GetClass(string? city) =&gt;\n            SelectedCity == city ? \"bg-info text-white\" : \"\";\n    }\n}<\/pre>\n<p class=\"body\">To enable tag helpers and add the namespaces that will be available by default in the view section of the Razor Pages, add a Razor View imports file named <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 32.22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.22 The contents of the _ViewImports.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n@using Advanced.Models\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using Microsoft.EntityFrameworkCore<\/pre>\n<p class=\"body\">To specify the default layout for Razor Pages, add a Razor View start file named <code class=\"fm-code-in-text\">_ViewStart.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 32.23.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.23 The contents of the _ViewStart.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@{\n    Layout = \"_Layout\";\n}<\/pre>\n<p class=\"body\">To create the layout, add a Razor Layout named <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder with the content shown in listing 32.24.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.24 The contents of the _Layout.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;h5 class=\"bg-secondary text-white text-center p-2\"&gt;Razor Page&lt;\/h5&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<h2 class=\"fm-head\" id=\"calibre_link-611\">32.7 Running the example application<\/h2>\n<p class=\"body\">Start the application by running the command shown in listing 32.25 in the <code class=\"fm-code-in-text\">Advanced<\/code> project folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 32.25 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/controllers and http:\/\/localhost:5000\/pages. Select a city using the select element and click the Select button to highlight rows in the table, as shown in figure 32.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre348\" src=\"\/images\/proaspnetcore7\/000354.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 32.2 Running the example application<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-612\">\n<div class=\"calibre1\" id=\"calibre_link-2695\">\n<h1 class=\"tochead\" id=\"calibre_link-2696\"><a id=\"calibre_link-2697\"><\/a>33 Using Blazor Server, part 1<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2698\"><\/a><a id=\"calibre_link-2699\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Creating Razor Components to add client-side interactivity to ASP.NET Core applications<\/li>\n<li class=\"co-summary-bullet\">Understanding how JavaScript events are used to respond to user interaction<\/li>\n<li class=\"co-summary-bullet\">Managing event propagation in a Razor Component<\/li>\n<li class=\"co-summary-bullet\">Understanding how to define Razor Components<\/li>\n<\/ul>\n<p class=\"body\">Blazor adds client-side interactivity to web applications. There are two varieties of Blazor, and in this chapter, I focus on Blazor Server. I explain the problem it solves and how it works. I show you how to configure an ASP.NET Core application to use Blazor Server and describe the basic features available when using Razor Components, which are the building blocks for Blazor Server projects. I describe more advanced Blazor Server features in chapters 34&ndash;36, and in chapter 37, I describe Blazor WebAssembly, which is the other variety of Blazor. Table 33.1 puts Blazor Server in context.<\/p>\n<p class=\"fm-table-caption\">Table 33.1 Putting Blazor Server in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2700\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What is it?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Blazor Server uses JavaScript to receive browser events, which are forwarded to ASP.NET Core and evaluated using C# code. The effect of the event on the state of the application is sent back to the browser and displayed to the user.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why is it useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Blazor Server can produce a richer and more responsive user experience compared to standard web applications.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How is it used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The building block for Blazor Server is the Razor Component, which uses a syntax similar to Razor Pages. The view section of the Razor Component contains special attributes that specify how the application will respond to user interaction.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Blazor Server relies on a persistent HTTP connection to the server and cannot function when that connection is interrupted. Blazor Server is not supported by older browsers.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The features described in part 3 of this book can be used to create web applications that work broadly but that offer a less responsive experience. You could also consider a client-side JavaScript framework, such as Angular, React, or Vue.js.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 33.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 33.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2701\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Configuring Blazor<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">AddServerSideBlazor<\/code> and <code class=\"fm-code-in-text1\">MapBlazorHub<\/code> methods to set up the required services and middleware and configure the JavaScript file.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">3&ndash;6<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a Blazor Component<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create a <code class=\"fm-code-in-text1\">.blazor<\/code> file and use it to define code and markup.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">7<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Applying a component<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a <code class=\"fm-code-in-text1\">component<\/code> element.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">8, 9<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Handling events<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use an attribute to specify the method or expression that will handle an event.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">10&ndash;15<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating a two-way relationship with an element<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create a data binding.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">16&ndash;20<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Defining the code separately from the markup<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a code-behind class.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">21&ndash;23<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Defining a component without declarative markup<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a Razor Component class.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">24, 25<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-613\">33.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the Advanced project from chapter 32. No changes are required to prepare for this chapter.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">Advanced.csproj<\/code> file, and run the command shown in listing 33.1 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.1 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 33.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.2 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/controllers, which will display a list of data items. Pick a city from the drop-down list and click the Select button to highlight elements, as shown in figure 33.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre349\" src=\"\/images\/proaspnetcore7\/000355.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-614\">33.2 Understanding Blazor Server<\/h2>\n<p class=\"body\">Consider what happens when you choose a city and click the Select button presented by the example application. The browser sends an HTTP GET request that submits a form, which is received by either an action method or a handler method, depending on whether you use the controller or Razor Page. The action or handler renders its view, which sends a new HTML document that reflects the selection to the browser, as illustrated by figure 33.2.<a id=\"calibre_link-2702\"><\/a><a id=\"calibre_link-2703\"><\/a><a id=\"calibre_link-780\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre350\" src=\"\/images\/proaspnetcore7\/000356.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.2 Interacting with the example application<\/p>\n<\/div>\n<p class=\"body\">This cycle is effective but can be inefficient. Each time the Submit button is clicked, the browser sends a new HTTP request to ASP.NET Core. Each request contains a complete set of HTTP headers that describe the request and the types of responses the browser is willing to receive. In its response, the server includes HTTP headers that describe the response and includes a complete HTML document for the browser to display.<\/p>\n<p class=\"body\">The amount of data sent by the example application is about 3KB on my system, and almost all of it is duplicated between requests. The browser only wants to tell the server which city has been selected, and the server only wants to indicate which table rows should be highlighted; however, each HTTP request is self-contained, so the browser must parse a complete HTML document each time. The root issue that every interaction is the same: send a request and get a complete HTML document in return.<a id=\"calibre_link-2704\"><\/a><\/p>\n<p class=\"body\">Blazor takes a different approach. A JavaScript library is included in the HTML document that is sent to the browser. When the JavaScript code is executed, it opens an HTTP connection back to the server and leaves it open, ready for user interaction. When the user picks a value using the select element, for example, details of the selection are sent to the server, which responds with just the changes to apply to the existing HTML, as shown in figure 33.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre351\" src=\"\/images\/proaspnetcore7\/000357.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.3 Interacting with Blazor<\/p>\n<\/div>\n<p class=\"body\">The persistent HTTP connection minimizes the delay, and replying with just the differences reduces the amount of data sent between the browser and the server.<a id=\"calibre_link-2705\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-615\">33.2.1 Understanding the Blazor Server advantages<\/h3>\n<p class=\"body\">The biggest attraction of Blazor is that it is based on Razor Pages written in C#. This means you can increase efficiency and responsiveness without having to learn a new framework, such as Angular or React, and a new language, such as TypeScript or JavaScript. Blazor is nicely integrated into the rest of ASP.NET Core and is built on features described in earlier chapters, which makes it easy to use (especially when compared to a framework like Angular, which has a dizzyingly steep learning curve).<a id=\"calibre_link-2706\"><\/a><a id=\"calibre_link-781\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-616\">33.2.2 Understanding the Blazor Server disadvantages<\/h3>\n<p class=\"body\">Blazor requires a modern browser to establish and maintain its persistent HTTP connection. And, because of this connection, applications that use Blazor stop working if the connection is lost, which makes them unsuitable for offline use, where connectivity cannot be relied on or where connections are slow. These issues are addressed by Blazor WebAssembly, described in chapter 36, but, as I explain, this has its own set of limitations.<a id=\"calibre_link-2707\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-617\">33.2.3 Choosing between Blazor Server and Angular\/React\/Vue.js<\/h3>\n<p class=\"body\">Decisions between Blazor and one of the JavaScript frameworks should be driven by the development team\u2019s experience and the users\u2019 expected connectivity. If you have no JavaScript expertise and have not used one of the JavaScript frameworks, then you should use Blazor, but only if you can rely on good connectivity and modern browsers. This makes Blazor a good choice for line-of-business applications, for example, where the browser demographic and network quality can be determined in advance.<\/p>\n<p class=\"body\">If you have JavaScript experience and you are writing a public-facing application, then you should use one of the JavaScript frameworks because you won\u2019t be able to make assumptions about browsers or network quality. (It doesn\u2019t matter which framework you choose&mdash;I have written books about Angular, React, and Vue.js, and they are all excellent. My advice for choosing a framework is to create a simple app in each of them and pick the one whose development model appeals to you the most.)<\/p>\n<p class=\"body\">If you are writing a public-facing application and you don\u2019t have JavaScript experience, then you have two choices. The safest option is to stick to the ASP.NET Core features described in earlier chapters and accept the inefficiencies this can bring. This isn\u2019t a terrible choice to make, and you can still produce top-quality applications. A more demanding choice is to learn TypeScript or JavaScript and one of Angular, React, or Vue.js&mdash;but don\u2019t underestimate the amount of time it takes to master JavaScript or the complexity of these frameworks.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-618\">33.3 Getting started with Blazor<\/h2>\n<p class=\"body\">The best way to get started with Blazor is to jump right in. In the sections that follow, I configure the application to enable Blazor and re-create the functionality offered by the controller and Razor Page. After that, I\u2019ll go right back to basics and explain how Razor Components work and the different features they offer.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-619\">33.3.1 Configuring ASP.NET Core for Blazor Server<\/h3>\n<p class=\"body\">Preparation is required before Blazor can be used. The first step is to add the services and middleware to the <code class=\"fm-code-in-text\">Program.cs<\/code> file, as shown in listing 33.3.<a id=\"calibre_link-2708\"><\/a><a id=\"calibre_link-2709\"><\/a><a id=\"calibre_link-793\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.3 Configuring the application in the Program.cs file in the Advanced folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\n<b class=\"fm-bold\">builder.Services.AddServerSideBlazor();<\/b>\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\napp.MapControllers();\napp.MapControllerRoute(\"controllers\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\n<b class=\"fm-bold\">app.MapBlazorHub();<\/b>\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The \u201chub\u201d in the <code class=\"fm-code-in-text\">MapBlazorHub<\/code> method relates to SignalR, which is the part of ASP.NET Core that handles the persistent HTTP request. I don\u2019t describe SignalR in this book because it is rarely used directly, but it can be useful if you need ongoing communication between clients and the server. See <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/signalr\">https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/signalr<\/a> for details. For this book&mdash;and most ASP.NET Core applications&mdash;it is enough to know that SignalR is used to manage the connections that Blazor relies on.<\/p>\n<p class=\"fm-head2\">Adding the Blazor JavaScript File to the layout<\/p>\n<p class=\"body\">Blazor relies on JavaScript code to communicate with the ASP.NET Core server. Add the elements shown in listing 33.4 to the <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Views\/Shared<\/code> folder to add the JavaScript file to the layout used by controller views.<a id=\"calibre_link-2710\"><\/a><a id=\"calibre_link-2711\"><\/a><a id=\"calibre_link-828\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.4 Adding elements in the _Layout.cshtml file in the Views\/Shared folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    <b class=\"fm-bold\">&lt;base href=\"~\/\" \/&gt;<\/b>  \n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n    <b class=\"fm-bold\">&lt;script src=\"_framework\/blazor.server.js\"&gt;&lt;\/script&gt;<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">script<\/code> element specifies the name of the JavaScript file, and requests for it are intercepted by the middleware added to the request pipeline in listing 33.3 so that no additional package is required to add the JavaScript code to the project. The <code class=\"fm-code-in-text\">base<\/code> element must also be added to specify the root URL for the application. The same elements must be added to the layout used by Razor Pages, as shown in listing 33.5.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.5 Adding elements in the _Layout.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    <b class=\"fm-bold\">&lt;base href=\"~\/\" \/&gt;<\/b>\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;h5 class=\"bg-secondary text-white text-center p-2\"&gt;\n            Razor Page\n        &lt;\/h5&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n    <b class=\"fm-bold\">&lt;script src=\"_framework\/blazor.server.js\"&gt;&lt;\/script&gt;<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"fm-head2\">Creating the Blazor imports file<\/p>\n<p class=\"body\">Blazor requires its own imports file to specify the namespaces that it uses. It is easy to forget to add this file to a project, but, without it, Blazor will silently fail. Add a file named <code class=\"fm-code-in-text\">_Imports.razor<\/code> to the <code class=\"fm-code-in-text\">Advanced<\/code> folder with the content shown in listing 33.6. (If you are using Visual Studio, you can use the Razor View imports template to create this file, but ensure you use the <code class=\"fm-code-in-text\">.razor<\/code> file extension.)<a id=\"calibre_link-2712\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.6 The contents of the _Imports.razor file in the Advanced folder<\/p>\n<pre class=\"programlisting\">@using Microsoft.AspNetCore.Components\n@using Microsoft.AspNetCore.Components.Forms\n@using Microsoft.AspNetCore.Components.Routing\n@using Microsoft.AspNetCore.Components.Web\n@using Microsoft.JSInterop\n@using Microsoft.EntityFrameworkCore\n@using Advanced.Models<\/pre>\n<p class=\"body\">The first five <code class=\"fm-code-in-text\">@using<\/code> expressions are for the namespaces required for Blazor. The last two expressions are for convenience in the examples that follow because they will allow me to use Entity Framework Core and the classes in the <code class=\"fm-code-in-text\">Models<\/code> namespace.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-620\">33.3.2 Creating a Razor Component<\/h3>\n<p class=\"body\">There is a clash in terminology: the technology is <i class=\"fm-italics\">Blazor<\/i>, but the key building block is called a <i class=\"fm-italics\">Razor Component<\/i>. Razor Components are defined in files with the <code class=\"fm-code-in-text\">.razor<\/code> extension and must begin with a capital letter. Components can be defined anywhere, but they are usually grouped together to help keep the project organized. Create a <code class=\"fm-code-in-text\">Blazor<\/code> folder in the <code class=\"fm-code-in-text\">Advanced<\/code> folder and add to it a Razor Component named <code class=\"fm-code-in-text\">PeopleList.razor<\/code> with the content shown in listing 33.7.<a id=\"calibre_link-2713\"><\/a><a id=\"calibre_link-2714\"><\/a><a id=\"calibre_link-799\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.7 The contents of the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;\n            &lt;th&gt;Name&lt;\/th&gt;\n            &lt;th&gt;Dept&lt;\/th&gt;\n            &lt;th&gt;Location&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (Person p in People ?? Enumerable.Empty&lt;Person&gt;()) {\n            &lt;tr class=\"@GetClass(p?.Location?.City)\"&gt;\n                &lt;td&gt;@p?.PersonId&lt;\/td&gt;\n                &lt;td&gt;@p?.Surname, @p?.Firstname&lt;\/td&gt;\n                &lt;td&gt;@p?.Department?.Name&lt;\/td&gt;\n                &lt;td&gt;@p?.Location?.City, @p?.Location?.State&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"city\"&gt;City&lt;\/label&gt;\n    &lt;select name=\"city\" class=\"form-control\" @bind=\"SelectedCity\"&gt;\n        &lt;option disabled selected value=\"\"&gt;Select City&lt;\/option&gt;\n        @foreach (string city in Cities ?? Enumerable.Empty&lt;string&gt;()) {\n            &lt;option value=\"@city\" selected=\"@(city == SelectedCity)\"&gt;\n                @city\n            &lt;\/option&gt;\n        }\n    &lt;\/select&gt;\n&lt;\/div&gt;\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt;? People =&gt;\n        Context?.People.Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location);\n                        \n    public IEnumerable&lt;string&gt;? Cities =&gt; \n        Context?.Locations.Select(l =&gt; l.City);\n                \n    public string SelectedCity { get; set; } = string.Empty;\n        \n    public string GetClass(string? city) =&gt;\n        SelectedCity == city ? \"bg-info text-white\" : \"\";\n}<\/pre>\n<p class=\"body\">Razor Components are similar to Razor Pages. The view section relies on the Razor features you have seen in earlier chapters, with <code class=\"fm-code-in-text\">@<\/code> expressions to insert data values into the component\u2019s HTML or to generate elements for objects in a sequence, like this:<\/p>\n<pre class=\"programlisting\">...\n@foreach (string city in Cities ?? Enumerable.Empty&lt;string&gt;()) {\n    &lt;option value=\"@city\" selected=\"@(city == SelectedCity)\"&gt;\n        @city\n    &lt;\/option&gt;\n}\n...<\/pre>\n<p class=\"body\">This <code class=\"fm-code-in-text\">@foreach<\/code> expression generates <code class=\"fm-code-in-text\">option<\/code> elements for each value in the <code class=\"fm-code-in-text\">Cities<\/code> sequence and is identical to the equivalent expression in the controller view and Razor Page created in chapter 32.<a id=\"calibre_link-2715\"><\/a><a id=\"calibre_link-2716\"><\/a><a id=\"calibre_link-797\"><\/a><\/p>\n<p class=\"body\">Although Razor Components look familiar, there are some important differences. The first is that there is no page model class and no <code class=\"fm-code-in-text\">@model<\/code> expression. The properties and methods that support a component\u2019s HTML are defined directly in an <code class=\"fm-code-in-text\">@code<\/code> expression, which is the counterpart to the Razor Page <code class=\"fm-code-in-text\">@functions<\/code> expression. To define the property that will provide the view section with <code class=\"fm-code-in-text\">Person<\/code> objects, for example, I just define a <code class=\"fm-code-in-text\">People<\/code> property in the <code class=\"fm-code-in-text\">@code<\/code> section, like this:<\/p>\n<pre class=\"programlisting\">...\npublic IEnumerable&lt;Person&gt;? People =&gt;\n    Context?.People.Include(p =&gt; p.Department)\n        .Include(p =&gt; p.Location);\n...<\/pre>\n<p class=\"body\">And, because there is no page model class, there is no constructor through which to declare service dependencies. Instead, the dependency injection sets the values of properties that have been decorated with the <code class=\"fm-code-in-text\">Inject<\/code> attribute, like this:<\/p>\n<pre class=\"programlisting\">...\n[Inject]\npublic DataContext? Context { get; set; }\n...<\/pre>\n<p class=\"body\">The most significant difference is the use of a special attribute on the <code class=\"fm-code-in-text\">select<\/code> element.<\/p>\n<pre class=\"programlisting\">...\n&lt;select name=\"city\" class=\"form-control\" <b class=\"fm-bold\">@bind=\"SelectedCity\"<\/b>&gt;\n    &lt;option disabled selected value=\"\"&gt;Select City&lt;\/option&gt;\n...<\/pre>\n<p class=\"body\">This Blazor attribute creates a data binding between the value of the <code class=\"fm-code-in-text\">select<\/code> element and the <code class=\"fm-code-in-text\">SelectedCity<\/code> property defined in the <code class=\"fm-code-in-text\">@code<\/code> section.<a id=\"calibre_link-2717\"><\/a><\/p>\n<p class=\"body\">I describe data bindings in more detail in the \u201cWorking with Data Bindings\u201d section, but for now, it is enough to know that the value of the <code class=\"fm-code-in-text\">SelectedCity<\/code> will be updated when the user changes the value of the <code class=\"fm-code-in-text\">select<\/code> element.<\/p>\n<p class=\"body\">Razor components are delivered to the browser as part of a Razor Page or a controller view. Listing 33.8 shows how to use a Razor Component in a controller view.<a id=\"calibre_link-2718\"><\/a><a id=\"calibre_link-2719\"><\/a><a id=\"calibre_link-2720\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.8 Using a Razor Component in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model PeopleListViewModel\n\n&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;People&lt;\/h4&gt;\n\n<b class=\"fm-bold\">&lt;component type=\"typeof(Advanced.Blazor.PeopleList)\"<\/b> \n    <b class=\"fm-bold\">render-mode=\"Server\" \/&gt;<\/b><\/pre>\n<p class=\"body\">Razor Components are applied using the <code class=\"fm-code-in-text\">component<\/code> element, for which there is a tag helper. The <code class=\"fm-code-in-text\">component<\/code> element is configured using the <code class=\"fm-code-in-text\">type<\/code> and <code class=\"fm-code-in-text\">render-mode<\/code> attributes. The <code class=\"fm-code-in-text\">type<\/code> attribute is used to specify the Razor Component. Razor Components are compiled into classes just like controller views and Razor Pages. The <code class=\"fm-code-in-text\">PeopleList<\/code> component is defined in the <code class=\"fm-code-in-text\">Blazor<\/code> folder in the Advanced project, so the type will be <code class=\"fm-code-in-text\">Advanced.Blazor.PeopleList<\/code>, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;component type=\"<b class=\"fm-bold\">typeof(Advanced.Blazor.PeopleList)<\/b>\" \n    render-mode=\"Server\" \/&gt;\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">render-mode<\/code> attribute is used to select how content is produced by the component, using a value from the <code class=\"fm-code-in-text\">RenderMode<\/code> enum, described in table 33.3.<a id=\"calibre_link-2721\"><\/a><a id=\"calibre_link-788\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 33.3 The RenderMode values<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2722\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Static<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The Razor Component renders its view section as static HTML with no client-side support.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Server<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The HTML document is sent to the browser with a placeholder for the component. The HTML displayed by the component is sent to the browser over the persistent HTTP connection and displayed to the user.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ServerPrerendered<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The view section of the component is included in the HTML and displayed to the user immediately. The HTML content is sent again over the persistent HTTP connection.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">For most applications, the <code class=\"fm-code-in-text\">Server<\/code> option is a good choice. The <code class=\"fm-code-in-text\">ServerPrerendered<\/code> includes a static rendition of the Razor Component\u2019s view section in the HTML document sent to the browser. This acts as placeholder content so that the user isn\u2019t presented with an empty browser window while the JavaScript code is loaded and executed. Once the persistent HTTP connection has been established, the placeholder content is deleted and replaced with a dynamic version sent by Blazor. The idea of showing static content to the user is a good one, but it can be confusing because the HTML elements are not wired up to the server-side part of the application, and any interaction from the user either doesn\u2019t work or will be discarded once the live content arrives.<\/p>\n<p class=\"body\">To see Blazor in action, restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/controllers. No form submission is required when using Blazor because the data binding will respond as soon as the <code class=\"fm-code-in-text\">select<\/code> element\u2019s value is changed, as shown in figure 33.4. (You may have to reload the browser to see the Blazor component in action.)<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre352\" src=\"\/images\/proaspnetcore7\/000358.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.4 Using a Razor Component<\/p>\n<\/div>\n<p class=\"body\">When you use the <code class=\"fm-code-in-text\">select<\/code> element, the value you choose is sent over the persistent HTTP connection to the ASP.NET Core server, which updates the Razor Component\u2019s <code class=\"fm-code-in-text\">SelectedCity<\/code> property and re-renders the HTML content. A set of updates is sent to the JavaScript code, which updates the table.<a id=\"calibre_link-2723\"><\/a><\/p>\n<p class=\"body\">Razor Components can also be used in Razor Pages. Add a Razor Page named <code class=\"fm-code-in-text\">Blazor.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder and add the content shown in listing 33.9.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.9 The contents of the Blazor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/blazor\"\n\n&lt;script type=\"text\/javascript\"&gt;\n    window.addEventListener(\"DOMContentLoaded\", () =&gt; {\n        document.getElementById(\"markElems\").addEventListener(\"click\", \n            () =&gt; {\n                document.querySelectorAll(\"td:first-child\")\n                    .forEach(elem =&gt; {\n                        elem.innerText = `M:${elem.innerText}`\n                        elem.classList.add(\"border\", \"border-dark\");\n                    });\n            });\n    });\n&lt;\/script&gt;\n\n&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;Blazor People&lt;\/h4&gt;\n\n&lt;button id=\"markElems\" class=\"btn btn-outline-primary mb-2\"&gt;\n    Mark Elements\n&lt;\/button&gt;\n\n&lt;component type=\"typeof(Advanced.Blazor.PeopleList)\" \n    render-mode=\"Server\" \/&gt;<\/pre>\n<p class=\"body\">The Razor Page in listing 33.9 contains additional JavaScript code that helps demonstrate that only changes are sent, instead of an entirely new HTML table. Restart ASP.NET Core and request http:\/\/localhost:5000\/pages\/blazor. Click the Mark Elements button, and the cells in the ID column will be changed to display different content and a border. Now use the <code class=\"fm-code-in-text\">select<\/code> element to pick a different city, and you will see that the elements in the table are modified without being deleted, as shown in figure 33.5.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding Blazor connection messages<\/p>\n<p class=\"fm-sidebar-text\">When you stop ASP.NET Core, you will see an error message in the browser window, which indicates the connection to the server has been lost and prevents the user from interacting with the displayed component. Blazor will attempt to reconnect and pick up where it left off when the disconnection is caused by temporary network issues, but it won\u2019t be able to do so when the server has been stopped or restarted because the context data for the connection has been lost; you will have to explicitly request a new URL.<\/p>\n<p class=\"fm-sidebar-text\">There is a default reload link in the connection message, but that goes to the default URL for the website, which isn\u2019t useful for this book where I direct you to specific URLs to see the effect of examples. See chapter 34 for details of how to configure the connection messages.<a id=\"calibre_link-2724\"><\/a><\/p>\n<\/div>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre353\" src=\"\/images\/proaspnetcore7\/000359.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.5 Demonstrating that only changes are used<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-621\">33.4 Understanding the basic Razor Component features<\/h2>\n<p class=\"body\">Now that I have demonstrated how Blazor can be used and how it works, it is time to go back to the basics and introduce the features that Razor Components offer. Although the example in the previous section showed how standard ASP.NET Core features can be reproduced using Blazor, there is a much wider set of features available.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-622\">33.4.1 Understanding Blazor events and data bindings<\/h3>\n<p class=\"body\">Events allow a Razor Component to respond to user interaction, and Blazor uses the persistent HTTP connection to send details of the event to the server where it can be processed. To see Blazor events in action, add a Razor Component named <code class=\"fm-code-in-text\">Events.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder with the content shown in listing 33.10.<a id=\"calibre_link-2725\"><\/a><a id=\"calibre_link-822\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.10 The contents of the Events.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"m-2 p-2 border\"&gt;\n    &lt;button class=\"btn btn-primary\" @onclick=\"IncrementCounter\"&gt;\n        Increment\n    &lt;\/button&gt;\n    &lt;span class=\"p-2\"&gt;Counter Value: @Counter&lt;\/span&gt;\n&lt;\/div&gt;\n\n@code {\n    public int Counter { get; set; } = 1;\n        \n    public void IncrementCounter(MouseEventArgs e) {\n        Counter++;\n    }\n}<\/pre>\n<p class=\"body\">You register a handler for an event by adding an attribute to an HTML element, where the attribute name is <code class=\"fm-code-in-text\">@on<\/code>, followed by the event name. In the example, I have set up a handler for the <code class=\"fm-code-in-text\">click<\/code> events generated by a <code class=\"fm-code-in-text\">button<\/code> element, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;button class=\"btn btn-primary\" <b class=\"fm-bold\">@onclick<\/b>=\"IncrementCounter\"&gt;\n    Increment\n&lt;\/button&gt;\n...<\/pre>\n<p class=\"body\">The value assigned to the attribute is the name of the method that will be invoked when the event is triggered. The method can define an optional parameter that is either an instance of the <code class=\"fm-code-in-text\">EventArgs<\/code> class or a class derived from <code class=\"fm-code-in-text\">EventArgs<\/code> that provides additional information about the event.<\/p>\n<p class=\"body\">For the <code class=\"fm-code-in-text\">onclick<\/code> event, the handler method receives a <code class=\"fm-code-in-text\">MouseEventArgs<\/code> object, which provides additional details, such as the screen coordinates of the click. Table 33.4 lists the event description events and the events for which they are used.<a id=\"calibre_link-2726\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 33.4 The EventArgs classes and the events they represent<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2727\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Class<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Events<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ChangeEventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">onchange<\/code>, <code class=\"fm-code-in-text1\">oninput<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ClipboardEventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">oncopy<\/code>, <code class=\"fm-code-in-text1\">oncut<\/code>, <code class=\"fm-code-in-text1\">onpaste<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">DragEventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ondrag<\/code>, <code class=\"fm-code-in-text1\">ondragend<\/code>, <code class=\"fm-code-in-text1\">ondragenter<\/code>, <code class=\"fm-code-in-text1\">ondragleave<\/code>, <code class=\"fm-code-in-text1\">ondragover<\/code>, <code class=\"fm-code-in-text1\">ondragstart<\/code>, <code class=\"fm-code-in-text1\">ondrop<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ErrorEventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">onerror<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">FocusEventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">onblur<\/code>, <code class=\"fm-code-in-text1\">onfocus<\/code>, <code class=\"fm-code-in-text1\">onfocusin<\/code>, <code class=\"fm-code-in-text1\">onfocusout<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">KeyboardEventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">onkeydown<\/code>, <code class=\"fm-code-in-text1\">onkeypress<\/code>, <code class=\"fm-code-in-text1\">onkeyup<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MouseEventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">onclick<\/code>, <code class=\"fm-code-in-text1\">oncontextmenu<\/code>, <code class=\"fm-code-in-text1\">ondblclick<\/code>, <code class=\"fm-code-in-text1\">onmousedown<\/code>, <code class=\"fm-code-in-text1\">onmousemove<\/code>, <code class=\"fm-code-in-text1\">onmouseout<\/code>, <code class=\"fm-code-in-text1\">onmouseover<\/code>, <code class=\"fm-code-in-text1\">onmouseup<\/code>, <code class=\"fm-code-in-text1\">onmousewheel<\/code>, <code class=\"fm-code-in-text1\">onwheel<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PointerEventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ongotpointercapture<\/code>, <code class=\"fm-code-in-text1\">onlostpointercapture<\/code>, <code class=\"fm-code-in-text1\">onpointercancel<\/code>, <code class=\"fm-code-in-text1\">onpointerdown<\/code>, <code class=\"fm-code-in-text1\">onpointerenter<\/code>, <code class=\"fm-code-in-text1\">onpointerleave<\/code>, <code class=\"fm-code-in-text1\">onpointermove<\/code>, <code class=\"fm-code-in-text1\">onpointerout<\/code>, <code class=\"fm-code-in-text1\">onpointerover<\/code>, <code class=\"fm-code-in-text1\">onpointerup<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ProgressEventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">onabort<\/code>, <code class=\"fm-code-in-text1\">onload<\/code>, <code class=\"fm-code-in-text1\">onloadend<\/code>, <code class=\"fm-code-in-text1\">onloadstart<\/code>, <code class=\"fm-code-in-text1\">onprogress<\/code>, <code class=\"fm-code-in-text1\">ontimeout<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">TouchEventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ontouchcancel<\/code>, <code class=\"fm-code-in-text1\">ontouchend<\/code>, <code class=\"fm-code-in-text1\">ontouchenter<\/code>, <code class=\"fm-code-in-text1\">ontouchleave<\/code>, <code class=\"fm-code-in-text1\">ontouchmove<\/code>, <code class=\"fm-code-in-text1\">ontouchstart<\/code><\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">EventArgs<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">onactivate<\/code>, <code class=\"fm-code-in-text1\">onbeforeactivate<\/code>, <code class=\"fm-code-in-text1\">onbeforecopy<\/code>, <code class=\"fm-code-in-text1\">onbeforecut<\/code>, <code class=\"fm-code-in-text1\">onbeforedeactivate<\/code>, <code class=\"fm-code-in-text1\">onbeforepaste<\/code>, <code class=\"fm-code-in-text1\">oncanplay<\/code>, <code class=\"fm-code-in-text1\">oncanplaythrough<\/code>, <code class=\"fm-code-in-text1\">oncuechange<\/code>, <code class=\"fm-code-in-text1\">ondeactivate<\/code>, <code class=\"fm-code-in-text1\">ondurationchange<\/code>, <code class=\"fm-code-in-text1\">onemptied<\/code>, <code class=\"fm-code-in-text1\">onended<\/code>, <code class=\"fm-code-in-text1\">onfullscreenchange<\/code>, <code class=\"fm-code-in-text1\">onfullscreenerror<\/code>, <code class=\"fm-code-in-text1\">oninvalid<\/code>, <code class=\"fm-code-in-text1\">onloadeddata<\/code>, <code class=\"fm-code-in-text1\">onloadedmetadata<\/code>, <code class=\"fm-code-in-text1\">onpause<\/code>, <code class=\"fm-code-in-text1\">onplay<\/code>, <code class=\"fm-code-in-text1\">onplaying<\/code>, <code class=\"fm-code-in-text1\">onpointerlockchange<\/code>, <code class=\"fm-code-in-text1\">onpointerlockerror<\/code>, <code class=\"fm-code-in-text1\">onratechange<\/code>, <code class=\"fm-code-in-text1\">onreadystatechange<\/code>, <code class=\"fm-code-in-text1\">onreset<\/code>, <code class=\"fm-code-in-text1\">onscroll<\/code>, <code class=\"fm-code-in-text1\">onseeked<\/code>, <code class=\"fm-code-in-text1\">onseeking<\/code>, <code class=\"fm-code-in-text1\">onselect<\/code>, <code class=\"fm-code-in-text1\">onselectionchange<\/code>, <code class=\"fm-code-in-text1\">onselectstart<\/code>, <code class=\"fm-code-in-text1\">onstalled<\/code>, <code class=\"fm-code-in-text1\">onstop<\/code>, <code class=\"fm-code-in-text1\">onsubmit<\/code>, <code class=\"fm-code-in-text1\">onsuspend<\/code>, <code class=\"fm-code-in-text1\">ontimeupdate<\/code>, <code class=\"fm-code-in-text1\">onvolumechange<\/code>, <code class=\"fm-code-in-text1\">onwaiting<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The Blazor JavaScript code receives the event when it is triggered and forwards it to the server over the persistent HTTP connection. The handler method is invoked, and the state of the component is updated. Any changes to the content produced by the component\u2019s view section will be sent back to the JavaScript code, which will update the content displayed by the browser.<\/p>\n<p class=\"body\">In the example, the <code class=\"fm-code-in-text\">click<\/code> event will be handled by the <code class=\"fm-code-in-text\">IncrementCounter<\/code> method, which changes the value of the <code class=\"fm-code-in-text\">Counter<\/code> property. The value of the <code class=\"fm-code-in-text\">Counter<\/code> property is included in the HTML rendered by the component, so Blazor sends the changes to the browser so that the JavaScript code can update the HTML elements displayed to the user. To display the <code class=\"fm-code-in-text\">Events<\/code> component, replace the contents of the <code class=\"fm-code-in-text\">Blazor.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Pages<\/code> folder, as shown in listing 33.11.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.11 Using a new component in the Blazor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/blazor\"\n\n<b class=\"fm-bold\">&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;Events&lt;\/h4&gt;<\/b>\n\n<b class=\"fm-bold\">&lt;component type=\"typeof(Advanced.Blazor.Events)\" render-mode=\"Server\" \/&gt;<\/b><\/pre>\n<p class=\"body\">Listing 33.11 changes the <code class=\"fm-code-in-text\">type<\/code> attribute of the component element and removes the custom JavaScript and the <code class=\"fm-code-in-text\">button<\/code> element I used to mark elements in the previous example. Restart ASP.NET Core and request http:\/\/localhost:5000\/pages\/blazor to see the new component. Click the Increment button, and the <code class=\"fm-code-in-text\">click<\/code> event will be received by the Blazor JavaScript code and sent to the server for processing by the <code class=\"fm-code-in-text\">IncrementCounter<\/code> method, as shown in figure 33.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre354\" src=\"\/images\/proaspnetcore7\/000360.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.6 Handling an event<\/p>\n<\/div>\n<p class=\"fm-head2\">Handling events from multiple elements<\/p>\n<p class=\"body\">To avoid code duplication, elements from multiple elements can be received by a single handler method, as shown in listing 33.12.<a id=\"calibre_link-2728\"><\/a><a id=\"calibre_link-824\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.12 Handling events in the Events.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"m-2 p-2 border\"&gt;\n    <b class=\"fm-bold\">&lt;button class=\"btn btn-primary\"<\/b> \n            <b class=\"fm-bold\">@onclick=\"@(e =&gt; IncrementCounter(e, 0))\"&gt;<\/b>\n        <b class=\"fm-bold\">Increment Counter #1<\/b>\n    <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n    <b class=\"fm-bold\">&lt;span class=\"p-2\"&gt;Counter Value: @Counter[0]&lt;\/span&gt;<\/b>\n&lt;\/div&gt;\n\n&lt;div class=\"m-2 p-2 border\"&gt;\n    <b class=\"fm-bold\">&lt;button class=\"btn btn-primary\"<\/b> \n            <b class=\"fm-bold\">@onclick=\"@(e =&gt; IncrementCounter(e, 1))\"&gt;<\/b>\n        <b class=\"fm-bold\">Increment Counter #2<\/b>\n    <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n    <b class=\"fm-bold\">&lt;span class=\"p-2\"&gt;Counter Value: @Counter[1]&lt;\/span&gt;<\/b>\n&lt;\/div&gt;\n\n@code {\n    <b class=\"fm-bold\">public int[] Counter { get; set; } = new int[] { 1, 1 };<\/b>\n        \n    <b class=\"fm-bold\">public void IncrementCounter(MouseEventArgs e, int index) {<\/b>\n        <b class=\"fm-bold\">Counter[index]++;<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">Blazor event attributes can be used with lambda functions that receive the <code class=\"fm-code-in-text\">EventArgs<\/code> object and invoke a handler method with additional arguments. In this example, I have added an <code class=\"fm-code-in-text\">index<\/code> parameter to the <code class=\"fm-code-in-text\">IncrementCounter<\/code> method, which is used to determine which counter value should be updated. The value for the argument is defined in the <code class=\"fm-code-in-text\">@onclick<\/code> attribute, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;button class=\"btn btn-primary\" @onclick=\"<b class=\"fm-bold\">@(e =&gt; IncrementCounter(e, 0))<\/b>\"&gt;\n...<\/pre>\n<p class=\"body\">This technique can also be used when elements are generated programmatically, as shown in listing 33.13. In this example, I use an <code class=\"fm-code-in-text\">@for<\/code> expression to generate elements and use the loop variable as the argument to the handler method. I have also removed the <code class=\"fm-code-in-text\">EventArgs<\/code> parameter from the handler method, which isn\u2019t being used.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Avoiding the handler method name pitfall<\/p>\n<p class=\"fm-sidebar-text\">The most common mistake when specifying an event handler method is to include parentheses, like this:<a id=\"calibre_link-2729\"><\/a><a id=\"calibre_link-823\"><\/a><\/p>\n<pre class=\"programlisting\">...\n&lt;button class=\"btn btn-primary\" @onclick=\"<b class=\"fm-bold\">IncrementCounter()<\/b>\"&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">The error message this produces will depend on the event handler method. You may see a warning telling you a formal parameter is missing or that <code class=\"fm-code-in-text1\">void<\/code> cannot be converted to an <code class=\"fm-code-in-text1\">EventCallback<\/code>. When specifying a handler method, you must specify just the event name, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;button class=\"btn btn-primary\" @onclick=\"<b class=\"fm-bold\">IncrementCounter<\/b>\"&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">You can specify the method name as a Razor expression, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;button class=\"btn btn-primary\" @onclick=\"<b class=\"fm-bold\">@IncrementCounter<\/b>\"&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">Some developers find this easier to parse, but the result is the same. A different set of rules applies when using a lambda function, which must be defined within a Razor expression, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;button class=\"btn btn-primary\" @onclick=\"<b class=\"fm-bold\">@( ... )<\/b>\"&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">Within the Razor expression, the lambda function is defined as it would be in a C# class, which means defining the parameters, followed by the \"goes to\" arrow, followed by the function body, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;button class=\"btn btn-primary\" @onclick=\"<b class=\"fm-bold\">@((e) =&gt; HandleEvent(e, local))<\/b>\"&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">If you don\u2019t need to use the <code class=\"fm-code-in-text1\">EventArgs<\/code> object, then you can omit the parameter from the lambda function, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;button class=\"btn btn-primary\" @onclick=\"<b class=\"fm-bold\">@(() =&gt;<\/b>\n    <b class=\"fm-bold\">IncrementCounter(local))<\/b>\"&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">You will quickly become used to these rules as you start to work with Blazor, even if they seem inconsistent at first.<\/p>\n<\/div>\n<p class=\"fm-code-listing-caption\">Listing 33.13 Generating elements in the Events.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@for (int i = 0; i &lt; ElementCount; i++) {<\/b>\n    <b class=\"fm-bold\">int local = i;<\/b>\n    <b class=\"fm-bold\">&lt;div class=\"m-2 p-2 border\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;button class=\"btn btn-primary\"<\/b> \n                <b class=\"fm-bold\">@onclick=\"@(() =&gt; IncrementCounter(local))\"&gt;<\/b>\n            <b class=\"fm-bold\">Increment Counter #@(i + 1)<\/b>\n        <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n        <b class=\"fm-bold\">&lt;span class=\"p-2\"&gt;Counter Value: @GetCounter(i)&lt;\/span&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n<b class=\"fm-bold\">}<\/b>\n\n<b class=\"fm-bold\">@code {<\/b>\n    <b class=\"fm-bold\">public int ElementCount { get; set; } = 4;<\/b>\n        \n    <b class=\"fm-bold\">public Dictionary&lt;int, int&gt; Counters { get; }<\/b> \n        <b class=\"fm-bold\">= new Dictionary&lt;int, int&gt;();<\/b>\n                \n    <b class=\"fm-bold\">public int GetCounter(int index) =&gt;<\/b>\n        <b class=\"fm-bold\">Counters.ContainsKey(index) ? Counters[index] : 0;<\/b>\n                \n    <b class=\"fm-bold\">public void IncrementCounter(int index) =&gt;<\/b>\n        <b class=\"fm-bold\">Counters[index] = GetCounter(index) + 1;<\/b>\n<b class=\"fm-bold\">}<\/b><\/pre>\n<p class=\"body\">The important point to understand about event handlers is that the <code class=\"fm-code-in-text\">@onclick<\/code> lambda function isn\u2019t evaluated until the server receives the <code class=\"fm-code-in-text\">click<\/code> event from the browser. This means care must be taken not to use the loop variable <code class=\"fm-code-in-text\">i<\/code> as the argument to the <code class=\"fm-code-in-text\">IncrementCounter<\/code> method because it will always be the final value produced by the loop, which would be <code class=\"fm-code-in-text\">4<\/code> in this case. Instead, you must capture the loop variable in a local variable, like this:<\/p>\n<pre class=\"programlisting\">...\nint local = i;\n...<\/pre>\n<p class=\"body\">The local variable is then used as the argument to the event handler method in the attribute, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; IncrementCounter(<b class=\"fm-bold\">local<\/b>))\"&gt;\n...<\/pre>\n<p class=\"body\">The local variable fixes the value for the lambda function for each of the generated elements. Restart ASP.NET Core and use a browser to request http:\/\/localhost:5000\/pages\/blazor, which will produce the response shown in figure 33.7. The <code class=\"fm-code-in-text\">click<\/code> events produced by all the <code class=\"fm-code-in-text\">button<\/code> elements are handled by the same method, but the argument provided by the lambda function ensures that the correct counter is updated.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre355\" src=\"\/images\/proaspnetcore7\/000361.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.7 Handling events from multiple elements<\/p>\n<\/div>\n<p class=\"fm-head2\">Processing events without a handler method<\/p>\n<p class=\"body\">Simple event handling can be done directly in a lambda function, without using a handler method, as shown in listing 33.14.<a id=\"calibre_link-2730\"><\/a><a id=\"calibre_link-825\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.14 Handling events in the Events.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@for (int i = 0; i &lt; ElementCount; i++) {\n    int local = i;\n    &lt;div class=\"m-2 p-2 border\"&gt;\n        &lt;button class=\"btn btn-primary\" \n                @onclick=\"@(() =&gt; IncrementCounter(local))\"&gt;\n            Increment Counter #@(i + 1)\n        &lt;\/button&gt;\n        <b class=\"fm-bold\">&lt;button class=\"btn btn-info\"<\/b> \n                <b class=\"fm-bold\">@onclick=\"@(() =&gt; Counters.Remove(local))\"&gt;<\/b>\n            <b class=\"fm-bold\">Reset<\/b>\n        <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n        &lt;span class=\"p-2\"&gt;Counter Value: @GetCounter(i)&lt;\/span&gt;\n    &lt;\/div&gt;\n}\n\n@code {\n    public int ElementCount { get; set; } = 4;\n        \n    public Dictionary&lt;int, int&gt; Counters { get; } \n        = new Dictionary&lt;int, int&gt;();\n                \n    public int GetCounter(int index) =&gt; \n        Counters.ContainsKey(index) ? Counters[index] : 0;\n                \n    public void IncrementCounter(int index)  =&gt;\n        Counters[index] = GetCounter(index) + 1;\n}<\/pre>\n<p class=\"body\">Complex handlers should be defined as methods, but this approach is more concise for simple handlers. Restart ASP.NET Core and request http:\/\/localhost:5000\/pages\/blazor. The Reset buttons remove values from the <code class=\"fm-code-in-text\">Counters<\/code> collection without relying on a method in the <code class=\"fm-code-in-text\">@code<\/code> section of the component, as shown in figure 33.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre356\" src=\"\/images\/proaspnetcore7\/000362.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.8 Handling events in a lambda expression<\/p>\n<\/div>\n<p class=\"fm-head2\">Preventing default events and event propagation<\/p>\n<p class=\"body\">Blazor provides two attributes that alter the default behavior of events in the browser, as described in table 33.5. These attributes, where the name of the event is followed by a colon and then a keyword, are known as <i class=\"fm-italics\">parameters<\/i>.<a id=\"calibre_link-2731\"><\/a><a id=\"calibre_link-2732\"><\/a><a id=\"calibre_link-2733\"><\/a><a id=\"calibre_link-2734\"><\/a><a id=\"calibre_link-784\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 33.5 The event configuration parameters<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2735\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@on{event}:preventDefault<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This parameter determines whether the default event for an element is triggered.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@on{event}:stopPropagation<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This parameter determines whether an event is propagated to its ancestor elements.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Listing 33.15 demonstrates what these parameters do and why they are useful.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.15 Overriding event defaults in the Events.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">&lt;form action=\"\/pages\/blazor\" method=\"get\"&gt;<\/b>\n    @for (int i = 0; i &lt; ElementCount; i++) {\n        int local = i;\n        &lt;div class=\"m-2 p-2 border\"&gt;\n            &lt;button class=\"btn btn-primary\"\n                    @onclick=\"@(() =&gt; IncrementCounter(local))\"\n                    <b class=\"fm-bold\">@onclick:preventDefault=\"EnableEventParams\"&gt;<\/b>\n                Increment Counter #@(i + 1)\n            &lt;\/button&gt;\n            &lt;button class=\"btn btn-info\" \n                    @onclick=\"@(() =&gt; Counters.Remove(local))\"&gt;\n                Reset\n            &lt;\/button&gt;\n            &lt;span class=\"p-2\"&gt;Counter Value: @GetCounter(i)&lt;\/span&gt;\n        &lt;\/div&gt;\n    }\n        \n<b class=\"fm-bold\">&lt;\/form&gt;<\/b>\n\n<b class=\"fm-bold\">&lt;div class=\"m-2\" @onclick=\"@(() =&gt; IncrementCounter(1))\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;button class=\"btn btn-primary\"<\/b> \n            <b class=\"fm-bold\">@onclick=\"@(() =&gt; IncrementCounter(0))\"<\/b>\n            <b class=\"fm-bold\">@onclick:stopPropagation=\"EnableEventParams\"&gt;<\/b>\n        <b class=\"fm-bold\">Propagation Test<\/b>\n    <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n\n<b class=\"fm-bold\">&lt;div class=\"form-check m-2\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;input class=\"form-check-input\" type=\"checkbox\"<\/b>\n           <b class=\"fm-bold\">@onchange=\"@(() =&gt; EnableEventParams = !EnableEventParams)\" \/&gt;<\/b>\n    <b class=\"fm-bold\">&lt;label class=\"form-check-label\"&gt;Enable Event Parameters&lt;\/label&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n\n@code {\n    public int ElementCount { get; set; } = 4;\n        \n    public Dictionary&lt;int, int&gt; Counters { get; } \n        = new Dictionary&lt;int, int&gt;();\n                \n    public int GetCounter(int index) =&gt;\n        Counters.ContainsKey(index) ? Counters[index] : 0;\n                \n    public void IncrementCounter(int index) =&gt;\n        Counters[index] = GetCounter(index) + 1;\n                \n    <b class=\"fm-bold\">public bool EnableEventParams { get; set; } = false;<\/b>\n}<\/pre>\n<p class=\"body\">This example creates two situations in which the default behavior of events in the browser can cause problems. The first is caused by adding a <code class=\"fm-code-in-text\">form<\/code> element. By default, <code class=\"fm-code-in-text\">button<\/code> elements contained in a form will submit that form when they are clicked, even when the <code class=\"fm-code-in-text\">@onclick<\/code> attribute is present. This means that whenever one of the Increment Counter buttons is clicked, the browser will send the form data to the ASP.NET Core server, which will respond with the contents of the <code class=\"fm-code-in-text\">Blazor.cshtml<\/code> Razor Page.<\/p>\n<p class=\"body\">The second problem is demonstrated by an element whose parent also defines an event handler, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"m-2\" <b class=\"fm-bold\">@onclick=\"@(() =&gt; IncrementCounter(1))\"<\/b>&gt;\n    &lt;button class=\"btn btn-primary\" <b class=\"fm-bold\">@onclick=\"@(() =&gt; IncrementCounter(0))\"<\/b>\n...<\/pre>\n<p class=\"body\">Events go through a well-defined lifecycle in the browser, which includes being passed up the chain of ancestor elements. In the example, this means clicking the <code class=\"fm-code-in-text\">button<\/code> will cause two counters to be updated, once by the <code class=\"fm-code-in-text\">@onclick<\/code> handler for the <code class=\"fm-code-in-text\">button<\/code> element and once by the <code class=\"fm-code-in-text\">@onclick<\/code> handler for the enclosing <code class=\"fm-code-in-text\">div<\/code> element.<\/p>\n<p class=\"body\">To see these problems, restart ASP.NET Core and request http:\/\/localhost:5000\/pages\/blazor. Click an Increment Counter button; you will see that the form is submitted and the page is essentially reloaded. Click the Propagation Test button, and you will see that two counters are updated. Figure 33.9 shows both problems.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre357\" src=\"\/images\/proaspnetcore7\/000363.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.9 Problems caused by the default behavior of events in the browser<\/p>\n<\/div>\n<p class=\"body\">The checkbox in listing 33.15 toggles the property that applies the parameters described in table 33.5, with the effect that the form isn\u2019t submitted and only the handler on the button element receives the event. To see the effect, check the checkbox and then click an Increment Counter button and the Propagation Test buttons, which produces the result shown in figure 33.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre358\" src=\"\/images\/proaspnetcore7\/000364.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.10 Overriding the default behavior of events in the browser<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-623\">33.4.2 Working with data bindings<\/h3>\n<p class=\"body\">Event handlers and Razor expressions can be used to create a two-way relationship between an HTML element and a C# value, which is useful for elements that allow users to make changes, such as <code class=\"fm-code-in-text\">input<\/code> and <code class=\"fm-code-in-text\">select<\/code> elements. Add a Razor Component named <code class=\"fm-code-in-text\">Bindings.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder with the content shown in listing 33.16.<a id=\"calibre_link-2736\"><\/a><a id=\"calibre_link-789\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.16 The contents of the Bindings.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;City:&lt;\/label&gt;\n    &lt;input class=\"form-control\" value=\"@City\" @onchange=\"UpdateCity\" \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"p-2 mb-2\"&gt;City Value: @City&lt;\/div&gt;\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; City = \"Paris\")\"&gt;\n    Paris\n&lt;\/button&gt;\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; City = \"Chicago\")\"&gt;\n    Chicago\n&lt;\/button&gt;\n\n@code {\n\n    public string? City { get; set; } = \"London\";\n        \n    public void UpdateCity(ChangeEventArgs e) {\n        City = e.Value as string;\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@onchange<\/code> attribute registers the <code class=\"fm-code-in-text\">UpdateCity<\/code> method as a handler for the <code class=\"fm-code-in-text\">change<\/code> event from the <code class=\"fm-code-in-text\">input<\/code> element. The events are described using the <code class=\"fm-code-in-text\">ChangeEventArgs<\/code> class, which provides a <code class=\"fm-code-in-text\">Value<\/code> property. Each time a <code class=\"fm-code-in-text\">change<\/code> event is received, the <code class=\"fm-code-in-text\">City<\/code> property is updated with the contents of the <code class=\"fm-code-in-text\">input<\/code> element.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">input<\/code> element\u2019s <code class=\"fm-code-in-text\">value<\/code> attribute creates a relationship in the other direction so that when the value of the <code class=\"fm-code-in-text\">City<\/code> property changes, so does the element\u2019s <code class=\"fm-code-in-text\">value<\/code> attribute, which changes the text displayed to the user. To apply the new Razor Component, change the <code class=\"fm-code-in-text\">component<\/code> attribute in the Razor Page, as shown in listing 33.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.17 Using a Razor Component in the Blazor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/blazor\"\n\n&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;Events&lt;\/h4&gt;\n\n<b class=\"fm-bold\">&lt;component type=\"typeof(Advanced.Blazor.Bindings)\" render-mode=\"Server\" \/&gt;<\/b><\/pre>\n<p class=\"body\">To see both parts of the relationship defined by the binding in listing 33.16, restart ASP.NET Core, navigate to http:\/\/localhost:5000\/pages\/blazor, and edit the content of the <code class=\"fm-code-in-text\">input<\/code> element. The <code class=\"fm-code-in-text\">change<\/code> event is triggered only when the <code class=\"fm-code-in-text\">input<\/code> element loses the focus, so once you have finished editing, press the Tab key or click outside of the <code class=\"fm-code-in-text\">input<\/code> element; you will see the value you entered displayed through the Razor expression in the <code class=\"fm-code-in-text\">div<\/code> element, as shown on the left of figure 33.11. Click one of the buttons, and the <code class=\"fm-code-in-text\">City<\/code> property will be changed to <code class=\"fm-code-in-text\">Paris<\/code> or <code class=\"fm-code-in-text\">Chicago<\/code>, and the selected value will be displayed by both the <code class=\"fm-code-in-text\">div<\/code> element and the <code class=\"fm-code-in-text\">input<\/code> element, as shown on the right of the figure.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre359\" src=\"\/images\/proaspnetcore7\/000365.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.11 Creating a two-way relationship between an element and a property<\/p>\n<\/div>\n<p class=\"body\">Two-way relationships involving the <code class=\"fm-code-in-text\">change<\/code> event can be expressed as data bindings, which allows both the value and the event to be configured with a single attribute, as shown in listing 33.18.<a id=\"calibre_link-2737\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.18 Using a data binding in the Bindings.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;City:&lt;\/label&gt;\n    <b class=\"fm-bold\">&lt;input class=\"form-control\" @bind=\"City\" \/&gt;<\/b>\n&lt;\/div&gt;\n&lt;div class=\"p-2 mb-2\"&gt;City Value: @City&lt;\/div&gt;\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; City = \"Paris\")\"&gt;\n    Paris\n&lt;\/button&gt;\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; City = \"Chicago\")\"&gt;\n    Chicago\n&lt;\/button&gt;\n\n@code {\n\n    public string? City { get; set; } = \"London\";\n        \n    <b class=\"fm-bold\">\/\/public void UpdateCity(ChangeEventArgs e) {<\/b>\n    <b class=\"fm-bold\">\/\/    City = e.Value as string;<\/b>\n    <b class=\"fm-bold\">\/\/}<\/b>\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@bind<\/code> attribute is used to specify the property that will be updated when the change event is triggered and that will update the <code class=\"fm-code-in-text\">value<\/code> attribute when it changes. The effect in listing 33.18 is the same as listing 33.16 but expressed more concisely and without the need for a handler method or a lambda function to update the property.<\/p>\n<p class=\"fm-head2\">Changing the binding event<\/p>\n<p class=\"body\">By default, the <code class=\"fm-code-in-text\">change<\/code> event is used in bindings, which provides reasonable responsiveness for the user without requiring too many updates from the server. The event used in a binding can be changed by using the attributes described in table 33.6.<a id=\"calibre_link-2738\"><\/a><a id=\"calibre_link-2739\"><\/a><a id=\"calibre_link-790\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 33.6 The binding attributes for specifying an event<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2740\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Attribute<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@bind-value<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to select the property for the data binding.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@bind-value:event<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to select the event for the data binding.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">These attributes are used instead of <code class=\"fm-code-in-text\">@bind<\/code>, as shown in listing 33.19, but can be used only with events that are represented with the <code class=\"fm-code-in-text\">ChangeEventArgs<\/code> class. This means that only the <code class=\"fm-code-in-text\">onchange<\/code> and <code class=\"fm-code-in-text\">oninput<\/code> events can be used, at least in the current release.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.19 Specifying an event in the Bindings.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;City:&lt;\/label&gt;\n    <b class=\"fm-bold\">&lt;input class=\"form-control\" @bind-value=\"City\"<\/b>\n           <b class=\"fm-bold\">@bind-value:event=\"oninput\" \/&gt;<\/b>\n&lt;\/div&gt;\n&lt;div class=\"p-2 mb-2\"&gt;City Value: @City&lt;\/div&gt;\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; City = \"Paris\")\"&gt;\n    Paris\n&lt;\/button&gt;\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; City = \"Chicago\")\"&gt;\n    Chicago\n&lt;\/button&gt;\n\n@code {\n    public string? City { get; set; } = \"London\";\n}<\/pre>\n<p class=\"body\">This combination of attributes creates a binding for the <code class=\"fm-code-in-text\">City<\/code> property that is updated when the <code class=\"fm-code-in-text\">oninput<\/code> event is triggered, which happens after every keystroke, rather than only when the <code class=\"fm-code-in-text\">input<\/code> element loses the focus. To see the effect, restart ASP.NET Core, navigate to http:\/\/localhost:5000\/pages\/blazor, and start typing into the <code class=\"fm-code-in-text\">input<\/code> element. The <code class=\"fm-code-in-text\">City<\/code> property will be updated after every keystroke, as shown in figure 33.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre360\" src=\"\/images\/proaspnetcore7\/000366.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.12 Changing the event in a data binding<\/p>\n<\/div>\n<p class=\"fm-head2\">Creating DateTime bindings<\/p>\n<p class=\"body\">Blazor has special support for creating bindings for <code class=\"fm-code-in-text\">DateTime<\/code> properties, allowing them to be expressed using a specific culture or a format string. This feature is applied using the parameters described in table 33.7.<a id=\"calibre_link-2741\"><\/a><a id=\"calibre_link-791\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> If you have used the <code class=\"fm-code-in-text1\">@bind-value<\/code> and <code class=\"fm-code-in-text1\">@bind-value:event<\/code> attributes to select an event, then you must use the <code class=\"fm-code-in-text1\">@bind-value:culture<\/code> and <code class=\"fm-code-in-text1\">@bind-value:format<\/code> parameters instead.<\/p>\n<p class=\"fm-table-caption\">Table 33.7 The DateTime parameters<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2742\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@bind:culture<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to select a <code class=\"fm-code-in-text1\">CultureInfo<\/code> object that will be used to format the <code class=\"fm-code-in-text1\">DateTime<\/code> value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">@bind:format<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute is used to specify a data formatting string that will be used to format the <code class=\"fm-code-in-text1\">DateTime<\/code> value.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Listing 33.20 shows the use of these attributes with a <code class=\"fm-code-in-text\">DateTime<\/code> property.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The formatting strings used in these examples are described at <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.datetime\">https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.datetime<\/a>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.20 Using a DateTime property in the Bindings.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@using System.Globalization<\/b>\n\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;City:&lt;\/label&gt;\n    &lt;input class=\"form-control\" @bind-value=\"City\" \n        @bind-value:event=\"oninput\" \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"p-2 mb-2\"&gt;City Value: @City&lt;\/div&gt;\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; City = \"Paris\")\"&gt;\n    Paris\n&lt;\/button&gt;\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; City = \"Chicago\")\"&gt;\n    Chicago\n&lt;\/button&gt;\n\n<b class=\"fm-bold\">&lt;div class=\"form-group mt-2\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;label&gt;Time:&lt;\/label&gt;<\/b>\n    <b class=\"fm-bold\">&lt;input class=\"form-control my-1\" @bind=\"Time\"<\/b> \n        <b class=\"fm-bold\">@bind:culture=\"Culture\" @bind:format=\"MMM-dd\" \/&gt;<\/b>\n    <b class=\"fm-bold\">&lt;input class=\"form-control my-1\" @bind=\"Time\"<\/b> \n            <b class=\"fm-bold\">@bind:culture=\"Culture\" \/&gt;<\/b>\n    <b class=\"fm-bold\">&lt;input class=\"form-control\" type=\"date\" @bind=\"Time\" \/&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n<b class=\"fm-bold\">&lt;div class=\"p-2 mb-2\"&gt;Time Value: @Time&lt;\/div&gt;<\/b>\n\n<b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;label&gt;Culture:&lt;\/label&gt;<\/b>\n    <b class=\"fm-bold\">&lt;select class=\"form-control\" @bind=\"Culture\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;option value=\"@CultureInfo.GetCultureInfo(\"en-us\")\"&gt;<\/b>\n            <b class=\"fm-bold\">en-US<\/b>\n        <b class=\"fm-bold\">&lt;\/option&gt;<\/b>\n        <b class=\"fm-bold\">&lt;option value=\"@CultureInfo.GetCultureInfo(\"en-gb\")\"&gt;<\/b>\n            <b class=\"fm-bold\">en-GB<\/b>\n        <b class=\"fm-bold\">&lt;\/option&gt;<\/b>\n        <b class=\"fm-bold\">&lt;option value=\"@CultureInfo.GetCultureInfo(\"fr-fr\")\"&gt;<\/b>\n            <b class=\"fm-bold\">fr-FR<\/b>\n        <b class=\"fm-bold\">&lt;\/option&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/select&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n\n@code {\n    public string? City { get; set; } = \"London\";\n        \n    <b class=\"fm-bold\">public DateTime Time { get; set; }<\/b> \n        <b class=\"fm-bold\">= DateTime.Parse(\"2050\/01\/20 09:50\");<\/b>\n                \n    <b class=\"fm-bold\">public CultureInfo Culture { get; set; }<\/b> \n        <b class=\"fm-bold\">= CultureInfo.GetCultureInfo(\"en-us\");<\/b>\n}<\/pre>\n<p class=\"body\">There are three input elements that are used to display the same <code class=\"fm-code-in-text\">DataTime<\/code> value, two of which have been configured using the attributes from table 33.7. The first element has been configured with a culture and a format string, like this:<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">&lt;input class=\"form-control my-1\" @bind=\"Time\" @bind:culture=\"Culture\"<\/b>\n    <b class=\"fm-bold\">@bind:format=\"MMM-dd\" \/&gt;<\/b>\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">DateTime<\/code> property is displayed using the culture picked in the <code class=\"fm-code-in-text\">select<\/code> element and with a format string that displays an abbreviated month name and the numeric date. The second <code class=\"fm-code-in-text\">input<\/code> element specifies just a culture, which means the default formatting string will be used.<\/p>\n<pre class=\"programlisting\">...\n&lt;input class=\"form-control my-1\" @bind=\"Time\" <b class=\"fm-bold\">@bind:culture=\"Culture\"<\/b> \/&gt;\n...<\/pre>\n<p class=\"body\">To see how dates are displayed, restart ASP.NET Core, request http:\/\/localhost:5000\/pages\/blazor, and use the <code class=\"fm-code-in-text\">select<\/code> element to pick different culture settings. The settings available represent English as it is used in the United States, English as it used in the United Kingdom, and French as it is used in France. Figure 33.13 shows the formatting each produces.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre361\" src=\"\/images\/proaspnetcore7\/000367.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.13 Formatting DateTime values<\/p>\n<\/div>\n<p class=\"body\">The initial locale in this example is <code class=\"fm-code-in-text\">en-US<\/code>. When you switch to <code class=\"fm-code-in-text\">en-GB<\/code>, the order in which the month and date appear changes. When you switch to <code class=\"fm-code-in-text\">en-FR<\/code>, the abbreviated month name changes.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Letting the browser format dates<\/p>\n<p class=\"fm-sidebar-text\">Notice that the value displayed by the third <code class=\"fm-code-in-text1\">input<\/code> element in listing 33.20 doesn\u2019t change, regardless of the locale you choose. This <code class=\"fm-code-in-text1\">input<\/code> element has neither of the attributes described in table 33.7 but does have its <code class=\"fm-code-in-text1\">type<\/code> attribute set to <code class=\"fm-code-in-text1\">date<\/code>, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;input class=\"form-control\" <b class=\"fm-bold\">type=\"date\"<\/b> @bind=\"Time\" \/&gt;\n...<\/pre>\n<p class=\"fm-sidebar-text\">You should not specify a culture or a format string when setting the type attribute to <code class=\"fm-code-in-text1\">date<\/code>, <code class=\"fm-code-in-text1\">datetime-local<\/code>, <code class=\"fm-code-in-text1\">month<\/code>, or <code class=\"fm-code-in-text1\">time<\/code>, because Blazor will automatically format date values into a culture-neutral format that the browser translates into the user\u2019s locale. Figure 33.11 shows how the date is formatted in the <code class=\"fm-code-in-text1\">en-US<\/code> locale, but the user will see the date expressed in their local convention.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-624\">33.5 Using class files to define components<\/h2>\n<p class=\"body\">If you don\u2019t like the mix of code and markup that Razor Components supports, you can use C# class files to define part, or all, of the component.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-625\">33.5.1 Using a code-behind class<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@code<\/code> section of a Razor Component can be defined in a separate class file, known as a <i class=\"fm-italics\">code-behind class<\/i> or <i class=\"fm-italics\">code-behind file<\/i>. Code-behind classes for Razor Components are defined as <code class=\"fm-code-in-text\">partial<\/code> classes with the same name as the component they provide code for.<\/p>\n<p class=\"body\">Add a Razor Component named <code class=\"fm-code-in-text\">Split.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder with the content shown in listing 33.21.<a id=\"calibre_link-2743\"><\/a><a id=\"calibre_link-816\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.21 The contents of the Split.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;ul class=\"list-group\"&gt;\n    @foreach (string name in Names) {\n        &lt;li class=\"list-group-item\"&gt;@name&lt;\/li&gt;\n    }\n&lt;\/ul&gt;<\/pre>\n<p class=\"body\">This file contains only HTML content and Razor expressions and renders a list of names that it expects to receive through a <code class=\"fm-code-in-text\">Names<\/code> property. To provide the component with its code, add a class file named <code class=\"fm-code-in-text\">Split.razor.cs<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder and use it to define the partial class shown in listing 33.22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.22 The contents of the Split.razor.cs file in the Blazor folder<\/p>\n<pre class=\"programlisting\">using Advanced.Models;\nusing Microsoft.AspNetCore.Components;\n\nnamespace Advanced.Blazor {\n\n    public partial class Split {\n        \n        [Inject]\n        public DataContext? Context { get; set; }\n                \n        public IEnumerable&lt;string&gt; Names =&gt; \n            Context?.People.Select(p =&gt; p.Firstname) \n                ?? Enumerable.Empty&lt;string&gt;();\n    }\n}<\/pre>\n<p class=\"body\">The partial class must be defined in the same namespace as its Razor Component and have the same name. For this example, that means the namespace is <code class=\"fm-code-in-text\">Advanced.Blazor<\/code>, and the class name is <code class=\"fm-code-in-text\">Splt<\/code>. Code-behind classes do not define constructors and receive services using the <code class=\"fm-code-in-text\">Inject<\/code> attribute. Listing 33.23 applies the new component.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.23 Applying a new component in the Blazor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/blazor\"\n\n<b class=\"fm-bold\">&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;Code-Behind&lt;\/h4&gt;<\/b>\n\n<b class=\"fm-bold\">&lt;component type=\"typeof(Advanced.Blazor.Split)\" render-mode=\"Server\" \/&gt;<\/b><\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/pages\/blazor, and you will see the response shown in figure 33.14.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre362\" src=\"\/images\/proaspnetcore7\/000368.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.14 Using a code behind class to define a Razor Component<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-626\">33.5.2 Defining a Razor Component class<\/h3>\n<p class=\"body\">Razor Components can be defined entirely in a class file, although this can be less expressive than using Razor expressions. Add a class file named <code class=\"fm-code-in-text\">CodeOnly.cs<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder and use it to define the class shown in listing 33.24.<a id=\"calibre_link-2744\"><\/a><a id=\"calibre_link-805\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.24 The contents of the CodeOnly.cs file in the Blazor folder<\/p>\n<pre class=\"programlisting\">using Advanced.Models;\nusing Microsoft.AspNetCore.Components;\nusing Microsoft.AspNetCore.Components.Rendering;\nusing Microsoft.AspNetCore.Components.Web;\n\nnamespace Advanced.Blazor {\n\n    public class CodeOnly : ComponentBase {\n        \n        [Inject]\n        public DataContext? Context { get; set; }\n                \n        public IEnumerable&lt;string&gt; Names =&gt; \n            Context?.People.Select(p =&gt; p.Firstname)\n                ?? Enumerable.Empty&lt;string&gt;();\n                                \n        public bool Ascending { get; set; } = false;\n                \n        protected override void BuildRenderTree(\n            RenderTreeBuilder builder) {\n                IEnumerable&lt;string&gt; data = Ascending\n                    ? Names.OrderBy(n =&gt; n) \n                    : Names.OrderByDescending(n =&gt; n);\n                                        \n            builder.OpenElement(1, \"button\");\n            builder.AddAttribute(2, \"class\", \"btn btn-primary mb-2\");\n            builder.AddAttribute(3, \"onclick\",\n                EventCallback.Factory.Create&lt;MouseEventArgs&gt;(this,\n                    () =&gt; Ascending = !Ascending));\n            builder.AddContent(4, new MarkupString(\"Toggle\"));\n            builder.CloseElement();\n                        \n            builder.OpenElement(5, \"ul\");\n            builder.AddAttribute(6, \"class\", \"list-group\");\n            foreach (string name in data) {\n                builder.OpenElement(7, \"li\");\n                builder.AddAttribute(8, \"class\", \"list-group-item\");\n                builder.AddContent(9, new MarkupString(name));\n                builder.CloseElement();\n            }\n            builder.CloseElement();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The base class for components is <code class=\"fm-code-in-text\">ComponentBase<\/code>. The content that would normally be expressed as annotated HTML elements is created by overriding the <code class=\"fm-code-in-text\">BuildRenderTree<\/code> method and using the <code class=\"fm-code-in-text\">RenderTreeBuilder<\/code> parameter. Creating content can be awkward because each element is created and configured using multiple code statements, and each statement must have a sequence number that the compiler uses to match up code and content. The <code class=\"fm-code-in-text\">OpenElement<\/code> method starts a new element, which is configured using the <code class=\"fm-code-in-text\">AddElement<\/code> and <code class=\"fm-code-in-text\">AddContent<\/code> methods and then completed with the <code class=\"fm-code-in-text\">CloseElement<\/code> method. All the features available in regular Razor Components are available, including events and bindings, which are set up by adding attributes to elements, just as if they were defined literally in a <code class=\"fm-code-in-text\">.razor<\/code> file. The component in listing 33.24 displays a list of sorted names, with the sort direction altered when a <code class=\"fm-code-in-text\">button<\/code> element is clicked. Listing 33.25 applies the component so that it will be displayed to the user.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 33.25 Applying a new component in the Blazor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/blazor\"\n\n<b class=\"fm-bold\">&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;Class Only&lt;\/h4&gt;<\/b>\n\n<b class=\"fm-bold\">&lt;component type=\"typeof(Advanced.Blazor.CodeOnly)\" render-mode=\"Server\" \/&gt;<\/b><\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/pages\/blazor to see the content produced by the class-based Razor Component. When you click the button, the sort direction of the names in the list is changed, as shown in figure 33.15.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre363\" src=\"\/images\/proaspnetcore7\/000369.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 33.15 Defining a component entirely in code<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2745\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Blazor adds client-side interactivity to ASP.NET Core by introducing JavaScript to efficiently perform updates.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Blazor functionality is created in Razor Components, which follow a similar syntax to Razor views and Razor Pages.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Razor Components can respond to user interaction by handling JavaScript events, which are used to invoke server-side methods and generate selective updates.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Razor Components are usually defined with the markup and code in a single file but can use a separate class file and even defined entirely in C#.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-627\">\n<div class=\"calibre1\" id=\"calibre_link-2746\">\n<h1 class=\"tochead\" id=\"calibre_link-2747\"><a id=\"calibre_link-2748\"><\/a><a id=\"calibre_link-2749\"><\/a>34 Using Blazor Server, part 2<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Composing elements to combine Blazor components<\/li>\n<li class=\"co-summary-bullet\">Configuring components using attributes<\/li>\n<li class=\"co-summary-bullet\">Displaying child content and creating templates<\/li>\n<li class=\"co-summary-bullet\">Managing connection errors and application errors<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I continue to describe Blazor Server, focusing on the way that Razor Components can be used together to create more complex features. Table 34.1 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 34.1 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2750\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating complex features using Blazor<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Combine components to reduce duplication.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">3, 4<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Configuring a component<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">Parameter<\/code> attribute to receive a value from an attribute.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">5&ndash;10<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Defining custom events and bindings<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use <code class=\"fm-code-in-text1\">EventCallbacks<\/code> to receive the handler for the event and follow the convention to create bindings.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">11&ndash;14<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Displaying child content in a component<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a <code class=\"fm-code-in-text1\">RenderFragment<\/code> named <code class=\"fm-code-in-text1\">ChildContent<\/code>.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">15, 16<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating templates<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use named <code class=\"fm-code-in-text1\">RenderFragment<\/code> properties.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">17, 25<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Distributing configuration settings widely<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a cascading parameter.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">26, 27<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Responding to connection errors<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the connection element and classes.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">28, 29<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Responding to unhandled errors<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the error element and classes or define an error boundary.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">30&ndash;35<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-628\">34.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the Advanced project from chapter 33. No changes are required to prepare for this chapter.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">Advanced.csproj<\/code> file, and run the command shown in listing 34.1 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.1 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 34.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.2 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/controllers, which will display a list of data items. Request http:\/\/localhost:5000\/pages\/blazor, and you will see the component from chapter 33 I used to demonstrate data bindings. Figure 34.1 shows both responses.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre364\" src=\"\/images\/proaspnetcore7\/000370.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-629\">34.2 Combining components<\/h2>\n<p class=\"body\">Blazor components can be combined to create more complex features. In the sections that follow, I show you how multiple components can be used together and how components can communicate. To get started, add a Razor Component named <code class=\"fm-code-in-text\">SelectFilter.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder with the content shown in listing 34.3.<a id=\"calibre_link-2751\"><\/a><a id=\"calibre_link-798\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.3 The contents of the SelectFilter.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"select-@Title\"&gt;@Title&lt;\/label&gt;\n    &lt;select name=\"select-@Title\" class=\"form-control\" \n            @bind=\"SelectedValue\"&gt;\n        &lt;option disabled selected value=\"\"&gt;Select @Title&lt;\/option&gt;\n        @foreach (string val in Values) {\n                &lt;option value=\"@val\" selected=\"@(val == SelectedValue)\"&gt;\n                @val\n                &lt;\/option&gt;\n        }\n    &lt;\/select&gt;\n&lt;\/div&gt;\n\n@code {\n\n    public IEnumerable&lt;string&gt; Values { get; set; } \n        = Enumerable.Empty&lt;string&gt;();\n                \n    public string? SelectedValue { get; set; }\n \n    public string Title { get; set; } = \"Placeholder\";\n}<\/pre>\n<p class=\"body\">The component renders a <code class=\"fm-code-in-text\">select<\/code> element that will allow the user to choose a city. In listing 34.4, I have applied the <code class=\"fm-code-in-text\">SelectFilter<\/code> component, replacing the existing <code class=\"fm-code-in-text\">select<\/code> element.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.4 Applying a component in the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (Person p in People ?? Enumerable.Empty&lt;Person&gt;()) {\n            &lt;tr class=\"@GetClass(p?.Location?.City)\"&gt;\n                &lt;td&gt;@p?.PersonId&lt;\/td&gt;\n                &lt;td&gt;@p?.Surname, @p?.Firstname&lt;\/td&gt;\n                &lt;td&gt;@p?.Department?.Name&lt;\/td&gt;\n                &lt;td&gt;@p?.Location?.City, @p?.Location?.State&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"city\"&gt;City&lt;\/label&gt;\n    &lt;select name=\"city\" class=\"form-control\" @bind=\"SelectedCity\"&gt;\n        &lt;option disabled selected value=\"\"&gt;Select City&lt;\/option&gt;\n        @foreach (string city in Cities ?? Enumerable.Empty&lt;string&gt;()) {\n            &lt;option value=\"@city\" selected=\"@(city == SelectedCity)\"&gt;\n                @city\n            &lt;\/option&gt;\n        }\n    &lt;\/select&gt;\n&lt;\/div&gt;\n\n<b class=\"fm-bold\">&lt;SelectFilter \/&gt;<\/b>\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt;? People =&gt;\n        Context?.People.Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location);\n                        \n    public IEnumerable&lt;string&gt;? Cities =&gt; \n        Context?.Locations.Select(l =&gt; l.City);\n                \n    public string SelectedCity { get; set; } = string.Empty;\n        \n    public string GetClass(string? city) =&gt;\n        SelectedCity == city ? \"bg-info text-white\" : \"\";\n}<\/pre>\n<p class=\"body\">When a component is added to the content rendered by a controller view or Razor Page, the <code class=\"fm-code-in-text\">component<\/code> element is used, as shown in chapter 33. When a component is added to the content rendered by another component, then the name of the component is used as an element instead. In this case, I am adding the <code class=\"fm-code-in-text\">SelectFilter<\/code> component to the content rendered by the <code class=\"fm-code-in-text\">PeopleList<\/code> component, which I do with a <code class=\"fm-code-in-text\">SelectFilter<\/code> element. It is important to pay close attention to the capitalization, which must match exactly.<\/p>\n<p class=\"body\">When combining components, the effect is that one component delegates responsibility for part of its layout to another. In this case, I have removed the <code class=\"fm-code-in-text\">select<\/code> element that the <code class=\"fm-code-in-text\">PeopleList<\/code> component used to present the user with a choice of cities and replaced it with the <code class=\"fm-code-in-text\">SelectFilter<\/code> component, which will provide the same feature. The components form a parent-child relationship; the <code class=\"fm-code-in-text\">PeopleList<\/code> component is the parent, and the <code class=\"fm-code-in-text\">SelectFilter<\/code> component is the child.<\/p>\n<p class=\"body\">Additional work is required before everything is properly integrated, but you can see that adding the <code class=\"fm-code-in-text\">SelectFilter<\/code> element displays the <code class=\"fm-code-in-text\">SelectFilter<\/code> component by restarting ASP.NET Core and requesting http:\/\/localhost:5000\/controllers, which produces the response shown in figure 34.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre365\" src=\"\/images\/proaspnetcore7\/000371.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.2 Adding one component to the content rendered by another<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-630\">34.2.1 Configuring components with attributes<\/h3>\n<p class=\"body\">My goal with the <code class=\"fm-code-in-text\">SelectList<\/code> component is to create a general-purpose feature that I can use throughout the application, configuring the values it displays each time it is used. Razor Components are configured using attributes added to the HTML element that applies them. The values assigned to the HTML element attributes are assigned to the component\u2019s C# properties. The <code class=\"fm-code-in-text\">Parameter<\/code> attribute is applied to the C# properties that a component allows to be configured, as shown in listing 34.5.<a id=\"calibre_link-2752\"><\/a><a id=\"calibre_link-2753\"><\/a><a id=\"calibre_link-2754\"><\/a><a id=\"calibre_link-794\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.5 Configurable properties in the SelectFilter.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"select-@Title\"&gt;@Title&lt;\/label&gt;\n    &lt;select name=\"select-@Title\" class=\"form-control\" \n            @bind=\"SelectedValue\"&gt;\n        &lt;option disabled selected value=\"\"&gt;Select @Title&lt;\/option&gt;\n        @foreach (string val in Values) {\n                &lt;option value=\"@val\" selected=\"@(val == SelectedValue)\"&gt;\n                @val\n                &lt;\/option&gt;\n        }\n    &lt;\/select&gt;\n&lt;\/div&gt;\n\n@code {\n\n    <b class=\"fm-bold\">[Parameter]<\/b>\n    public IEnumerable&lt;string&gt; Values { get; set; } \n        = Enumerable.Empty&lt;string&gt;();\n                \n    public string? SelectedValue { get; set; }\n        \n    <b class=\"fm-bold\">[Parameter]<\/b>\n    public string Title { get; set; } = \"Placeholder\";\n}<\/pre>\n<p class=\"body\">Components can be selective about the properties they allow to be configured. In this case, the <code class=\"fm-code-in-text\">Parameter<\/code> attribute has been applied to two of the properties defined by the <code class=\"fm-code-in-text\">SelectFilter<\/code> component. In listing 34.6, I have modified the element the <code class=\"fm-code-in-text\">PeopleList<\/code> component uses to apply the <code class=\"fm-code-in-text\">SelectFilter<\/code> component to add configuration attributes.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.6 Configuring a Component in the PeopleList.razor File in the Blazor Folder<\/p>\n<pre class=\"programlisting\">...\n&lt;SelectFilter <b class=\"fm-bold\">values=\"@Cities\" title=\"City\"<\/b> \/&gt;\n...<\/pre>\n<p class=\"body\">For each property that should be configured, an attribute of the same name is added to the parent\u2019s HTML element. The attribute values can be fixed values, such as the <code class=\"fm-code-in-text\">City<\/code> string assigned to the <code class=\"fm-code-in-text\">title<\/code> attribute, or Razor expressions, such as <code class=\"fm-code-in-text\">@Cities<\/code>, which assigns the sequence of objects from the <code class=\"fm-code-in-text\">Cities<\/code> property to the <code class=\"fm-code-in-text\">values<\/code> attribute.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">EditorRequired<\/code> attribute can be applied alongside the <code class=\"fm-code-in-text1\">Parameter<\/code> attribute to denote properties for which values are required. A warning will be produced if the component is used without the required attribute.<\/p>\n<p class=\"fm-head2\">Setting and receiving bulk configuration settings<\/p>\n<p class=\"body\">Defining individual properties to receive values can be error-prone if there are many configuration settings, especially if those values are being received by a component so they can be passed on, either to a child component or to a regular HTML element. In these situations, a single property can be designated to receive any attribute values that have not been matched by other properties, which can then be applied as a set, as shown in listing 34.7.<a id=\"calibre_link-2755\"><\/a><a id=\"calibre_link-2756\"><\/a><a id=\"calibre_link-795\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.7 Receiving bulk attributes in the SelectFilter.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"select-@Title\"&gt;@Title&lt;\/label&gt;\n    <b class=\"fm-bold\">&lt;select name=\"select-@Title\" class=\"form-control\"<\/b> \n            <b class=\"fm-bold\">@bind=\"SelectedValue\" @attributes=\"Attrs\"&gt;<\/b>\n        &lt;option disabled selected value=\"\"&gt;Select @Title&lt;\/option&gt;\n        @foreach (string val in Values) {\n            &lt;option value=\"@val\" selected=\"@(val == SelectedValue)\"&gt;\n                @val\n            &lt;\/option&gt;\n        }\n    &lt;\/select&gt;\n&lt;\/div&gt;\n\n@code {\n\n    [Parameter]\n    public IEnumerable&lt;string&gt; Values { get; set; } \n        = Enumerable.Empty&lt;string&gt;();\n                \n    public string? SelectedValue { get; set; }\n        \n    [Parameter]\n    public string Title { get; set; } = \"Placeholder\";\n        \n    <b class=\"fm-bold\">[Parameter(CaptureUnmatchedValues = true)]<\/b>\n    <b class=\"fm-bold\">public Dictionary&lt;string, object&gt;? Attrs { get; set; }<\/b>\n}<\/pre>\n<p class=\"body\">Setting the <code class=\"fm-code-in-text\">Parameter<\/code> attribute\u2019s <code class=\"fm-code-in-text\">CaptureUnmatchedValues<\/code> argument to <code class=\"fm-code-in-text\">true<\/code> identifies a property as the catchall for attributes that are not otherwise matched. The type of the property must be <code class=\"fm-code-in-text\">Dictionary&lt;string, object&gt;<\/code>, which allows the attribute names and values to be represented.<\/p>\n<p class=\"body\">Properties whose type is <code class=\"fm-code-in-text\">Dictionary&lt;string, object&gt;<\/code> can be applied to elements using the <code class=\"fm-code-in-text\">@attribute<\/code> expression, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;select name=\"select-@Title\" class=\"form-control\" @bind=\"SelectedValue\" \n    <b class=\"fm-bold\">@attributes=\"Attrs\"<\/b>&gt;\n...<\/pre>\n<p class=\"body\">This is known as <i class=\"fm-italics\">attribute splatting<\/i>, and it allows a set of attributes to be applied in one go. The effect of the changes in listing 34.7 means that the <code class=\"fm-code-in-text\">SelectFilter<\/code> component will receive the <code class=\"fm-code-in-text\">Values<\/code> and <code class=\"fm-code-in-text\">Title<\/code> attribute values and that any other attributes will be assigned to the <code class=\"fm-code-in-text\">Attrs<\/code> property and passed on to the <code class=\"fm-code-in-text\">select<\/code> element. Listing 34.8 adds some attributes to demonstrate the effect.<a id=\"calibre_link-2757\"><\/a><a id=\"calibre_link-785\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.8 Adding element attributes in the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">&lt;SelectFilter values=\"@Cities\" title=\"City\" autofocus=\"true\" name=\"city\"<\/b> \n    <b class=\"fm-bold\">required=\"true\" \/&gt;<\/b>\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core and navigate to http:\/\/localhost:5000\/controllers. The attributes passed on to the <code class=\"fm-code-in-text\">select<\/code> element do not affect appearance, but if you right-click the <code class=\"fm-code-in-text\">select<\/code> element and select Inspect from the pop-up menu, you will see the attributes added to the <code class=\"fm-code-in-text\">SelectFilter<\/code> element in the <code class=\"fm-code-in-text\">PeopleList<\/code> component have been added to the element rendered by the <code class=\"fm-code-in-text\">SelectFilter<\/code> component, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;select class=\"form-control\" <b class=\"fm-bold\">autofocus=\"true\" name=\"city\" required=\"true\"<\/b>&gt;\n...<\/pre>\n<p class=\"fm-head2\">Configuring a component in a controller view or Razor Page<\/p>\n<p class=\"body\">Attributes are also used to configure components when they are applied using the <code class=\"fm-code-in-text\">component<\/code> element. In listing 34.9, I have added properties to the <code class=\"fm-code-in-text\">PeopleList<\/code> component that specify how many items from the database should be displayed and a string value that will be passed on to the <code class=\"fm-code-in-text\">SelectFilter<\/code> component.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.9 Adding properties in the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (Person p in People ?? Enumerable.Empty&lt;Person&gt;()) {\n            &lt;tr class=\"@GetClass(p?.Location?.City)\"&gt;\n                &lt;td&gt;@p?.PersonId&lt;\/td&gt;\n                &lt;td&gt;@p?.Surname, @p?.Firstname&lt;\/td&gt;\n                &lt;td&gt;@p?.Department?.Name&lt;\/td&gt;\n                &lt;td&gt;@p?.Location?.City, @p?.Location?.State&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n<b class=\"fm-bold\">&lt;SelectFilter values=\"@Cities\" title=\"@SelectTitle\" \/&gt;<\/b>\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n        \n    <b class=\"fm-bold\">public IEnumerable&lt;Person&gt;? People =&gt; Context?.People<\/b>\n            <b class=\"fm-bold\">.Include(p =&gt; p.Department)<\/b>\n            <b class=\"fm-bold\">.Include(p =&gt; p.Location).Take(ItemCount);<\/b>\n                        \n    public IEnumerable&lt;string&gt;? Cities =&gt; \n        Context?.Locations.Select(l =&gt; l.City);\n                \n    public string SelectedCity { get; set; } = string.Empty;\n        \n    public string GetClass(string? city) =&gt;\n        SelectedCity == city ? \"bg-info text-white\" : \"\";\n                \n    <b class=\"fm-bold\">[Parameter]<\/b>\n    <b class=\"fm-bold\">public int ItemCount { get; set; } = 4;<\/b>\n        \n    <b class=\"fm-bold\">[Parameter]<\/b>\n    <b class=\"fm-bold\">public string? SelectTitle { get; set; }<\/b>\n}<\/pre>\n<p class=\"body\">Values for the C# properties are provided by adding attributes whose name begins with <code class=\"fm-code-in-text\">param-<\/code>, followed by the property name, to the <code class=\"fm-code-in-text\">component<\/code> element, as shown in listing 34.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.10 Adding attributes in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model PeopleListViewModel\n\n&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;People&lt;\/h4&gt;\n\n<b class=\"fm-bold\">&lt;component type=\"typeof(Advanced.Blazor.PeopleList)\" render-mode=\"Server\"<\/b>\n    <b class=\"fm-bold\">param-itemcount=\"5\" param-selecttitle=\"@(\"Location\")\" \/&gt;<\/b><\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">param-itemcount<\/code> attribute provides a value for the <code class=\"fm-code-in-text\">ItemCount<\/code> property, and the <code class=\"fm-code-in-text\">param-selecttitle<\/code> attribute provides a value for the <code class=\"fm-code-in-text\">SelectTitle<\/code> property.<\/p>\n<p class=\"body\">When using the <code class=\"fm-code-in-text\">component<\/code> element, attribute values that can be parsed into numeric or <code class=\"fm-code-in-text\">bool<\/code> values are handled as literal values and not Razor expressions, which is why I am able to specify the value for the <code class=\"fm-code-in-text\">ItemCount<\/code> property as <code class=\"fm-code-in-text\">4<\/code>. Other values are assumed to be Razor expressions and not literal values, even though they are not prefixed with <code class=\"fm-code-in-text\">@<\/code>. This oddity means that since I want to specify the value for the <code class=\"fm-code-in-text\">SelectTitle<\/code> property as a literal string, I need a Razor expression, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;component type=\"typeof(Advanced.Blazor.PeopleList)\" render-mode=\"Server\"\n    param-itemcount=\"5\" param-selecttitle=\"<b class=\"fm-bold\">@(\"Location\")<\/b>\" \/&gt;\n...<\/pre>\n<p class=\"body\">To see the effect of the configuration attributes, restart ASP.NET Core and request http:\/\/localhost:5000\/controllers, which will produce the response shown in figure 34.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre366\" src=\"\/images\/proaspnetcore7\/000372.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.3 Configuring components with attributes<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-631\">34.2.2 Creating custom events and bindings<\/h3>\n<p class=\"body\"><a id=\"calibre_link-800\"><\/a>The <code class=\"fm-code-in-text\">SelectFilter<\/code> component receives its data values from its parent component, but it has no way to indicate when the user makes a selection. For this, I need to create a custom event for which the parent component can register a handler method, just as it would for events from regular HTML elements. Listing 34.11 adds a custom event to the <code class=\"fm-code-in-text\">SelectFilter<\/code> component.<a id=\"calibre_link-2758\"><\/a><a id=\"calibre_link-2759\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.11 Creating an event in the SelectFilter.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"select-@Title\"&gt;@Title&lt;\/label&gt;\n    <b class=\"fm-bold\">&lt;select name=\"select-@Title\" class=\"form-control\"<\/b> \n            <b class=\"fm-bold\">@onchange=\"HandleSelect\" value=\"@SelectedValue\"&gt;<\/b>\n        &lt;option disabled selected value=\"\"&gt;Select @Title&lt;\/option&gt;\n        @foreach (string val in Values) {\n            &lt;option value=\"@val\" selected=\"@(val == SelectedValue)\"&gt;\n                @val\n            &lt;\/option&gt;\n        }\n    &lt;\/select&gt;\n&lt;\/div&gt;\n\n@code {\n\n    [Parameter]\n    public IEnumerable&lt;string&gt; Values { get; set; } \n        = Enumerable.Empty&lt;string&gt;();\n                \n    public string? SelectedValue { get; set; }\n        \n    [Parameter]\n    public string Title { get; set; } = \"Placeholder\";\n        \n    [Parameter(CaptureUnmatchedValues = true)]\n    public Dictionary&lt;string, object&gt;? Attrs { get; set; }\n        \n    [Parameter]\n    <b class=\"fm-bold\">public EventCallback&lt;string&gt; CustomEvent { get; set; }<\/b>\n\n    <b class=\"fm-bold\">public async Task HandleSelect(ChangeEventArgs e) {<\/b>\n        <b class=\"fm-bold\">SelectedValue = e.Value as string;<\/b>\n        <b class=\"fm-bold\">await CustomEvent.InvokeAsync(SelectedValue);<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">The custom event is defined by adding a property whose type is <code class=\"fm-code-in-text\">EventCallback&lt;T&gt;<\/code>. The generic type argument is the type that will be received by the parent\u2019s event handler and is <code class=\"fm-code-in-text\">string<\/code> in this case. I have changed the <code class=\"fm-code-in-text\">select<\/code> element so the <code class=\"fm-code-in-text\">@onchange<\/code> attribute registers the <code class=\"fm-code-in-text\">HandleSelect<\/code> method when the <code class=\"fm-code-in-text\">select<\/code> element triggers its <code class=\"fm-code-in-text\">onchange<\/code> event.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HandleSelect<\/code> method updates the <code class=\"fm-code-in-text\">SelectedValue<\/code> property and triggers the custom event by invoking the <code class=\"fm-code-in-text\">EventCallback&lt;T&gt;.InvokeAsync<\/code> method, like this:<\/p>\n<pre class=\"programlisting\">...\nawait CustomEvent.InvokeAsync(SelectedValue);\n...<\/pre>\n<p class=\"body\">The argument to the <code class=\"fm-code-in-text\">InvokeAsync<\/code> method is used to trigger the event using the value received from the <code class=\"fm-code-in-text\">ChangeEventArgs<\/code> object that was received from the <code class=\"fm-code-in-text\">select<\/code> element. Listing 34.12 changes the <code class=\"fm-code-in-text\">PeopleList<\/code> component so that it receives the custom event emitted by the <code class=\"fm-code-in-text\">SelectList<\/code> component.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.12 Handling an event in the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (Person p in People ?? Enumerable.Empty&lt;Person&gt;()) {\n            &lt;tr class=\"@GetClass(p?.Location?.City)\"&gt;\n                &lt;td&gt;@p?.PersonId&lt;\/td&gt;\n                &lt;td&gt;@p?.Surname, @p?.Firstname&lt;\/td&gt;\n                &lt;td&gt;@p?.Department?.Name&lt;\/td&gt;\n                &lt;td&gt;@p?.Location?.City, @p?.Location?.State&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n<b class=\"fm-bold\">&lt;SelectFilter values=\"@Cities\" title=\"@SelectTitle\"<\/b> \n    <b class=\"fm-bold\">CustomEvent=\"@HandleCustom\" \/&gt;<\/b>\n        \n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt;? People =&gt; Context?.People\n            .Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location).Take(ItemCount);\n                        \n    public IEnumerable&lt;string&gt;? Cities =&gt;\n        Context?.Locations.Select(l =&gt; l.City);\n                \n    public string SelectedCity { get; set; } = string.Empty;\n        \n    public string GetClass(string? city) =&gt;\n        SelectedCity == city ? \"bg-info text-white\" : \"\";\n                \n    [Parameter]\n    public int ItemCount { get; set; } = 4;\n        \n    [Parameter]\n    public string? SelectTitle { get; set; }\n        \n    <b class=\"fm-bold\">public void HandleCustom(string newValue) {<\/b>\n        <b class=\"fm-bold\">SelectedCity = newValue;<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">To set up the event handler, an attribute is added to the element that applies the child component using the name of its <code class=\"fm-code-in-text\">EventCallback&lt;T&gt;<\/code> property. The value of the attribute is a Razor expression that selects a method that receives a parameter of type <code class=\"fm-code-in-text\">T<\/code>.<\/p>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000\/controllers, and select a value from the list of cities. The custom event completes the relationship between the parent and child components. The parent configures the child through its attributes to specify the title and the list of data values that will be presented to the user. The child component uses a custom event to tell the parent when the user selects a value, allowing the parent to highlight the corresponding rows in its HTML table, as shown in figure 34.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre367\" src=\"\/images\/proaspnetcore7\/000373.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.4 Using a custom event<\/p>\n<\/div>\n<p class=\"body\">A parent component can create a binding on a child component if it defines a pair of properties, one of which is assigned a data value and the other of which is a custom event. The names of the property are important: the name of the event property must be the same as the data property plus the word <code class=\"fm-code-in-text\">Changed<\/code>. Listing 34.13 updates the <code class=\"fm-code-in-text\">SelectFilter<\/code> component so it presents the properties required for the binding.<a id=\"calibre_link-2760\"><\/a><a id=\"calibre_link-801\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.13 Preparing for bindings in the SelectFilter.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"select-@Title\"&gt;@Title&lt;\/label&gt;\n    &lt;select name=\"select-@Title\" class=\"form-control\" \n            @onchange=\"HandleSelect\" value=\"@SelectedValue\"&gt;\n        &lt;option disabled selected value=\"\"&gt;Select @Title&lt;\/option&gt;\n        @foreach (string val in Values) {\n            &lt;option value=\"@val\" selected=\"@(val == SelectedValue)\"&gt;\n                @val\n            &lt;\/option&gt;\n        }\n    &lt;\/select&gt;\n&lt;\/div&gt;\n\n@code {\n\n    [Parameter]\n    public IEnumerable&lt;string&gt; Values { get; set; } \n        = Enumerable.Empty&lt;string&gt;();\n \n    <b class=\"fm-bold\">[Parameter]<\/b>\n    public string? SelectedValue { get; set; }\n        \n    [Parameter]\n    public string Title { get; set; } = \"Placeholder\";\n        \n    [Parameter(CaptureUnmatchedValues = true)]\n    public Dictionary&lt;string, object&gt;? Attrs { get; set; }\n \n    [Parameter]\n    <b class=\"fm-bold\">public EventCallback&lt;string&gt; SelectedValueChanged { get; set; }<\/b>\n    public async Task HandleSelect(ChangeEventArgs e) {\n        SelectedValue = e.Value as string;\n        <b class=\"fm-bold\">await SelectedValueChanged.InvokeAsync(SelectedValue);<\/b>\n    }\n}<\/pre>\n<p class=\"body\">Notice that the <code class=\"fm-code-in-text\">Parameter<\/code> attribute must be applied to both the <code class=\"fm-code-in-text\">SelectedValue<\/code> and <code class=\"fm-code-in-text\">SelectedValueChanged<\/code> properties. If either attribute is omitted, the data binding won\u2019t work as expected.<\/p>\n<p class=\"body\">The parent component binds to the child with the <code class=\"fm-code-in-text\">@bind-&lt;name&gt;<\/code> attribute, where <code class=\"fm-code-in-text\">&lt;name&gt;<\/code> corresponds to the property defined by the child component. In this example, the name of the child component\u2019s property is <code class=\"fm-code-in-text\">SelectedValue<\/code>, and the parent can create a binding using <code class=\"fm-code-in-text\">@bind-SelectedValue<\/code>, as shown in listing 34.14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.14 Using a custom binding in the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (Person p in People ?? Enumerable.Empty&lt;Person&gt;()) {\n            &lt;tr class=\"@GetClass(p?.Location?.City)\"&gt;\n                &lt;td&gt;@p?.PersonId&lt;\/td&gt;\n                &lt;td&gt;@p?.Surname, @p?.Firstname&lt;\/td&gt;\n                &lt;td&gt;@p?.Department?.Name&lt;\/td&gt;\n                &lt;td&gt;@p?.Location?.City, @p?.Location?.State&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n<b class=\"fm-bold\">&lt;SelectFilter values=\"@Cities\" title=\"@SelectTitle\"<\/b> \n    <b class=\"fm-bold\">@bind-SelectedValue=\"SelectedCity\" \/&gt;<\/b>\n\n<b class=\"fm-bold\">&lt;button class=\"btn btn-primary mt-2\"<\/b> \n    <b class=\"fm-bold\">@onclick=\"@(() =&gt; SelectedCity = \"Oakland\")\"&gt;<\/b>\n        <b class=\"fm-bold\">Change<\/b>\n<b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt;? People =&gt; Context?.People\n            .Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location).Take(ItemCount);\n                        \n    public IEnumerable&lt;string&gt;? Cities =&gt; \n        Context?.Locations.Select(l =&gt; l.City);\n                \n    public string SelectedCity { get; set; } = string.Empty;\n        \n    public string GetClass(string? city) =&gt;\n        SelectedCity == city ? \"bg-info text-white\" : \"\";\n                \n    [Parameter]\n    public int ItemCount { get; set; } = 4;\n \n    [Parameter]\n    public string? SelectTitle { get; set; }\n        \n    <b class=\"fm-bold\">\/\/public void HandleCustom(string newValue) {<\/b>\n    <b class=\"fm-bold\">\/\/    SelectedCity = newValue;<\/b>\n    <b class=\"fm-bold\">\/\/}<\/b>\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000\/controllers, and select New York from the list of cities. The custom binding will cause the value chosen in the <code class=\"fm-code-in-text\">select<\/code> element to be reflected by the highlighting in the table. Click the Change button to test the binding in the other direction, and you will see the highlighted city change, as shown in figure 34.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre368\" src=\"\/images\/proaspnetcore7\/000374.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.5 Using a custom binding<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-632\">34.3 Displaying child content in a component<\/h2>\n<p class=\"body\">Components that display child content act as wrappers around elements provided by their parents. To see how child content is managed, add a Razor Component named <code class=\"fm-code-in-text\">ThemeWrapper.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder with the content shown in listing 34.15.<a id=\"calibre_link-2761\"><\/a><a id=\"calibre_link-2762\"><\/a><a id=\"calibre_link-796\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.15 The contents of the ThemeWrapper.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"p-2 bg-@Theme border text-white\"&gt;\n    &lt;h5 class=\"text-center\"&gt;@Title&lt;\/h5&gt;\n    @ChildContent\n&lt;\/div&gt;\n\n@code {\n    [Parameter]\n    public string? Theme { get; set; }\n \n    [Parameter]\n    public string? Title { get; set; }\n \n    [Parameter]\n    public RenderFragment? ChildContent { get; set; }\n}<\/pre>\n<p class=\"body\">To receive child content, a component defines a property named <code class=\"fm-code-in-text\">ChildContent<\/code> whose type is <code class=\"fm-code-in-text\">RenderFragment<\/code> and that has been decorated with the <code class=\"fm-code-in-text\">Parameter<\/code> attribute. The <code class=\"fm-code-in-text\">@ChildContent<\/code> expression includes the child content in the component\u2019s HTML output. The component in the listing wraps its child content in a <code class=\"fm-code-in-text\">div<\/code> element that is styled using a Bootstrap theme color and that displays a title. The name of the theme color and the text of the title are also received as parameters.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Restricting element reuse<\/p>\n<p class=\"fm-sidebar-text\">When updating the content presented to the user, Blazor will reuse elements if it can because creating new elements is a relatively expensive operation. This is particularly true when displaying elements for a sequence of values, such as with <code class=\"fm-code-in-text1\">@for<\/code> or <code class=\"fm-code-in-text1\">@foreach<\/code> expressions. If the sequence changes, Blazor will reuse the elements it created for the old data values to display the new data.<a id=\"calibre_link-2763\"><\/a><a id=\"calibre_link-2764\"><\/a><a id=\"calibre_link-803\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">This can cause problems if changes have been made to the elements outside of the control of Blazor, such as with custom JavaScript code. Blazor isn\u2019t aware of the changes, which will persist when the elements are reused. Although this is a rare situation, you can restrict the reuse of elements by using an <code class=\"fm-code-in-text1\">@key<\/code> attribute and providing an expression that associates the element with one of the data values in the sequence, like this:<\/p>\n<pre class=\"programlisting\">...\n@foreach (Person p in People ?? Enumerable.Empty&lt;Person&gt;()) {\n    &lt;tr <b class=\"fm-bold\">@key=\"p.PersonId\"<\/b> class=\"@GetClass(p?.Location?.City)\"&gt;\n        &lt;td&gt;@p?.PersonId&lt;\/td&gt;\n        &lt;td&gt;@p?.Surname, @p?.Firstname&lt;\/td&gt;\n        &lt;td&gt;@p?.Department?.Name&lt;\/td&gt;\n        &lt;td&gt;@p?.Location?.City, @p?.Location?.State&lt;\/td&gt;\n    &lt;\/tr&gt;\n}\n...<\/pre>\n<p class=\"fm-sidebar-text\">Blazor will reuse an element only if there is a data item that has the same key. For other values, new elements will be created.<\/p>\n<\/div>\n<p class=\"body\">Child content is defined by adding HTML elements between the start and end tags when applying the component, as shown in listing 34.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.16 Defining child content in the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @foreach (Person p in People ?? Enumerable.Empty&lt;Person&gt;()) {\n            &lt;tr class=\"@GetClass(p?.Location?.City)\"&gt;\n                &lt;td&gt;@p?.PersonId&lt;\/td&gt;\n                &lt;td&gt;@p?.Surname, @p?.Firstname&lt;\/td&gt;\n                &lt;td&gt;@p?.Department?.Name&lt;\/td&gt;\n                &lt;td&gt;@p?.Location?.City, @p?.Location?.State&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n<b class=\"fm-bold\">&lt;ThemeWrapper Theme=\"info\" Title=\"Location Selector\"&gt;<\/b>\n    &lt;SelectFilter values=\"@Cities\" title=\"@SelectTitle\" \n        @bind-SelectedValue=\"SelectedCity\" \/&gt;\n                \n    &lt;button class=\"btn btn-primary mt-2\" \n        @onclick=\"@(() =&gt; SelectedCity = \"Oakland\")\"&gt;\n            Change\n    &lt;\/button&gt;\n<b class=\"fm-bold\">&lt;\/ThemeWrapper&gt;<\/b>\n\n@code {\n\n    \/\/ ...<i class=\"fm-italics\">statements omitted for brevity<\/i>...\n}<\/pre>\n<p class=\"body\">No additional attributes are required to configure the child content, which is processed and assigned to the <code class=\"fm-code-in-text\">ChildContent<\/code> property automatically. To see how the <code class=\"fm-code-in-text\">ThemeWrapper<\/code> component presents its child content, restart ASP.NET Core and request http:\/\/localhost:5000\/controllers. You will see the configuration attributes that selected the theme and the title text used to produce the response shown in figure 34.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre369\" src=\"\/images\/proaspnetcore7\/000375.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.6 Using child content<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-633\">34.3.1 Creating template components<\/h3>\n<p class=\"body\">Template components bring more structure to the presentation of child content, allowing multiple sections of content to be displayed. Template components are a good way of consolidating features that are used throughout an application to prevent the duplication of code and content.<a id=\"calibre_link-2765\"><\/a><a id=\"calibre_link-804\"><\/a><\/p>\n<p class=\"body\">To see how this works, add a Razor Component named <code class=\"fm-code-in-text\">TableTemplate.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder with the content shown in listing 34.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.17 The contents of the TableTemplate.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    @if (Header != null) {\n        &lt;thead&gt;@Header&lt;\/thead&gt;\n    }\n    &lt;tbody&gt;@Body&lt;\/tbody&gt;\n&lt;\/table&gt;\n\n@code {\n    [Parameter]\n    public RenderFragment? Header { get; set; }\n \n    [Parameter]\n    public RenderFragment? Body { get; set; }\n}<\/pre>\n<p class=\"body\">The component defines a <code class=\"fm-code-in-text\">RenderFragment<\/code> property for each region of child content it supports. The <code class=\"fm-code-in-text\">TableTemplate<\/code> component defines two <code class=\"fm-code-in-text\">RenderFragment<\/code> properties, named <code class=\"fm-code-in-text\">Header<\/code> and <code class=\"fm-code-in-text\">Body<\/code>, which represent the content sections of a table. Each region of child content is rendered using a Razor expression, <code class=\"fm-code-in-text\">@Header<\/code> and <code class=\"fm-code-in-text\">@Body<\/code>, and you can check to see whether content has been provided for a specific section by checking to see whether the property value is <code class=\"fm-code-in-text\">null<\/code>, which this component does for the <code class=\"fm-code-in-text\">Header<\/code> section.<\/p>\n<p class=\"body\">When using a template component, the content for each region is enclosed in an HTML element whose tag matches the name of the corresponding <code class=\"fm-code-in-text\">RenderFragment<\/code> property, as shown in listing 34.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.18 Applying a template component in the Blazor\/PeopleList.razor file<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">&lt;TableTemplate&gt;<\/b>\n    <b class=\"fm-bold\">&lt;Header&gt;<\/b>\n        <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;\/tr&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/Header&gt;<\/b>\n    <b class=\"fm-bold\">&lt;Body&gt;<\/b>\n        <b class=\"fm-bold\">@foreach (Person p in People ?? Enumerable.Empty&lt;Person&gt;()) {<\/b>\n            <b class=\"fm-bold\">&lt;tr class=\"@GetClass(p?.Location?.City)\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;td&gt;@p?.PersonId&lt;\/td&gt;<\/b>\n                <b class=\"fm-bold\">&lt;td&gt;@p?.Surname, @p?.Firstname&lt;\/td&gt;<\/b>\n                <b class=\"fm-bold\">&lt;td&gt;@p?.Department?.Name&lt;\/td&gt;<\/b>\n                <b class=\"fm-bold\">&lt;td&gt;@p?.Location?.City, @p?.Location?.State&lt;\/td&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/tr&gt;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">&lt;\/Body&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/TableTemplate&gt;<\/b>\n\n&lt;ThemeWrapper Theme=\"info\" Title=\"Location Selector\"&gt;\n    &lt;SelectFilter values=\"@Cities\" title=\"@SelectTitle\" \n        @bind-SelectedValue=\"SelectedCity\" \/&gt;\n                \n    &lt;button class=\"btn btn-primary mt-2\" \n        @onclick=\"@(() =&gt; SelectedCity = \"Oakland\")\"&gt;\n            Change\n    &lt;\/button&gt;\n&lt;\/ThemeWrapper&gt;\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt;? People =&gt; Context?.People\n            .Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location).Take(ItemCount);\n                        \n    public IEnumerable&lt;string&gt;? Cities =&gt; \n        Context?.Locations.Select(l =&gt; l.City);\n                \n    public string SelectedCity { get; set; } = string.Empty;\n        \n    public string GetClass(string? city) =&gt;\n        SelectedCity == city ? \"bg-info text-white\" : \"\";\n \n    [Parameter]\n    public int ItemCount { get; set; } = 4;\n \n    [Parameter]\n    public string? SelectTitle { get; set; }\n}<\/pre>\n<p class=\"body\">The child content is structured into sections that correspond to the template component\u2019s properties, <code class=\"fm-code-in-text\">Header<\/code> and <code class=\"fm-code-in-text\">Body<\/code>, which leaves the <code class=\"fm-code-in-text\">TableTemplate<\/code> component responsible for the table structure and the <code class=\"fm-code-in-text\">PeopleList<\/code> component responsible for providing the detail. Restart ASP.NET Core and request http:\/\/localhost:5000\/controllers, and you will see the output produced by the template component, as shown in figure 34.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre370\" src=\"\/images\/proaspnetcore7\/000376.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.7 Using a template component<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-634\">34.3.2 Using generic type parameters in template components<\/h3>\n<p class=\"body\">The template component I created in the previous section is useful, in the sense that it provides a consistent representation of a table that I can use throughout the example application. But it is also limited because it relies on the parent component to take responsibility for generating the rows for the table body. The template component doesn\u2019t have any insight into the content it presents, which means it cannot do anything with that content other than display it.<\/p>\n<p class=\"body\">Template components can be made data-aware with the use of a generic type parameter, which allows the parent component to provide a sequence of data objects and a template for presenting them. The template component becomes responsible for generating the content for each data object and, consequently, can provide more useful functionality. As a demonstration, I am going to add support to the template component for selecting how many table rows are displayed and for selecting table rows. The first step is to add a generic type parameter to the component and use it to render the content for the table body, as shown in listing 34.19.<a id=\"calibre_link-2766\"><\/a><a id=\"calibre_link-802\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.19 Adding a type parameter in the Blazor\/TableTemplate.razor file<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@typeparam RowType<\/b>\n&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    @if (Header != null) {\n        &lt;thead&gt;@Header&lt;\/thead&gt;\n    }\n    &lt;tbody&gt;\n        <b class=\"fm-bold\">@if (RowData != null &amp;&amp; RowTemplate != null) {<\/b>\n            <b class=\"fm-bold\">@foreach (RowType item in RowData) {<\/b>\n                <b class=\"fm-bold\">&lt;tr&gt;@RowTemplate(item)&lt;\/tr&gt;<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n@code {\n\n    [Parameter]\n    public RenderFragment? Header { get; set; }\n        \n    <b class=\"fm-bold\">[Parameter]<\/b>\n    <b class=\"fm-bold\">public RenderFragment&lt;RowType&gt;? RowTemplate { get; set; }<\/b>\n        \n    <b class=\"fm-bold\">[Parameter]<\/b>\n    <b class=\"fm-bold\">public IEnumerable&lt;RowType&gt;? RowData { get; set; }<\/b>\n}<\/pre>\n<p class=\"body\">The generic type parameter is specified using the <code class=\"fm-code-in-text\">@typeparam<\/code> attribute, and, in this case, I have given the parameter the name <code class=\"fm-code-in-text\">RowType<\/code> because it will refer to the data type for which the component will generate table rows.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Blazor generic type parameters can be constrained using the C# <code class=\"fm-code-in-text1\">where<\/code> keyword so that only types that have specified characteristics, such as classes that implement a particular interface, can be used. See <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/keywords\/where-generic-type-constraint\">https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/keywords\/where-generic-type-constraint<\/a> for details.<\/p>\n<p class=\"body\">The data the component will process is received by adding a property whose type is a sequence of objects of the generic type. I have named the property <code class=\"fm-code-in-text\">RowData<\/code>, and its type is <code class=\"fm-code-in-text\">IEnumerable&lt;RowType&gt;<\/code>. The content the component will display for each object is received using a <code class=\"fm-code-in-text\">RenderFragment&lt;T&gt;<\/code> property. I have named this property <code class=\"fm-code-in-text\">RowTemplate<\/code>, and its type is <code class=\"fm-code-in-text\">RenderFragment&lt;RowType&gt;<\/code>, reflecting the name I selected for the generic type parameter.<\/p>\n<p class=\"body\">When a component receives a content section through a <code class=\"fm-code-in-text\">RenderFragment&lt;T&gt;<\/code> property, it can render it for a single object by invoking the section as a method and using the object as the argument, like this:<\/p>\n<pre class=\"programlisting\">...\n@foreach (RowType item in RowData) {\n    &lt;tr&gt;<b class=\"fm-bold\">@RowTemplate(item)<\/b>&lt;\/tr&gt;\n}\n...<\/pre>\n<p class=\"body\">This fragment of code enumerates the <code class=\"fm-code-in-text\">RowType<\/code> objects in the <code class=\"fm-code-in-text\">RowData<\/code> sequence and renders the content section received through the <code class=\"fm-code-in-text\">RowTemplate<\/code> property for each of them.<\/p>\n<p class=\"fm-head2\">Using a generic template component<\/p>\n<p class=\"body\">I have simplified the <code class=\"fm-code-in-text\">PeopleList<\/code> component so it only uses the template component to produce a table of <code class=\"fm-code-in-text\">Person<\/code> objects, and I have removed earlier features, as shown in listing 34.20.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.20 Using a generic template component in the Blazor\/PeopleList.razor file<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">&lt;TableTemplate RowType=\"Person\" RowData=\"People\"&gt;<\/b>\n    &lt;Header&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/Header&gt;\n    <b class=\"fm-bold\">&lt;RowTemplate Context=\"p\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;td&gt;@p.PersonId&lt;\/td&gt;<\/b>\n        <b class=\"fm-bold\">&lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;<\/b>\n        <b class=\"fm-bold\">&lt;td&gt;@p.Department?.Name&lt;\/td&gt;<\/b>\n        <b class=\"fm-bold\">&lt;td&gt;@p.Location?.City, @p.Location?.State&lt;\/td&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/RowTemplate&gt;<\/b>\n&lt;\/TableTemplate&gt;\n\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n        \n    <b class=\"fm-bold\">public IEnumerable&lt;Person&gt;? People =&gt; Context?.People<\/b>\n            <b class=\"fm-bold\">.Include(p =&gt; p.Department)<\/b>\n            <b class=\"fm-bold\">.Include(p =&gt; p.Location);<\/b>\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">RowType<\/code> attribute is used to specify the value for the generic type argument. The <code class=\"fm-code-in-text\">RowData<\/code> attribute specifies the data the template component will process.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">RowTemplate<\/code> element denotes the elements that will be produced for each data object. When defining a content section for a <code class=\"fm-code-in-text\">RenderFragment&lt;T&gt;<\/code> property, the <code class=\"fm-code-in-text\">Context<\/code> attribute is used to assign a name to the current object being processed. In this case, the <code class=\"fm-code-in-text\">Context<\/code> attribute is used to assign the name <code class=\"fm-code-in-text\">p<\/code> to the current object, which is then referred to in the Razor expressions used to populate the content section\u2019s elements.<\/p>\n<p class=\"body\">The overall effect is that the template component is configured to display <code class=\"fm-code-in-text\">Person<\/code> objects. The component will generate a table row for each <code class=\"fm-code-in-text\">Person<\/code>, which will contain <code class=\"fm-code-in-text\">td<\/code> elements whose content is set using the current <code class=\"fm-code-in-text\">Person<\/code> object\u2019s properties.<\/p>\n<p class=\"body\">Since I removed properties that were decorated with the <code class=\"fm-code-in-text\">Parameter<\/code> attribute in listing 34.20, I need to remove the corresponding attributes from the element that applies the <code class=\"fm-code-in-text\">PeopleList<\/code> component, as shown in listing 34.21.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.21 Removing attributes in the Index.cshtml file in the Views\/Home folder<\/p>\n<pre class=\"programlisting\">@model PeopleListViewModel\n\n&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;People&lt;\/h4&gt;\n\n<b class=\"fm-bold\">&lt;component type=\"typeof(Advanced.Blazor.PeopleList)\"<\/b> \n    <b class=\"fm-bold\">render-mode=\"Server\" \/&gt;<\/b><\/pre>\n<p class=\"body\">To see the generic template component, restart ASP.NET Core and request http:\/\/localhost:5000\/controllers. The data and content sections provided by the <code class=\"fm-code-in-text\">PeopleList<\/code> component have been used by the <code class=\"fm-code-in-text\">TableTemplate<\/code> component to produce the table shown in figure 34.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre371\" src=\"\/images\/proaspnetcore7\/000377.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.8 Using a generic template component<\/p>\n<\/div>\n<p class=\"fm-head2\">Adding features to the generic template component<\/p>\n<p class=\"body\">This may feel like a step backward, but, as you will see, giving the template component insight into the data it handles sets the foundation for adding features, as shown in listing 34.22.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.22 Adding a feature in the TableTemplate.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@typeparam RowType\n\n<b class=\"fm-bold\">&lt;div class=\"container-fluid\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;div class=\"row p-2\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"col\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;SelectFilter Title=\"@(\"Sort\")\"<\/b> \n                <b class=\"fm-bold\">Values=\"@SortDirectionChoices\"<\/b>\n                <b class=\"fm-bold\">@bind-SelectedValue=\"SortDirectionSelection\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"col\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;SelectFilter Title=\"@(\"Highlight\")\"<\/b> \n                <b class=\"fm-bold\">Values=\"@HighlightChoices()\"<\/b>\n                <b class=\"fm-bold\">@bind-SelectedValue=\"HighlightSelection\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n\n&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    @if (Header != null) {\n        &lt;thead&gt;@Header&lt;\/thead&gt;\n    }\n    &lt;tbody&gt;\n        <b class=\"fm-bold\">@if (RowTemplate != null) {<\/b>\n            <b class=\"fm-bold\">@foreach (RowType item in SortedData()) {<\/b>\n                <b class=\"fm-bold\">&lt;tr class=\"@IsHighlighted(item)\"&gt;@RowTemplate(item)&lt;\/tr&gt;<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n@code {\n\n    [Parameter]\n    public RenderFragment? Header { get; set; }\n \n    [Parameter]\n    public RenderFragment&lt;RowType&gt;? RowTemplate { get; set; }\n        \n    <b class=\"fm-bold\">[Parameter]<\/b>\n    <b class=\"fm-bold\">public IEnumerable&lt;RowType&gt; RowData { get; set; }<\/b>\n        <b class=\"fm-bold\">= Enumerable.Empty&lt;RowType&gt;();<\/b>\n                \n    <b class=\"fm-bold\">[Parameter]<\/b>\n    <b class=\"fm-bold\">public Func&lt;RowType, string&gt; Highlight { get; set; }<\/b> \n        <b class=\"fm-bold\">= (row) =&gt; String.Empty;<\/b>\n                \n    <b class=\"fm-bold\">public IEnumerable&lt;string&gt; HighlightChoices() =&gt;<\/b>\n        <b class=\"fm-bold\">RowData.Select(item =&gt; Highlight(item)).Distinct();<\/b>\n                \n    <b class=\"fm-bold\">public string? HighlightSelection { get; set; }<\/b>\n        \n    <b class=\"fm-bold\">public string IsHighlighted(RowType item) =&gt;<\/b>\n        <b class=\"fm-bold\">Highlight(item) == HighlightSelection<\/b> \n            <b class=\"fm-bold\">? \"table-dark text-white\" : \"\";<\/b>\n                        \n    <b class=\"fm-bold\">[Parameter]<\/b>\n    <b class=\"fm-bold\">public Func&lt;RowType, string&gt; SortDirection { get; set; }<\/b>\n        <b class=\"fm-bold\">= (row) =&gt; String.Empty;<\/b>\n                \n    <b class=\"fm-bold\">public string[] SortDirectionChoices =<\/b>\n        <b class=\"fm-bold\">new string[] { \"Ascending\", \"Descending\" };<\/b>\n                \n    <b class=\"fm-bold\">public string SortDirectionSelection { get; set; } = \"Ascending\";<\/b>\n        \n    <b class=\"fm-bold\">public IEnumerable&lt;RowType&gt; SortedData() =&gt;<\/b>\n        <b class=\"fm-bold\">SortDirectionSelection == \"Ascending\"<\/b>\n            <b class=\"fm-bold\">? RowData.OrderBy(SortDirection)<\/b>\n            <b class=\"fm-bold\">: RowData.OrderByDescending(SortDirection);<\/b>\n}<\/pre>\n<p class=\"body\">The changes present the user with two <code class=\"fm-code-in-text\">select<\/code> elements via the <code class=\"fm-code-in-text\">SelectFilter<\/code> component created earlier in the chapter. These new elements allow the user to sort the data in ascending and descending order and to select a value used to highlight rows in the table. The parent component provides additional parameters that give the template component functions that select the properties used for sorting and highlighting, as shown in listing 34.23.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.23 Configuring component features in the Blazor\/PeopleList.razor file<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">&lt;TableTemplate RowType=\"Person\" RowData=\"People\"<\/b>\n        <b class=\"fm-bold\">Highlight=\"@(p =&gt; p.Location?.City)\"<\/b> \n        <b class=\"fm-bold\">SortDirection=\"@(p =&gt; p.Surname)\"&gt;<\/b>\n    &lt;Header&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/Header&gt;\n    &lt;RowTemplate Context=\"p\"&gt;\n        &lt;td&gt;@p.PersonId&lt;\/td&gt;\n        &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n        &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n        &lt;td&gt;@p.Location?.City, @p.Location?.State&lt;\/td&gt;\n    &lt;\/RowTemplate&gt;\n&lt;\/TableTemplate&gt;\n\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt;? People =&gt; Context?.People\n            .Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location);\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Highlight<\/code> attribute provides the template component with a function that selects the property used for highlighting table rows, and the <code class=\"fm-code-in-text\">SortDirection<\/code> attribute provides a function that selects a property used for sorting. To see the effect, restart ASP.NET Core and request http:\/\/localhost:5000\/controllers. The response will contain the new <code class=\"fm-code-in-text\">select<\/code> elements, which can be used to change the sort order or select a city for filtering, as shown in figure 34.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre372\" src=\"\/images\/proaspnetcore7\/000378.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.9 Adding features to a template component<\/p>\n<\/div>\n<p class=\"fm-head2\">Reusing a generic template component<\/p>\n<p class=\"body\">The features added to the template component all relied on the generic type parameter, which allows the component to modify the content it presents without being tied to a specific class. The result is a component that can be used to display, sort, and highlight any data type wherever a table is required. Add a Razor Component named <code class=\"fm-code-in-text\">DepartmentList.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder with the content shown in listing 34.24.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.24 The contents of the DepartmentList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;TableTemplate RowType=\"Department\" RowData=\"Departments\"\n    Highlight=\"@(d =&gt; d.Name)\" \n    SortDirection=\"@(d =&gt; d.Name)\"&gt;\n    &lt;Header&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;People&lt;\/th&gt;&lt;th&gt;Locations&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/Header&gt;\n    &lt;RowTemplate Context=\"d\"&gt;\n        &lt;td&gt;@d.Departmentid&lt;\/td&gt;\n        &lt;td&gt;@d.Name&lt;\/td&gt;\n        &lt;td&gt;@(String.Join(\", \", d.People!.Select(p =&gt; p.Surname)))&lt;\/td&gt;\n        &lt;td&gt;\n            @(String.Join(\", \", d.People!.Select(p =&gt; \n                p.Location!.City).Distinct()))\n        &lt;\/td&gt;\n    &lt;\/RowTemplate&gt;\n&lt;\/TableTemplate&gt;\n\n@code {\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Department&gt;? Departments =&gt; Context?.Departments?\n        .Include(d =&gt; d.People!).ThenInclude(p =&gt; p.Location!);   \n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">TableTemplate<\/code> component is used to present the user with a list of the <code class=\"fm-code-in-text\">Department<\/code> objects in the database, along with details of the related <code class=\"fm-code-in-text\">Person<\/code> and <code class=\"fm-code-in-text\">Location<\/code> objects, which are queried with the Entity Framework Core <code class=\"fm-code-in-text\">Include<\/code> and <code class=\"fm-code-in-text\">ThenInclude<\/code> methods. Listing 34.25 changes the Razor Component displayed by the Razor Page named <code class=\"fm-code-in-text\">Blazor<\/code>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.25 Changing the component in the Blazor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/blazor\"\n\n<b class=\"fm-bold\">&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;Departments&lt;\/h4&gt;<\/b>\n\n<b class=\"fm-bold\">&lt;component type=\"typeof(Advanced.Blazor.DepartmentList)\"<\/b> \n    <b class=\"fm-bold\">render-mode=\"Server\" \/&gt;<\/b><\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/pages\/blazor. The response will be presented using the templated component, as shown in figure 34.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre373\" src=\"\/images\/proaspnetcore7\/000379.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.10 Reusing a generic template component<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-635\">34.3.3 Cascading parameters<\/h3>\n<p class=\"body\">As the number of components increases, it can be useful for a component to provide configuration data to descendants deep in the hierarchy of components. This can be done by having each component in the chain receive the data and pass it on to all of its children, but that is error-prone and requires every component to participate in the process, even if none of its descendants uses the data it passes on.<\/p>\n<p class=\"body\"><a id=\"calibre_link-792\"><\/a>Blazor provides a solution to this problem by supporting <i class=\"fm-italics\">cascading parameters<\/i>, in which a component provides data values that are available directly to any of its descendants, without being relayed by intermediate components. Cascading parameters are defined using the <code class=\"fm-code-in-text\">CascadingValue<\/code> component, which is used to wrap a section of content, as shown in listing 34.26.<a id=\"calibre_link-2767\"><\/a><a id=\"calibre_link-2768\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.26 A cascading parameter in the DepartmentList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">&lt;CascadingValue Name=\"BgTheme\" Value=\"Theme\" IsFixed=\"false\"&gt;<\/b>\n    &lt;TableTemplate RowType=\"Department\" RowData=\"Departments\"\n                   Highlight=\"@(d =&gt; d.Name)\"\n                   SortDirection=\"@(d =&gt; d.Name)\"&gt;\n        &lt;Header&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;People&lt;\/th&gt;&lt;th&gt;Locations&lt;\/th&gt;\n        &lt;\/tr&gt;\n        &lt;\/Header&gt;\n        &lt;RowTemplate Context=\"d\"&gt;\n            &lt;td&gt;@d.Departmentid&lt;\/td&gt;\n            &lt;td&gt;@d.Name&lt;\/td&gt;\n            &lt;td&gt;\n                @(String.Join(\", \", d.People!.Select(p =&gt; p.Surname)))\n            &lt;\/td&gt;\n            &lt;td&gt;\n                @(String.Join(\", \", d.People!.Select(p =&gt;\n                    p.Location!.City).Distinct()))\n            &lt;\/td&gt;\n        &lt;\/RowTemplate&gt;\n    &lt;\/TableTemplate&gt;\n<b class=\"fm-bold\">&lt;\/CascadingValue&gt;<\/b>\n\n&lt;<b class=\"fm-bold\">SelectFilter<\/b> Title=\"@(\"Theme\")\" Values=\"Themes\"\n              @bind-SelectedValue=\"Theme\" \/&gt;\n\n@code {\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Department&gt;? Departments =&gt; Context?.Departments?\n        .Include(d =&gt; d.People!).ThenInclude(p =&gt; p.Location!);\n                \n    public string Theme { get; set; } = \"info\";\n    <b class=\"fm-bold\">public string[] Themes =<\/b> \n        <b class=\"fm-bold\">new string[] { \"primary\", \"info\", \"success\" };<\/b>\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">CascadingValue<\/code> element makes a value available to the components it encompasses and their descendants. The <code class=\"fm-code-in-text\">Name<\/code> attribute specifies the name of the parameter, the <code class=\"fm-code-in-text\">Value<\/code> attribute specifies the value, and the <code class=\"fm-code-in-text\">isFixed<\/code> attribute is used to specify whether the value will change. The <code class=\"fm-code-in-text\">CascadingValue<\/code> element has been used in listing 34.26 to create a cascading parameter named <code class=\"fm-code-in-text\">BgTheme<\/code>, whose value is set by an instance of the <code class=\"fm-code-in-text\">SelectFilter<\/code> component that presents the user with a selection of Bootstrap CSS theme names.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Each <code class=\"fm-code-in-text1\">CascadingValue<\/code> element creates one cascading parameter. If you need to pass on multiple values, then you can nest the <code class=\"fm-code-in-text1\">CascadingValue<\/code> or create a simple parameter that provides multiple settings through a dictionary.<\/p>\n<p class=\"body\">Cascading parameters are received directly by the components that require them with the <code class=\"fm-code-in-text\">CascadingParameter<\/code> attribute, as shown in listing 34.27.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.27 Receiving a cascading parameter in the SelectFilter.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">&lt;div class=\"form-group p-2 bg-@Theme @TextColor()\"&gt;<\/b>\n    &lt;label for=\"select-@Title\"&gt;@Title&lt;\/label&gt;\n    &lt;select name=\"select-@Title\" class=\"form-control\"\n            @onchange=\"HandleSelect\" value=\"@SelectedValue\"&gt;\n        &lt;option disabled selected value=\"\"&gt;Select @Title&lt;\/option&gt;\n        @foreach (string val in Values) {\n            &lt;option value=\"@val\" selected=\"@(val == SelectedValue)\"&gt;\n                @val\n            &lt;\/option&gt;\n        }\n    &lt;\/select&gt;\n&lt;\/div&gt;\n\n@code {\n\n    [Parameter]\n    public IEnumerable&lt;string&gt; Values { get; set; }\n        = Enumerable.Empty&lt;string&gt;();\n \n    [Parameter]\n    public string? SelectedValue { get; set; }\n \n    [Parameter]\n    public string Title { get; set; } = \"Placeholder\";\n        \n    [Parameter(CaptureUnmatchedValues = true)]\n    public Dictionary&lt;string, object&gt;? Attrs { get; set; }\n \n    [Parameter]\n    public EventCallback&lt;string&gt; SelectedValueChanged { get; set; }\n \n    public async Task HandleSelect(ChangeEventArgs e) {\n        SelectedValue = e.Value as string;\n        await SelectedValueChanged.InvokeAsync(SelectedValue);\n    }\n        \n    <b class=\"fm-bold\">[CascadingParameter(Name = \"BgTheme\")]<\/b>\n    <b class=\"fm-bold\">public string Theme { get; set; } = \"\";<\/b>\n        \n    <b class=\"fm-bold\">public string TextColor() =&gt; String.IsNullOrEmpty(Theme)<\/b>\n        <b class=\"fm-bold\">? \"text-dark\" : \"text-light\";<\/b>\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">CascadingParameter<\/code> attribute\u2019s <code class=\"fm-code-in-text\">Name<\/code> argument is used to specify the name of the cascading parameter. The <code class=\"fm-code-in-text\">BgTheme<\/code> parameter defined in listing 34.26 is received by the <code class=\"fm-code-in-text\">Theme<\/code> property in listing 34.27 and used to set the background for the component. Restart ASP.NET Core and request http:\/\/localhost:5000\/pages\/blazor, which produces the response shown in figure 34.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre374\" src=\"\/images\/proaspnetcore7\/000380.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.11 Using a cascading parameter<\/p>\n<\/div>\n<p class=\"body\">There are three instances of the <code class=\"fm-code-in-text\">SelectFilter<\/code> component used in this example, but only two of them are within the hierarchy contained by the <code class=\"fm-code-in-text\">CascadingValue<\/code> element. The other instance is defined outside of the <code class=\"fm-code-in-text\">CascadingValue<\/code> element and does not receive the cascading value.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-636\">34.4 Handling errors<\/h2>\n<p class=\"body\">In the following sections, I describe the features Blazor provides for dealing with connection errors and unhandled application errors.<a id=\"calibre_link-2769\"><\/a><a id=\"calibre_link-817\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-637\">34.4.1 Handling connection errors<\/h3>\n<p class=\"body\">Blazor relies on its persistent HTTP connection between the browser and the ASP.NET Core server. The application cannot function when the connection is disrupted, and a modal error message is displayed that prevents the user from interacting with components.<a id=\"calibre_link-2770\"><\/a><\/p>\n<p class=\"body\">Blazor allows the connection errors to be customized by defining an element with a specific <code class=\"fm-code-in-text\">id<\/code>, as shown in listing 34.28.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.28 A connection error element in the Blazor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/blazor\"\n\n&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;Departments&lt;\/h4&gt;\n\n<b class=\"fm-bold\">&lt;link rel=\"stylesheet\" href=\"connectionErrors.css\" \/&gt;<\/b>\n\n<b class=\"fm-bold\">&lt;div id=\"components-reconnect-modal\"<\/b>\n     <b class=\"fm-bold\">class=\"h4 bg-dark text-white text-center my-2 p-2<\/b> \n            <b class=\"fm-bold\">components-reconnect-hide\"&gt;<\/b>\n    <b class=\"fm-bold\">Blazor Connection Lost<\/b>\n    <b class=\"fm-bold\">&lt;div class=\"reconnect\"&gt;<\/b>\n        <b class=\"fm-bold\">Trying to reconnect...<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">&lt;div class=\"failed\"&gt;<\/b>\n        <b class=\"fm-bold\">Reconnection Failed.<\/b>\n        <b class=\"fm-bold\">&lt;button class=\"btn btn-light btn-sm m-1\"<\/b>\n                <b class=\"fm-bold\">onclick=\"window.Blazor.reconnect()\"&gt;<\/b>\n            <b class=\"fm-bold\">Reconnect<\/b>\n        <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">&lt;div class=\"rejected\"&gt;<\/b>\n        <b class=\"fm-bold\">Reconnection Rejected.<\/b>\n        <b class=\"fm-bold\">&lt;button class=\"btn btn-light btn-sm m-1\"<\/b> \n                <b class=\"fm-bold\">onclick=\"location.reload()\"&gt;<\/b>\n            <b class=\"fm-bold\">Reload<\/b>\n        <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n\n&lt;component type=\"typeof(Advanced.Blazor.DepartmentList)\" \n    render-mode=\"Server\" \/&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">id<\/code> attribute of the custom error element must be <code class=\"fm-code-in-text\">components-reconnect-modal<\/code>. When there is a connection error, Blazor locates this element and adds it to one of four classes, described in table 34.2.<a id=\"calibre_link-2771\"><\/a><a id=\"calibre_link-2772\"><\/a><a id=\"calibre_link-818\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 34.2 The connection error classes<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2773\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">components-reconnect-show<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The element is added to this class when the connection has been lost and Blazor is attempting a reconnection. The error message should be displayed to the user, and interaction with the Blazor content should be prevented.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">components-reconnect-hide<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The element is added to this class if the connection is reestablished. The error message should be hidden, and interaction should be permitted.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">components-reconnect-failed<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The element is added to this class if Blazor reconnection fails. The user can be presented with a button that invokes <code class=\"fm-code-in-text1\">window.Blazor.reconnect()<\/code> to attempt reconnection.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">components-reconnect-rejected<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The element is added to this class if Blazor is able to reach the server but the user\u2019s connection state has been lost. This typically happens when the server has been restarted. The user can be presented with a button that invokes <code class=\"fm-code-in-text1\">location.reload()<\/code> to reload the application and try again.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The element isn\u2019t added to any of these classes initially, so I have explicitly added it to the <code class=\"fm-code-in-text\">components-reconnect-hide<\/code> class so that it isn\u2019t visible until a problem occurs.<\/p>\n<p class=\"body\">I want to present specific messages to the user for each of the conditions that can arise during reconnection. To this end, I added elements that display a message for each condition. To manage their visibility, add a CSS stylesheet named <code class=\"fm-code-in-text\">connectionErrors.css<\/code> to the <code class=\"fm-code-in-text\">wwwroot<\/code> folder and use it to define the styles shown in listing 34.29.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.29 The contents of the connectionErrors.css file in the wwwroot folder<\/p>\n<pre class=\"programlisting\">#components-reconnect-modal {\n    position: fixed; top: 0; right: 0; bottom: 0;\n    left: 0; z-index: 1000; overflow: hidden; opacity: 0.9;\n}\n\n.components-reconnect-hide { display: none; }\n.components-reconnect-show { display: block; }\n\n.components-reconnect-show &gt; .reconnect { display: block; }\n.components-reconnect-show &gt; .failed, \n.components-reconnect-show &gt; .rejected {\n    display: none;\n}\n\n.components-reconnect-failed &gt; .failed {\n    display: block;\n}\n.components-reconnect-failed &gt; .reconnect, \n.components-reconnect-failed &gt; .rejected {\n    display: none;\n}\n\n.components-reconnect-rejected &gt; .rejected {\n    display: block;\n}\n.components-reconnect-rejected &gt; .reconnect, \n.components-reconnect-rejected &gt; .failed {\n    display: none;\n}<\/pre>\n<p class=\"body\">These styles show the <code class=\"fm-code-in-text\">components-reconnect-modal<\/code> element as a modal item, with its visibility determined by the <code class=\"fm-code-in-text\">components-reconnect-hide<\/code> and <code class=\"fm-code-in-text\">components-reconnect-show<\/code> classes. The visibility of the specific messages is toggled based on the application of the classes in table 34.2.<\/p>\n<p class=\"body\">To see the effect, restart ASP.NET Core and request http:\/\/localhost:5000\/pages\/blazor. Wait until the component is displayed and then stop the ASP.NET Core server. You will see an initial error message as Blazor attempts to reconnect. After a few minutes, you will see the message that indicates that reconnection has failed.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/pages\/blazor. Wait until the component is displayed and then restart ASP.NET Core. This time Blazor will be able to connect to the server, but the connection will be rejected because the server restart has caused the connection state to be lost. Figure 34.12 shows both sequences of error messages.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> It is not possible to test successful connection recovery with just the browser because there is no way to interrupt the persistent HTTP connection. I use the excellent Fiddler proxy, <a class=\"url\" href=\"https:\/\/www.telerik.com\/fiddler\">https:\/\/www.telerik.com\/fiddler<\/a>, which allows me to terminate the connection without stopping the ASP.NET Core server.<a id=\"calibre_link-2774\"><\/a><a id=\"calibre_link-819\"><\/a><\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre375\" src=\"\/images\/proaspnetcore7\/000381.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.12 Handling connection errors<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-638\">34.4.2 Handling uncaught application errors<\/h3>\n<p class=\"body\">Blazor does not respond well to uncaught application errors, which are almost always treated as terminal. To demonstrate the way that exceptions are handled, listing 34.30 introduces an exception that will be thrown when the user selects a specific value.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.30 Introducing an exception in the SelectFilter.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"form-group p-2 bg-@Theme @TextColor()\"&gt;\n    &lt;label for=\"select-@Title\"&gt;@Title&lt;\/label&gt;\n    &lt;select name=\"select-@Title\" class=\"form-control\"\n            @onchange=\"HandleSelect\" value=\"@SelectedValue\"&gt;\n        &lt;option disabled selected value=\"\"&gt;Select @Title&lt;\/option&gt;\n        @foreach (string val in Values) {\n            &lt;option value=\"@val\" selected=\"@(val == SelectedValue)\"&gt;\n                @val\n            &lt;\/option&gt;\n        }\n    &lt;\/select&gt;\n&lt;\/div&gt;\n\n@code {\n\n    [Parameter]\n    public IEnumerable&lt;string&gt; Values { get; set; }\n        = Enumerable.Empty&lt;string&gt;();\n \n    [Parameter]\n    public string? SelectedValue { get; set; }\n \n    [Parameter]\n    public string Title { get; set; } = \"Placeholder\";\n        \n    [Parameter(CaptureUnmatchedValues = true)]\n    public Dictionary&lt;string, object&gt;? Attrs { get; set; }\n \n    [Parameter]\n    public EventCallback&lt;string&gt; SelectedValueChanged { get; set; }\n \n    public async Task HandleSelect(ChangeEventArgs e) {\n        SelectedValue = e.Value as string;\n        if (SelectedValue == \"Sales\") {\n            throw new Exception(\"Sales cannot be selected\");\n        }\n        await SelectedValueChanged.InvokeAsync(SelectedValue);\n    }\n        \n    [CascadingParameter(Name = \"BgTheme\")]\n    public string Theme { get; set; } = \"\";\n        \n    public string TextColor() =&gt; String.IsNullOrEmpty(Theme)\n        ? \"text-dark\" : \"text-light\";\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000\/pages\/blazor, and select Sales from the Highlight menu. There is no visible change in the browser, but the exception thrown at the server when the button was clicked has proved fatal: the user can still choose values using the select elements because these are presented by the browser, but the event handlers that respond to selections no longer work, and the application is essentially dead.<\/p>\n<p class=\"body\">When there is an unhandled application error, Blazor looks for an element whose <code class=\"fm-code-in-text\">id<\/code> is <code class=\"fm-code-in-text\">blazor-error-ui<\/code> and sets its CSS <code class=\"fm-code-in-text\">display<\/code> property to <code class=\"fm-code-in-text\">block<\/code>. Listing 34.31 adds an element with this <code class=\"fm-code-in-text\">id<\/code> to the <code class=\"fm-code-in-text\">Blazor.cshtml<\/code> file styled to present a useful message.<a id=\"calibre_link-2775\"><\/a><a id=\"calibre_link-782\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.31 Adding an error element in the Blazor.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/pages\/blazor\"\n\n&lt;h4 class=\"bg-primary text-white text-center p-2\"&gt;Departments&lt;\/h4&gt;\n\n&lt;link rel=\"stylesheet\" href=\"connectionErrors.css\" \/&gt;\n\n&lt;div id=\"components-reconnect-modal\"\n     class=\"h4 bg-dark text-white text-center my-2 p-2 \n            components-reconnect-hide\"&gt;\n    Blazor Connection Lost\n    &lt;div class=\"reconnect\"&gt;\n        Trying to reconnect...\n    &lt;\/div&gt;\n    &lt;div class=\"failed\"&gt;\n        Reconnection Failed.\n        &lt;button class=\"btn btn-light btn-sm m-1\"\n                onclick=\"window.Blazor.reconnect()\"&gt;\n            Reconnect\n        &lt;\/button&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"rejected\"&gt;\n        Reconnection Rejected.\n        &lt;button class=\"btn btn-light btn-sm m-1\" \n                onclick=\"location.reload()\"&gt;\n            Reload\n        &lt;\/button&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n<b class=\"fm-bold\">&lt;div id=\"blazor-error-ui\"<\/b> \n     <b class=\"fm-bold\">class=\"text-center bg-danger h6 text-white p-2 fixed-top w-100\"<\/b>\n     <b class=\"fm-bold\">style=\"display:none\"&gt;<\/b>\n    <b class=\"fm-bold\">An error has occurred. This application will not respond until reloaded.<\/b>\n    <b class=\"fm-bold\">&lt;button class=\"btn btn-sm btn-primary m-1\" onclick=\"location.reload()\"&gt;<\/b>\n        <b class=\"fm-bold\">Reload<\/b>\n    <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n<b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n\n&lt;component type=\"typeof(Advanced.Blazor.DepartmentList)\" \n    render-mode=\"Server\" \/&gt;<\/pre>\n<p class=\"body\">When the element is shown, the user will be presented with a warning and a button that reloads the browser. To see the effect, restart ASP.NET Core, request http:\/\/localhost:5000\/pages\/blazor, and select Sales from the Highlight menu, which will display the message shown in figure 34.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre376\" src=\"\/images\/proaspnetcore7\/000382.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.13 Displaying an error message<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-639\">34.4.3 Using error boundaries<\/h3>\n<p class=\"body\">Error boundaries are used to contain errors within the component hierarchy so that a component can take responsibility for its own exceptions and the exceptions thrown by its child components. Listing 34.32 introduces an error boundary to contain the exception thrown by the <code class=\"fm-code-in-text\">SelectFilter<\/code> component.<a id=\"calibre_link-2776\"><\/a><a id=\"calibre_link-2777\"><\/a><a id=\"calibre_link-783\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.32 An error boundary in the TableTemplate.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">...\n@typeparam RowType\n\n<b class=\"fm-bold\">&lt;link rel=\"stylesheet\" href=\"errorBoundaries.css\" \/&gt;<\/b>\n\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row p-2\"&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;SelectFilter Title=\"@(\"Sort\")\" \n                Values=\"@SortDirectionChoices\"\n                @bind-SelectedValue=\"SortDirectionSelection\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            <b class=\"fm-bold\">&lt;ErrorBoundary&gt;<\/b>\n                &lt;SelectFilter Title=\"@(\"Highlight\")\" \n                    Values=\"@HighlightChoices()\"\n                    @bind-SelectedValue=\"HighlightSelection\" \/&gt;\n            <b class=\"fm-bold\">&lt;\/ErrorBoundary&gt;<\/b>\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    @if (Header != null) {\n        &lt;thead&gt;@Header&lt;\/thead&gt;\n    }\n    &lt;tbody&gt;\n        @if (RowTemplate != null) {\n            @foreach (RowType item in SortedData()) {\n                &lt;tr class=\"@IsHighlighted(item)\"&gt;@RowTemplate(item)&lt;\/tr&gt;\n            }\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n...<\/pre>\n<p class=\"body\">Error boundaries are defined using the <code class=\"fm-code-in-text\">ErrorBoundary<\/code> component, which displays its child content normally until an exception is thrown, at which point the child content is removed and a <code class=\"fm-code-in-text\">div<\/code> element assigned to a <code class=\"fm-code-in-text\">blazor-error-boundary<\/code> class is displayed. To define the content and styles that will be displayed, add a CSS stylesheet named <code class=\"fm-code-in-text\">errorBoundaries.css<\/code> to the <code class=\"fm-code-in-text\">wwwroot<\/code> folder with the content shown in listing 34.33.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.33 The contents of the errorBoundaries.css file in the wwwroot folder<\/p>\n<pre class=\"programlisting\">.blazor-error-boundary {\n    background-color: darkred;\n    color: white;\n    padding: 1rem;\n    text-align: center;\n    vertical-align: middle;\n    height: 100%;\n    font-size: large;\n    font-weight: bold;\n}\n\n.blazor-error-boundary::after {\n    content: \"Error: Sales selected\"\n}<\/pre>\n<p class=\"body\">The CSS in the stylesheet displays a basic error message, which is styled in white text on a red background. (Don\u2019t worry about how these styles work because there are easier ways to define error messages, as I explain shortly.)<\/p>\n<p class=\"body\">To see the effect of the error boundary, restart ASP.NET Core, request http:\/\/localhost:5000\/pages\/blazor, and select Sales from the Highlight menu. An exception is thrown when the selection is made, which is contained by the error boundary, displaying the message shown in figure 34.14. Only the content contained within the <code class=\"fm-code-in-text\">ErrorBoundary<\/code> component is affected by the exception, which means that the rest of the application works as normal, which means that the user can still change the sort order.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre377\" src=\"\/images\/proaspnetcore7\/000383.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.14 Using an error boundary<\/p>\n<\/div>\n<p class=\"fm-head2\">Defining error content within the boundary<\/p>\n<p class=\"body\">Defining the error message in a CSS stylesheet is awkward, and I prefer to define the error content as part of the error boundary, as shown in listing 34.34.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.34 Defining error content in the TableTemplate.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row p-2\"&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;SelectFilter Title=\"@(\"Sort\")\" Values=\"@SortDirectionChoices\" \n                @bind-SelectedValue=\"SortDirectionSelection\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;ErrorBoundary&gt;\n                <b class=\"fm-bold\">&lt;ChildContent&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;SelectFilter Title=\"@(\"Highlight\")\"<\/b> \n                        <b class=\"fm-bold\">Values=\"@HighlightChoices()\"<\/b> \n                        <b class=\"fm-bold\">@bind-SelectedValue=\"HighlightSelection\" \/&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/ChildContent&gt;<\/b>\n                <b class=\"fm-bold\">&lt;ErrorContent&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;h4 class=\"bg-danger text-white text-center h-100 p-2\"&gt;<\/b>\n                        <b class=\"fm-bold\">Inline error: Sales Selected<\/b>\n                    <b class=\"fm-bold\">&lt;\/h4&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/ErrorContent&gt;<\/b>\n            &lt;\/ErrorBoundary&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ChildContent<\/code> and <code class=\"fm-code-in-text\">ErrorContent<\/code> tags are used to specify the content that will be displayed normally and when an exception has been thrown. Restart ASP.NET Core, request http:\/\/localhost:5000\/pages\/blazor, and select Sales from the Highlight menu to see the new error message, which is shown in figure 34.15.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre378\" src=\"\/images\/proaspnetcore7\/000384.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.15 Error content defined within the boundary<\/p>\n<\/div>\n<p class=\"fm-head2\">Recovering from exceptions<\/p>\n<p class=\"body\">Error boundaries allow an application to recover from exceptions, as shown in listing 34.35, although care should be taken to ensure that whatever problem caused the issue originally has truly been resolved.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 34.35 A recoverable error boundary in the Blazor\/TableTemplate.razor file<\/p>\n<pre class=\"programlisting\">@typeparam RowType\n\n&lt;link rel=\"stylesheet\" href=\"errorBoundaries.css\" \/&gt;\n\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row p-2\"&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;SelectFilter Title=\"@(\"Sort\")\" \n                Values=\"@SortDirectionChoices\"\n                @bind-SelectedValue=\"SortDirectionSelection\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            <b class=\"fm-bold\">&lt;ErrorBoundary @ref=\"boundary\"&gt;<\/b>\n                &lt;ChildContent&gt;\n                    &lt;SelectFilter Title=\"@(\"Highlight\")\" \n                        Values=\"@HighlightChoices()\" \n                        @bind-SelectedValue=\"HighlightSelection\" \/&gt;\n                &lt;\/ChildContent&gt;\n                &lt;ErrorContent&gt;\n                    &lt;h4 class=\"bg-danger text-white text-center h-100 p-2\"&gt;\n                        Inline error: Sales Selected\n                        <b class=\"fm-bold\">&lt;div&gt;<\/b>\n                            <b class=\"fm-bold\">&lt;button class=\"btn btn-light btn-sm m-1\"<\/b> \n                                <b class=\"fm-bold\">@onclick=\"@(() =&gt; boundary?.Recover())\"&gt;<\/b>\n                                    <b class=\"fm-bold\">Recover<\/b>\n                            <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n                    &lt;\/h4&gt;\n                &lt;\/ErrorContent&gt;\n            &lt;\/ErrorBoundary&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;table class=\"table table-sm table-bordered table-striped\"&gt;\n    @if (Header != null) {\n        &lt;thead&gt;@Header&lt;\/thead&gt;\n    }\n    &lt;tbody&gt;\n        @if (RowTemplate != null) {\n            @foreach (RowType item in SortedData()) {\n                &lt;tr class=\"@IsHighlighted(item)\"&gt;@RowTemplate(item)&lt;\/tr&gt;\n            }\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n@code {\n\n    <b class=\"fm-bold\">ErrorBoundary? boundary;<\/b>\n\n    \/\/ ...<i class=\"fm-italics\">other members omitted for brevity<\/i>...\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@ref<\/code> binding is used to obtain a reference to the <code class=\"fm-code-in-text\">ErrorBoundary<\/code>, which defines a <code class=\"fm-code-in-text\">Recover<\/code> method. The error content presented to the user contains a button that invokes the <code class=\"fm-code-in-text\">Recover<\/code> method when clicked, allowing the user to recover from the error.<\/p>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000\/pages\/blazor, and select Sales from the Highlight menu to trigger the error; then click the Recover button, which will display the child content once again, as shown in figure 34.16.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre379\" src=\"\/images\/proaspnetcore7\/000385.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 34.16 Recovering from an error<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2778\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Blazor components can be combined to present composite features to users.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Components can be configured using attributes in markup, which are received by applying the <code class=\"fm-code-in-text\">Parameter<\/code> attribute to code properties.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Components can define custom events, which can be consumed in the parent component\u2019s markup.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Components can also be wrappers around content, which is projected using the <code class=\"fm-code-in-text\">@ChildContent<\/code> expression. Multiple sections of content can be presented by template components.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Errors can be presented to the user by defining elements with specific classes, as described in table 34.2. These include connection errors and uncaught application errors.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The effect of errors can be contained using error boundaries.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-640\">\n<div class=\"calibre1\" id=\"calibre_link-2779\">\n<h1 class=\"tochead\" id=\"calibre_link-2780\"><a id=\"calibre_link-2781\"><\/a><a id=\"calibre_link-2782\"><\/a>35 Advanced Blazor features<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2783\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Creating routes that map requests to Blazor components<\/li>\n<li class=\"co-summary-bullet\">Navigating between components and receiving route data in a component<\/li>\n<li class=\"co-summary-bullet\">Using a layout with a routed component<\/li>\n<li class=\"co-summary-bullet\">Implementing the component lifecycle methods<\/li>\n<li class=\"co-summary-bullet\">Managing interaction between components and with JavaScript code<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I explain how Blazor supports URL routing so that multiple components can be displayed through a single request. I show you how to set up the routing system, how to define routes, and how to create common content in a layout.<\/p>\n<p class=\"body\">This chapter also covers the component lifecycle, which allows components to participate actively in the Blazor environment, which is especially important once you start using the URL routing feature. Finally, this chapter explains the different ways that components can interact outside of the parent-child relationships described in earlier chapters. Table 35.1 puts these features in context.<\/p>\n<p class=\"fm-table-caption\">Table 35.1 Putting Blazor routing and lifecycle component interactions in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2784\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The routing feature allows components to respond to changes in the URL without requiring a new HTTP connection. The lifecycle feature allows components to define methods that are invoked as the application executes, and the interaction features provide useful ways of communicating between components and with other JavaScript code.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">These features allow the creation of complex applications that take advantage of the Blazor architecture.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">URL routing is set up using built-in components and configured using <code class=\"fm-code-in-text1\">@page<\/code> directives. The lifecycle features are used by overriding methods in a component\u2019s <code class=\"fm-code-in-text1\">@code<\/code> section. The interaction features are used in different ways depending on what a component is interacting with.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">These are advanced features that must be used with care, especially when creating interactions outside of Blazor.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">All the features described in this chapter are optional, but it is hard to create complex applications without them.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 35.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 35.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2785\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Selecting components based on the current URL<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use URL routing.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">6&ndash;12<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Defining content that will be used by multiple components<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use a layout.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">13, 14<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Responding to the stages of the component\u2019s lifecycle<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Implement the lifecycle notification methods.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">15&ndash;17<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Coordinating the activities of multiple components<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Retain references with the <code class=\"fm-code-in-text1\">@ref<\/code> expression.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">18, 19<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Coordinating with code outside of Blazor<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the interoperability features.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">20&ndash;35<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-641\">35.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the Advanced project from chapter 35. No changes are required for this chapter.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">Advanced.csproj<\/code> file, and run the command shown in listing 35.1 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.1 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 35.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.2 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/controllers, which will display a list of data items. Request http:\/\/localhost:5000\/pages\/blazor, and you will see the component from chapter 34 that I used to demonstrate bindings. Figure 35.1 shows both responses.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre380\" src=\"\/images\/proaspnetcore7\/000386.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-642\">35.2 Using component routing<\/h2>\n<p class=\"body\"><a id=\"calibre_link-831\"><\/a>Blazor includes support for selecting the components to display to the user based on the ASP.NET Core routing system so that the application responds to changes in the URL by displaying different Razor Components. To get started, add a Razor Component named <code class=\"fm-code-in-text\">Routed.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder with the content shown in listing 35.3.<a id=\"calibre_link-2786\"><\/a><a id=\"calibre_link-2787\"><\/a><a id=\"calibre_link-2788\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.3 The contents of the Routed.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;Router AppAssembly=\"typeof(Program).Assembly\"&gt;\n    &lt;Found&gt;\n        &lt;RouteView RouteData=\"@context\" \/&gt;\n    &lt;\/Found&gt;\n    &lt;NotFound&gt;\n        &lt;h4 class=\"bg-danger text-white text-center p-2\"&gt;\n            No Matching Route Found\n        &lt;\/h4&gt;\n    &lt;\/NotFound&gt;\n&lt;\/Router&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Router<\/code> component is included with ASP.NET Core and provides the link between Blazor and the ASP.NET Core routing features. <code class=\"fm-code-in-text\">Router<\/code> is a generic template component that defines <code class=\"fm-code-in-text\">Found<\/code> and <code class=\"fm-code-in-text\">NotFound<\/code> sections.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Router<\/code> component requires the <code class=\"fm-code-in-text\">AppAssembly<\/code> attribute, which specifies the .NET assembly to use. For most projects this is the current assembly, which is specified like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;Router <b class=\"fm-bold\">AppAssembly=\"typeof(Program).Assembly\"<\/b>&gt;\n...<\/pre>\n<p class=\"body\">The type of the <code class=\"fm-code-in-text\">Router<\/code> component\u2019s <code class=\"fm-code-in-text\">Found<\/code> property is <code class=\"fm-code-in-text\">RenderFragment&lt;RouteData&gt;<\/code>, which is passed on to the <code class=\"fm-code-in-text\">RouteView<\/code> component through its <code class=\"fm-code-in-text\">RouteData<\/code> property, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;Found&gt;\n    <b class=\"fm-bold\">&lt;RouteView RouteData=\"@context\" \/&gt;<\/b>\n&lt;\/Found&gt;\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">RouteView<\/code> component is responsible for displaying the component matched by the current route and, as I explain shortly, for displaying common content through layouts. The type of the <code class=\"fm-code-in-text\">NotFound<\/code> property is <code class=\"fm-code-in-text\">RenderFragment<\/code>, without a generic type argument, and displays a section of content when no component can be found for the current route.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-643\">35.2.1 Preparing the Razor Page<\/h3>\n<p class=\"body\">Individual components can be displayed in existing controller views and Razor Pages, as previous chapters have shown. But when using component routing, it is preferable to create a set of URLs that are distinct to working with Blazor because the way that URLs are supported is limited and leads to tortured workarounds. Add a Razor Page named <code class=\"fm-code-in-text\">_Host.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder and add the content shown in listing 35.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.4 The contents of the _Host.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/\"\n@{ Layout = null; }\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    &lt;base href=\"~\/\" \/&gt;  \n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;component type=\"typeof(Advanced.Blazor.Routed)\" \n            render-mode=\"Server\" \/&gt;\n    &lt;\/div&gt;\n    &lt;script src=\"_framework\/blazor.server.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">This page contains a <code class=\"fm-code-in-text\">component<\/code> element that applies the <code class=\"fm-code-in-text\">Routed<\/code> component defined in listing 35.4 and a <code class=\"fm-code-in-text\">script<\/code> element for the Blazor JavaScript code. There is also a <code class=\"fm-code-in-text\">link<\/code> element for the Bootstrap CSS stylesheet. Alter the configuration for the example application to use the <code class=\"fm-code-in-text\">_Host.cshtml<\/code> file as a fallback when requests are not matched by the existing URL routes, as shown in listing 35.5.<a id=\"calibre_link-2789\"><\/a><a id=\"calibre_link-832\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.5 Adding the fallback in the Progam.cs file in the Advanced folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddServerSideBlazor();\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\napp.MapControllers();\napp.MapControllerRoute(\"controllers\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\napp.MapBlazorHub();\n<b class=\"fm-bold\">app.MapFallbackToPage(\"\/_Host\");<\/b>\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">MapFallbackToPage<\/code> method configures the routing system to use the <code class=\"fm-code-in-text\">_Host<\/code> page as a last resort for unmatched requests.<a id=\"calibre_link-2790\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-644\">35.2.2 Adding routes to components<\/h3>\n<p class=\"body\">Components declare the URLs for which they should be displayed using <code class=\"fm-code-in-text\">@page<\/code> directives. Listing 35.6 adds the <code class=\"fm-code-in-text\">@page<\/code> directive to the <code class=\"fm-code-in-text\">PeopleList<\/code> component.<a id=\"calibre_link-2791\"><\/a><a id=\"calibre_link-835\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.6 Adding a directive in the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@page \"\/people\"<\/b>\n\n&lt;TableTemplate RowType=\"Person\" RowData=\"People\"\n        Highlight=\"@(p =&gt; p.Location?.City)\" \n        SortDirection=\"@(p =&gt; p.Surname)\"&gt;\n    &lt;Header&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/Header&gt;\n    &lt;RowTemplate Context=\"p\"&gt;\n        &lt;td&gt;@p.PersonId&lt;\/td&gt;\n        &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n        &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n        &lt;td&gt;@p.Location?.City, @p.Location?.State&lt;\/td&gt;\n    &lt;\/RowTemplate&gt;\n&lt;\/TableTemplate&gt;\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt;? People =&gt; Context?.People\n            .Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location);\n}<\/pre>\n<p class=\"body\">The directive in listing 35.6 means the <code class=\"fm-code-in-text\">PeopleList<\/code> component will be displayed for the http:\/\/localhost:5000\/people URL. Components can declare support for more than one route using multiple <code class=\"fm-code-in-text\">@page<\/code> directives. Listing 35.7 adds <code class=\"fm-code-in-text\">@page<\/code> directives to the <code class=\"fm-code-in-text\">DepartmentList<\/code> component to support two URLs.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.7 Adding a directive in the DepartmentList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@page \"\/departments\"<\/b>\n<b class=\"fm-bold\">@page \"\/depts\"<\/b>\n\n&lt;CascadingValue Name=\"BgTheme\" Value=\"Theme\" IsFixed=\"false\"&gt;\n    &lt;TableTemplate RowType=\"Department\" RowData=\"Departments\"\n                   Highlight=\"@(d =&gt; d.Name)\"\n                   SortDirection=\"@(d =&gt; d.Name)\"&gt;\n        &lt;Header&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;People&lt;\/th&gt;&lt;th&gt;Locations&lt;\/th&gt;\n        &lt;\/tr&gt;\n        &lt;\/Header&gt;\n        &lt;RowTemplate Context=\"d\"&gt;\n            &lt;td&gt;@d.Departmentid&lt;\/td&gt;\n            &lt;td&gt;@d.Name&lt;\/td&gt;\n            &lt;td&gt;\n                @(String.Join(\", \", d.People!.Select(p =&gt; p.Surname)))\n            &lt;\/td&gt;\n            &lt;td&gt;\n                @(String.Join(\", \", d.People!.Select(p =&gt;\n                    p.Location!.City).Distinct()))\n            &lt;\/td&gt;\n        &lt;\/RowTemplate&gt;\n    &lt;\/TableTemplate&gt;\n&lt;\/CascadingValue&gt;\n\n&lt;SelectFilter Title=\"@(\"Theme\")\" Values=\"Themes\"\n              @bind-SelectedValue=\"Theme\" \/&gt;\n\n@code {\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Department&gt;? Departments =&gt; Context?.Departments?\n        .Include(d =&gt; d.People!).ThenInclude(p =&gt; p.Location!);\n                \n    public string Theme { get; set; } = \"info\";\n    public string[] Themes = \n        new string[] { \"primary\", \"info\", \"success\" };\n}<\/pre>\n<p class=\"body\">Most of the routing pattern features described in chapter 13 can be used in <code class=\"fm-code-in-text\">@page<\/code> expressions, except catchall segment variables and optional segment variables. Using two <code class=\"fm-code-in-text\">@page<\/code> expressions, one with a segment variable, can be used to re-create the optional variable feature, as demonstrated in chapter 36, where I show you how to implement a CRUD application using Blazor.<\/p>\n<p class=\"body\">To see the basic Razor Component routing feature at work, restart ASP.NET Core and request http:\/\/localhost:5000\/people and http:\/\/localhost:5000\/depts. Each URL displays one of the components in the application, as shown in figure 35.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre381\" src=\"\/images\/proaspnetcore7\/000387.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.2 Enabling Razor Component routing in the example application<\/p>\n<\/div>\n<p class=\"fm-head2\">Setting a default component route<\/p>\n<p class=\"body\">The configuration change in listing 35.5 sets up the fallback route for requests. A corresponding route is required in one of the application\u2019s components to identify the component that should be displayed for the application\u2019s default URL, http:\/\/ localhost:5000, as shown in listing 35.8.<a id=\"calibre_link-2792\"><\/a><a id=\"calibre_link-833\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.8 Defining the default route in the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@page \"\/people\"\n<b class=\"fm-bold\">@page \"\/\"<\/b>\n\n&lt;TableTemplate RowType=\"Person\" RowData=\"People\"\n        Highlight=\"@(p =&gt; p.Location?.City)\" \n        SortDirection=\"@(p =&gt; p.Surname)\"&gt;\n    &lt;Header&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/Header&gt;\n    &lt;RowTemplate Context=\"p\"&gt;\n        &lt;td&gt;@p.PersonId&lt;\/td&gt;\n        &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n        &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n        &lt;td&gt;@p.Location?.City, @p.Location?.State&lt;\/td&gt;\n    &lt;\/RowTemplate&gt;\n&lt;\/TableTemplate&gt;\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt;? People =&gt; Context?.People\n            .Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location);\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000, and you will see the content produced by the <code class=\"fm-code-in-text\">PeopleList<\/code> component, as shown in figure 35.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre382\" src=\"\/images\/proaspnetcore7\/000388.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.3 Displaying a component for the default URL<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-645\">35.2.3 Navigating between routed components<\/h3>\n<p class=\"body\">The basic routing configuration is in place, but it may not be obvious why using routes offers any advantages over the independent components demonstrated in earlier chapters. Improvements come through the <code class=\"fm-code-in-text\">NavLink<\/code> component, which renders anchor elements that are wired into the routing system. Listing 35.9 adds <code class=\"fm-code-in-text\">NavLink<\/code> to the <code class=\"fm-code-in-text\">PeopleList<\/code> component.<a id=\"calibre_link-2793\"><\/a><a id=\"calibre_link-2794\"><\/a><a id=\"calibre_link-836\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.9 Adding navigation in the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@page \"\/people\"\n@page \"\/\"\n\n&lt;TableTemplate RowType=\"Person\" RowData=\"People\"\n        Highlight=\"@(p =&gt; p.Location?.City)\" \n        SortDirection=\"@(p =&gt; p.Surname)\"&gt;\n    &lt;Header&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;\/tr&gt;\n    &lt;\/Header&gt;\n    &lt;RowTemplate Context=\"p\"&gt;\n        &lt;td&gt;@p.PersonId&lt;\/td&gt;\n        &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n        &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n        &lt;td&gt;@p.Location?.City, @p.Location?.State&lt;\/td&gt;\n    &lt;\/RowTemplate&gt;\n&lt;\/TableTemplate&gt;\n\n<b class=\"fm-bold\">&lt;NavLink class=\"btn btn-primary\" href=\"\/depts\"&gt;Departments&lt;\/NavLink&gt;<\/b>\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt;? People =&gt; Context?.People\n            .Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location);\n}<\/pre>\n<p class=\"body\">Unlike the anchor elements used in other parts of ASP.NET Core, <code class=\"fm-code-in-text\">Navlink<\/code> components are configured using URLs and not component, page, or action names. The <code class=\"fm-code-in-text\">NavLink<\/code> in this example navigates to the URL supported by the <code class=\"fm-code-in-text\">@page<\/code> directive of the <code class=\"fm-code-in-text\">DepartmentList<\/code> component.<\/p>\n<p class=\"body\">Navigation can also be performed programmatically, which is useful when a component responds to an event and then needs to navigate to a different URL, as shown in listing 35.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.10 Navigating in the DepartmentList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@page \"\/departments\"\n@page \"\/depts\"\n\n&lt;CascadingValue Name=\"BgTheme\" Value=\"Theme\" IsFixed=\"false\"&gt;\n    &lt;TableTemplate RowType=\"Department\" RowData=\"Departments\"\n                   Highlight=\"@(d =&gt; d.Name)\"\n                   SortDirection=\"@(d =&gt; d.Name)\"&gt;\n        &lt;Header&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;People&lt;\/th&gt;&lt;th&gt;Locations&lt;\/th&gt;\n        &lt;\/tr&gt;\n        &lt;\/Header&gt;\n        &lt;RowTemplate Context=\"d\"&gt;\n            &lt;td&gt;@d.Departmentid&lt;\/td&gt;\n            &lt;td&gt;@d.Name&lt;\/td&gt;\n            &lt;td&gt;\n                @(String.Join(\", \", d.People!.Select(p =&gt; p.Surname)))\n            &lt;\/td&gt;\n            &lt;td&gt;\n                @(String.Join(\", \", d.People!.Select(p =&gt;\n                    p.Location!.City).Distinct()))\n            &lt;\/td&gt;\n        &lt;\/RowTemplate&gt;\n    &lt;\/TableTemplate&gt;\n&lt;\/CascadingValue&gt;\n\n<b class=\"fm-bold\">&lt;SelectFilter Title=\"@(\"Theme\")\" Values=\"Themes\"<\/b>\n    <b class=\"fm-bold\">@bind-SelectedValue=\"Theme\" \/&gt;<\/b>\n\n&lt;button class=\"btn btn-primary\" @onclick=\"HandleClick\"&gt;People&lt;\/button&gt;\n\n@code {\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Department&gt;? Departments =&gt; Context?.Departments?\n        .Include(d =&gt; d.People!).ThenInclude(p =&gt; p.Location!);\n                \n    public string Theme { get; set; } = \"info\";\n    public string[] Themes = \n        new string[] { \"primary\", \"info\", \"success\" };\n                \n    <b class=\"fm-bold\">[Inject]<\/b>\n    <b class=\"fm-bold\">public NavigationManager? NavManager { get; set; }<\/b>\n        \n    <b class=\"fm-bold\">public void HandleClick() =&gt; NavManager?.NavigateTo(\"\/people\");<\/b>\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">NavigationManager<\/code> class provides programmatic access to navigation. Table 35.3 describes the most important members provided by the <code class=\"fm-code-in-text\">NavigationManager<\/code> class.<a id=\"calibre_link-2795\"><\/a><a id=\"calibre_link-837\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 35.3 Useful NavigationManager members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2796\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">NavigateTo(url)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method navigates to the specified URL without sending a new HTTP request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ToAbsoluteUri(path)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method converts a relative path to a complete URL.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ToBaseRelativePath(url)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method gets a relative path from a complete URL.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">LocationChanged<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This event is triggered when the location changes.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Uri<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the current URL.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">NavigationManager<\/code> class is provided as a service and is received by Razor Components using the <code class=\"fm-code-in-text\">Inject<\/code> attribute, which provides access to the dependency injection features described in chapter 14.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">NavigationManager.NavigateTo<\/code> method navigates to a URL and is used in this example to navigate to the <code class=\"fm-code-in-text\">\/people<\/code> URL, which will be handled by the <code class=\"fm-code-in-text\">PeopleList<\/code> component.<\/p>\n<p class=\"body\">To see why routing and navigation are important, restart ASP.NET Core and request http:\/\/localhost:5000\/people. Click the Departments link, which is styled as a button, and the <code class=\"fm-code-in-text\">DepartmentList<\/code> component will be displayed. Click the People link, and you will return to the <code class=\"fm-code-in-text\">PeopleList<\/code> component, as shown in figure 35.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre383\" src=\"\/images\/proaspnetcore7\/000389.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.4 Navigating between routed components<\/p>\n<\/div>\n<p class=\"body\">If you perform this sequence with the F12 developer tools open, you will see that the transition from one component to the next is done without needing a separate HTTP request, even though the URL displayed by the browser changes. Blazor delivers the content rendered by each component over the persistent HTTP connection that is established when the first component is displayed and uses a JavaScript API to navigate without loading a new HTML document.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> The <code class=\"fm-code-in-text1\">NavigationManager.NavigateTo<\/code> method accepts an optional argument that, when <code class=\"fm-code-in-text1\">true<\/code>, forces the browser to send a new HTTP request and reload the HTML document.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-646\">35.2.4 Receiving routing data<\/h3>\n<p class=\"body\">Components can receive segment variables by decorating a property with the <code class=\"fm-code-in-text\">Parameter<\/code> attribute. To demonstrate, add a Razor Component named <code class=\"fm-code-in-text\">PersonDisplay.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder with the content shown in listing 35.11.<a id=\"calibre_link-2797\"><\/a><a id=\"calibre_link-838\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.11 The contents of the PersonDisplay.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@page \"\/person\" \n@page \"\/person\/{id:long}\"\n\n&lt;h5&gt;Editor for Person: @Id&lt;\/h5&gt;\n\n&lt;NavLink class=\"btn btn-primary\" href=\"\/people\"&gt;Return&lt;\/NavLink&gt;\n\n@code {\n\n    [Parameter]\n    public long Id { get; set; }\n}<\/pre>\n<p class=\"body\">This component doesn\u2019t do anything other than displaying the value it receives from the routing data until I add features later in the chapter. The <code class=\"fm-code-in-text\">@page<\/code> expression includes a segment variable named <code class=\"fm-code-in-text\">id<\/code>, whose type is specified as <code class=\"fm-code-in-text\">long<\/code>. The component receives the value assigned to the segment variable by defining a property with the same name and decorating it with the <code class=\"fm-code-in-text\">Parameter<\/code> attribute.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> If you don\u2019t specify a type for segment variables in the <code class=\"fm-code-in-text1\">@page<\/code> expression, then you must set the type of the property to be <code class=\"fm-code-in-text1\">string<\/code>.<\/p>\n<p class=\"body\">Listing 35.12 uses the <code class=\"fm-code-in-text\">NavLink<\/code> component to create navigation links for each of the <code class=\"fm-code-in-text\">Person<\/code> objects displayed by the <code class=\"fm-code-in-text\">PeopleList<\/code> component.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.12 Adding navigation links in the PeopleList.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@page \"\/people\"\n@page \"\/\"\n\n&lt;TableTemplate RowType=\"Person\" RowData=\"People\"\n        Highlight=\"@(p =&gt; p.Location?.City)\" \n        SortDirection=\"@(p =&gt; p.Surname)\"&gt;\n    &lt;Header&gt;\n        &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;\n            <b class=\"fm-bold\">&lt;td&gt;&lt;\/td&gt;<\/b>\n        &lt;\/tr&gt;\n    &lt;\/Header&gt;\n    &lt;RowTemplate Context=\"p\"&gt;\n        &lt;td&gt;@p.PersonId&lt;\/td&gt;\n        &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n        &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n        &lt;td&gt;@p.Location?.City, @p.Location?.State&lt;\/td&gt;\n        <b class=\"fm-bold\">&lt;td&gt;<\/b>\n            <b class=\"fm-bold\">&lt;NavLink class=\"btn btn-sm btn-info\"<\/b> \n                    <b class=\"fm-bold\">href=\"@GetEditUrl(p.PersonId)\"&gt;<\/b>\n                <b class=\"fm-bold\">Edit<\/b>\n            <b class=\"fm-bold\">&lt;\/NavLink&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/td&gt;<\/b>\n    &lt;\/RowTemplate&gt;\n&lt;\/TableTemplate&gt;\n\n&lt;NavLink class=\"btn btn-primary\" href=\"\/depts\"&gt;Departments&lt;\/NavLink&gt;\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt;? People =&gt; Context?.People\n            .Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location);\n                        \n    <b class=\"fm-bold\">public string GetEditUrl(long id) =&gt; $\"\/person\/{id}\";<\/b>\n}<\/pre>\n<p class=\"body\">Razor Components do not support mixing static content and Razor expressions in attribute values. Instead, I have defined the <code class=\"fm-code-in-text\">GetEditUrl<\/code> method to generate the navigation URLs for each <code class=\"fm-code-in-text\">Person<\/code> object, which is called to produce the value for the <code class=\"fm-code-in-text\">NavLink<\/code> <code class=\"fm-code-in-text\">href<\/code> attributes.<\/p>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000\/people, and click one of the Edit buttons. The browser will navigate to the new URL without reloading the HTML document and display the placeholder content generated by the <code class=\"fm-code-in-text\">PersonDisplay<\/code> component, as shown in figure 35.5, which shows how a component can receive data from the routing system.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre384\" src=\"\/images\/proaspnetcore7\/000390.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.5 Receiving data from the routing system in a Razor Component<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-647\">35.2.5 Defining common content using layouts<\/h3>\n<p class=\"body\">Layouts are template components that provide common content for Razor Components. To create a layout, add a Razor Component called <code class=\"fm-code-in-text\">NavLayout.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder and add the content shown in listing 35.13.<a id=\"calibre_link-2798\"><\/a><a id=\"calibre_link-834\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.13 The contents of the NavLayout.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@inherits LayoutComponentBase\n\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row\"&gt;\n        &lt;div class=\"col-3\"&gt;\n            &lt;div class=\"d-grid gap-2\"&gt;\n                @foreach (string key in NavLinks.Keys) {\n                    &lt;NavLink class=\"btn btn-outline-primary\"\n                             href=\"@NavLinks[key]\" \n                             ActiveClass=\"btn-primary text-white\" \n                             Match=\"NavLinkMatch.Prefix\"&gt;\n                        @key\n                    &lt;\/NavLink&gt;\n                }\n            &lt;\/div&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            @Body\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n@code {\n\n    public Dictionary&lt;string, string&gt; NavLinks\n        = new Dictionary&lt;string, string&gt; {\n            {\"People\", \"\/people\" },\n            {\"Departments\", \"\/depts\" },\n            {\"Details\", \"\/person\" }\n        };\n}<\/pre>\n<p class=\"body\">Layouts use the <code class=\"fm-code-in-text\">@inherits<\/code> expression to specify the <code class=\"fm-code-in-text\">LayoutComponentBase<\/code> class as the base for the class generated from the Razor Component. The <code class=\"fm-code-in-text\">LayoutComponentBase<\/code> class defines a <code class=\"fm-code-in-text\">RenderFragment<\/code> class named <code class=\"fm-code-in-text\">Body<\/code> that is used to specify the content from components within the common content displayed by the layout. In this example, the layout component creates a grid layout that displays a set of <code class=\"fm-code-in-text\">NavLink<\/code> components for each of the components in the application. The <code class=\"fm-code-in-text\">NavLink<\/code> components are configured with two new attributes, described in table 35.4.<a id=\"calibre_link-2799\"><\/a><a id=\"calibre_link-815\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 35.4 The NavLink configuration attributes<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2800\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ActiveClass<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies one or more CSS classes that the anchor element rendered by the <code class=\"fm-code-in-text1\">NavLink<\/code> component will be added to when the current URL matches the <code class=\"fm-code-in-text1\">href<\/code> attribute value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Match<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This attribute specifies how the current URL is matched to the <code class=\"fm-code-in-text1\">href<\/code> attribute, using a value from the <code class=\"fm-code-in-text1\">NavLinkMatch<\/code> enum. The values are <code class=\"fm-code-in-text1\">Prefix<\/code>, which considers a match if the <code class=\"fm-code-in-text1\">href<\/code> matches the start of the URL, and <code class=\"fm-code-in-text1\">All<\/code>, which requires the entire URL to be the same.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">NavLink<\/code> components are configured to use <code class=\"fm-code-in-text\">Prefix<\/code> matching and to add the anchor elements they render to the Bootstrap <code class=\"fm-code-in-text\">btn-primary<\/code> and <code class=\"fm-code-in-text\">text-white<\/code> classes when there is a match.<\/p>\n<p class=\"fm-head2\">Applying a layout<\/p>\n<p class=\"body\">There are three ways that a layout can be applied. A component can select its own layout using an <code class=\"fm-code-in-text\">@layout<\/code> expression. A parent can use a layout for its child components by wrapping them in the built-in <code class=\"fm-code-in-text\">LayoutView<\/code> component. A layout can be applied to all components by setting the <code class=\"fm-code-in-text\">DefaultLayout<\/code> attribute of the <code class=\"fm-code-in-text\">RouteView<\/code> component, as shown in listing 35.14.<a id=\"calibre_link-2801\"><\/a><a id=\"calibre_link-2802\"><\/a><a id=\"calibre_link-2803\"><\/a><a id=\"calibre_link-2804\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.14 Applying a layout in the Routed.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;Router AppAssembly=\"typeof(Program).Assembly\"&gt;\n    &lt;Found&gt;\n        <b class=\"fm-bold\">&lt;RouteView RouteData=\"@context\"<\/b> \n            <b class=\"fm-bold\">DefaultLayout=\"typeof(NavLayout)\" \/&gt;<\/b>\n    &lt;\/Found&gt;\n    &lt;NotFound&gt;\n        &lt;h4 class=\"bg-danger text-white text-center p-2\"&gt;\n            No Matching Route Found\n        &lt;\/h4&gt;\n    &lt;\/NotFound&gt;\n&lt;\/Router&gt;<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/people. The layout will be displayed with the content rendered by the <code class=\"fm-code-in-text\">PeopleList<\/code> component. The navigation buttons on the left side of the layout can be used to navigate through the application, as shown in figure 35.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre385\" src=\"\/images\/proaspnetcore7\/000391.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.6 Using a layout component<\/p>\n<\/div>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> If you request http:\/\/localhost:5000, you will see the content from the <code class=\"fm-code-in-text1\">PeopleList<\/code> component, but the corresponding navigation button will not be highlighted. I show you how to resolve this problem later in the chapter.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-648\">35.3 Understanding the component lifecycle methods<\/h2>\n<p class=\"body\">Razor Components have a well-defined lifecycle, which is represented with methods that components can implement to receive notifications of key transitions. Table 35.5 describes the lifecycle methods.<a id=\"calibre_link-2805\"><\/a><a id=\"calibre_link-2806\"><\/a><a id=\"calibre_link-829\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 35.5 The Razor Component lifecycle methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2807\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnInitialized()<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnInitializedAsync()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">These methods are invoked when the component is first initialized.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnParametersSet()<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnParametersSetAsync()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">These methods are invoked after the values for properties decorated with the <code class=\"fm-code-in-text1\">Parameter<\/code> attribute have been applied.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ShouldRender()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is called before the component\u2019s content is rendered to update the content presented to the user. If the method returns <code class=\"fm-code-in-text1\">false<\/code>, the component\u2019s content will not be rendered, and the update is suppressed. This method does not suppress the initial rendering for the component.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnAfterRender(first)<\/code><\/p>\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnAfterRenderAsync(first)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is invoked after the component\u2019s content is rendered. The <code class=\"fm-code-in-text1\">bool<\/code> parameter is <code class=\"fm-code-in-text1\">true<\/code> when Blazor performs the initial render for the component.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Using either the <code class=\"fm-code-in-text\">OnInitialized<\/code> or <code class=\"fm-code-in-text\">OnParameterSet<\/code> method is useful for setting the initial state of the component. The layout defined in the previous section doesn\u2019t deal with the default URL because the <code class=\"fm-code-in-text\">NavLink<\/code> component matches only a single URL. The same issue exists for the <code class=\"fm-code-in-text\">DepartmentList<\/code> component, which can be requested using the <code class=\"fm-code-in-text\">\/departments<\/code> and <code class=\"fm-code-in-text\">\/depts<\/code> paths.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding lifecycles for routed components<\/p>\n<p class=\"fm-sidebar-text\">When using URL routing, components can be removed from the display when the URL changes. Components can implement the <code class=\"fm-code-in-text1\">System.IDisposable<\/code> interface, and Blazor will call the method when the component is removed.<\/p>\n<\/div>\n<p class=\"body\">Creating a component that matches multiple URLs requires the use of lifecycle methods. To understand why, add a Razor Component named <code class=\"fm-code-in-text\">MultiNavLink.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor<\/code> folder with the content shown in listing 35.15.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.15 The contents of the MultiNavLink.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;a class=\"@ComputedClass\" @onclick=\"HandleClick\"  href=\"\"&gt;\n    @ChildContent\n&lt;\/a&gt;\n\n@code {\n\n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n \n    [Parameter]\n    public IEnumerable&lt;string&gt; Href { get; set; } \n        = Enumerable.Empty&lt;string&gt;();\n \n    [Parameter]\n    public string Class { get; set; } = string.Empty;\n \n    [Parameter]\n    public string ActiveClass { get; set; } = string.Empty;\n \n    [Parameter]\n    public NavLinkMatch? Match { get; set; }\n \n    public NavLinkMatch ComputedMatch { get =&gt;\n            Match ?? (Href.Count() == 1 \n                ? NavLinkMatch.Prefix : NavLinkMatch.All); }\n \n    [Parameter]\n    public RenderFragment? ChildContent { get; set; }\n \n    public string ComputedClass { get; set; } = string.Empty;\n        \n    public void HandleClick() {\n        NavManager?.NavigateTo(Href.First());\n    }\n        \n    private void CheckMatch(string currentUrl) {\n        string path = NavManager!.ToBaseRelativePath(currentUrl);\n        path = path.EndsWith(\"\/\") \n            ? path.Substring(0, path.Length - 1) : path;\n        bool match = Href.Any(href =&gt; ComputedMatch == NavLinkMatch.All \n                ? path == href : path.StartsWith(href));\n        ComputedClass = match ? $\"{Class} {ActiveClass}\" : Class;\n    }\n        \n    protected override void OnParametersSet() {\n        ComputedClass = Class;\n        NavManager!.LocationChanged += \n            (sender, arg) =&gt; CheckMatch(arg.Location);\n        Href = Href.Select(h =&gt; h.StartsWith(\"\/\") ? h.Substring(1) : h);\n        CheckMatch(NavManager!.Uri);\n    }\n}<\/pre>\n<p class=\"body\">This component works in the same way as a regular <code class=\"fm-code-in-text\">NavLink<\/code> but accepts an array of paths to match. The component relies on the <code class=\"fm-code-in-text\">OnParametersSet<\/code> lifecycle method because some initial setup is required that cannot be performed until after values have been assigned to the properties decorated with the <code class=\"fm-code-in-text\">Parameter<\/code> attribute, such as extracting the individual paths.<\/p>\n<p class=\"body\">This component responds to changes in the current URL by listening for the <code class=\"fm-code-in-text\">LocationChanged<\/code> event defined by the <code class=\"fm-code-in-text\">NavigationManager<\/code> class. The event\u2019s <code class=\"fm-code-in-text\">Location<\/code> property provides the component with the current URL, which is used to alter the classes for the anchor element. Listing 35.16 applies the new component in the layout.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Notice that I have removed the <code class=\"fm-code-in-text1\">Match<\/code> attribute in listing 35.14. The new component supports this attribute but defaults to matching based on the number of paths that it receives through the <code class=\"fm-code-in-text1\">href<\/code> attribute.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.16 Applying a new component in the NavLayout.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@inherits LayoutComponentBase\n\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row\"&gt;\n        &lt;div class=\"col-3\"&gt;\n            &lt;div class=\"d-grid gap-2\"&gt;\n                @foreach (string key in NavLinks.Keys) {\n                    <b class=\"fm-bold\">&lt;MultiNavLink<\/b> \n                        <b class=\"fm-bold\">class=\"btn btn-outline-primary btn-block\"<\/b>\n                        <b class=\"fm-bold\">href=\"@NavLinks[key]\"<\/b> \n                        <b class=\"fm-bold\">ActiveClass=\"btn-primary text-white\"&gt;<\/b>\n                            <b class=\"fm-bold\">@key<\/b>\n                    <b class=\"fm-bold\">&lt;\/MultiNavLink&gt;<\/b>\n                }\n            &lt;\/div&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            @Body\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n@code {\n    <b class=\"fm-bold\">public Dictionary&lt;string, string[]&gt; NavLinks<\/b>\n        <b class=\"fm-bold\">= new Dictionary&lt;string, string[]&gt; {<\/b>\n            <b class=\"fm-bold\">{\"People\", new string[] {\"\/people\", \"\/\" } },<\/b>\n            <b class=\"fm-bold\">{\"Departments\", new string[] {\"\/depts\", \"\/departments\" } },<\/b>\n            <b class=\"fm-bold\">{\"Details\", new string[] { \"\/person\" } }<\/b>\n        <b class=\"fm-bold\">};<\/b>\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/people and http:\/\/localhost:5000\/departments. Both URLs are recognized, and the corresponding navigation buttons are highlighted, as shown in figure 35.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre386\" src=\"\/images\/proaspnetcore7\/000392.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.7 Using the lifecycle methods<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-649\">35.3.1 Using the lifecycle methods for asynchronous tasks<\/h3>\n<p class=\"body\">The lifecycle methods are also useful for performing tasks that may complete after the initial content from the component has been rendered, such as querying the database. Listing 35.17 replaces the placeholder content in the <code class=\"fm-code-in-text\">PersonDisplay<\/code> component and uses the lifecycle methods to query the database using values received as parameters.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.17 Querying for data in the PersonDisplay.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@page \"\/person\"\n@page \"\/person\/{id:long}\"\n\n<b class=\"fm-bold\">@if (Person == null) {<\/b>\n    <b class=\"fm-bold\">&lt;h5 class=\"bg-info text-white text-center p-2\"&gt;Loading...&lt;\/h5&gt;<\/b>\n<b class=\"fm-bold\">} else {<\/b>\n    <b class=\"fm-bold\">&lt;table class=\"table table-striped table-bordered\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;tbody&gt;<\/b>\n            <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Id&lt;\/th&gt;&lt;td&gt;@Person.PersonId&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n            <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Surname&lt;\/th&gt;&lt;td&gt;@Person.Surname&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n            <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Firstname&lt;\/th&gt;&lt;td&gt;@Person.Firstname&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/tbody&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/table&gt;<\/b>\n<b class=\"fm-bold\">}<\/b>\n\n<b class=\"fm-bold\">&lt;button class=\"btn btn-outline-primary\"<\/b> \n        <b class=\"fm-bold\">@onclick=\"@(() =&gt; HandleClick(false))\"&gt;<\/b>\n    <b class=\"fm-bold\">Previous<\/b>\n<b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n<b class=\"fm-bold\">&lt;button class=\"btn btn-outline-primary\"<\/b> \n        <b class=\"fm-bold\">@onclick=\"@(() =&gt; HandleClick(true))\"&gt;<\/b>\n    <b class=\"fm-bold\">Next<\/b>\n<b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n\n@code {\n\n    <b class=\"fm-bold\">[Inject]<\/b>\n    <b class=\"fm-bold\">public DataContext? Context { get; set; }<\/b>\n        \n    <b class=\"fm-bold\">[Inject]<\/b>\n    <b class=\"fm-bold\">public NavigationManager? NavManager { get; set; }<\/b>\n        \n    [Parameter]\n    public long Id { get; set; } = 0;\n        \n    <b class=\"fm-bold\">public Person? Person { get; set; }<\/b>\n        \n    <b class=\"fm-bold\">protected async override Task OnParametersSetAsync() {<\/b>\n        <b class=\"fm-bold\">await Task.Delay(1000);<\/b>\n        <b class=\"fm-bold\">if (Context != null) {<\/b>\n            <b class=\"fm-bold\">Person = await Context.People<\/b>\n                <b class=\"fm-bold\">.FirstOrDefaultAsync(p =&gt; p.PersonId == Id)<\/b>\n                  <b class=\"fm-bold\">?? new Person();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">}<\/b>\n        \n    <b class=\"fm-bold\">public void HandleClick(bool increment) {<\/b>\n        <b class=\"fm-bold\">Person = null;<\/b>\n        <b class=\"fm-bold\">NavManager?.NavigateTo(<\/b>\n            <b class=\"fm-bold\">$\"\/person\/{(increment ? Id + 1 : Id - 1)}\");<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">The component can\u2019t query the database until the parameter values have been set and so the value of the <code class=\"fm-code-in-text\">Person<\/code> property is obtained in the <code class=\"fm-code-in-text\">OnParametersSetAsync<\/code> method. Since the database is running alongside the ASP.NET Core server, I have added a one-second delay before querying the database to help emphasize the way the component works.<\/p>\n<p class=\"body\">The value of the <code class=\"fm-code-in-text\">Person<\/code> property is <code class=\"fm-code-in-text\">null<\/code> until the query has completed, at which point it will be either an object representing the query result or a new <code class=\"fm-code-in-text\">Person<\/code> object if the query doesn\u2019t produce a result. A loading message is displayed while the <code class=\"fm-code-in-text\">Person<\/code> object is <code class=\"fm-code-in-text\">null<\/code>.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000. Click one of the Edit buttons presented in the table, and the <code class=\"fm-code-in-text\">PersonDisplay<\/code> component will display a summary of the data. Click the Previous and Next buttons to query for the objects with the adjacent primary key values, producing the results shown in figure 35.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre387\" src=\"\/images\/proaspnetcore7\/000393.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.8 Performing asynchronous tasks in a component<\/p>\n<\/div>\n<p class=\"body\">Notice that Blazor doesn\u2019t wait for the <code class=\"fm-code-in-text\">Task<\/code> performed in the <code class=\"fm-code-in-text\">OnParametersSetAsync<\/code> method to complete before displaying content to the user, which is why a loading message is useful when the <code class=\"fm-code-in-text\">Person<\/code> property is <code class=\"fm-code-in-text\">null<\/code>. Once the <code class=\"fm-code-in-text\">Task<\/code> is complete and a value has been assigned to the <code class=\"fm-code-in-text\">Person<\/code> property, the component\u2019s view is automatically re-rendered, and the changes are sent to the browser over the persistent HTTP connection to be displayed to the user.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-650\">35.4 Managing component interaction<\/h2>\n<p class=\"body\">Most components work together through parameters and events, allowing the user\u2019s interaction to drive changes in the application. Blazor also provides advanced options for managing interaction with components, which I describe in the following sections.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-651\">35.4.1 Using references to child components<\/h3>\n<p class=\"body\">A parent component can obtain a reference to a child component and use it to consume the properties and methods it defines. In preparation, listing 35.18 adds a disabled state to the <code class=\"fm-code-in-text\">MultiNavLink<\/code> component.<a id=\"calibre_link-2808\"><\/a><a id=\"calibre_link-2809\"><\/a><a id=\"calibre_link-830\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.18 Adding a feature in the MultiNavLink.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;a class=\"@ComputedClass\" @onclick=\"HandleClick\"  href=\"\"&gt;\n    <b class=\"fm-bold\">@if (Enabled) {<\/b>  \n        <b class=\"fm-bold\">@ChildContent<\/b> \n    <b class=\"fm-bold\">} else {<\/b> \n        <b class=\"fm-bold\">@(\"Disabled\")<\/b> \n    <b class=\"fm-bold\">}<\/b>\n&lt;\/a&gt;\n\n@code {\n\n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n \n    [Parameter]\n    public IEnumerable&lt;string&gt; Href { get; set; } \n        = Enumerable.Empty&lt;string&gt;();\n \n    [Parameter]\n    public string Class { get; set; } = string.Empty;\n \n    [Parameter]\n    public string ActiveClass { get; set; } = string.Empty;\n \n    <b class=\"fm-bold\">[Parameter]<\/b>\n    <b class=\"fm-bold\">public string DisabledClasses { get; set; } = string.Empty;<\/b>\n        \n    [Parameter]\n    public NavLinkMatch? Match { get; set; }\n \n    public NavLinkMatch ComputedMatch { get =&gt;\n            Match ?? (Href.Count() == 1 \n                ? NavLinkMatch.Prefix : NavLinkMatch.All); }\n \n    [Parameter]\n    public RenderFragment? ChildContent { get; set; }\n \n    public string ComputedClass { get; set; } = string.Empty;\n\n    public void HandleClick() {\n        NavManager?.NavigateTo(Href.First());\n    }\n        \n    private void CheckMatch(string currentUrl) {\n        string path = NavManager!.ToBaseRelativePath(currentUrl);\n        path = path.EndsWith(\"\/\") \n            ? path.Substring(0, path.Length - 1) : path;\n        bool match = Href.Any(href =&gt; ComputedMatch == NavLinkMatch.All \n                ? path == href : path.StartsWith(href));\n        <b class=\"fm-bold\">if (!Enabled) {<\/b>\n            <b class=\"fm-bold\">ComputedClass = DisabledClasses;<\/b>\n        <b class=\"fm-bold\">} else {<\/b>\n            <b class=\"fm-bold\">ComputedClass = match ? $\"{Class} {ActiveClass}\" : Class;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n        \n    protected override void OnParametersSet() {\n        ComputedClass = Class;\n        NavManager!.LocationChanged += \n            (sender, arg) =&gt; CheckMatch(arg.Location);\n        Href = Href.Select(h =&gt; h.StartsWith(\"\/\") ? h.Substring(1) : h);\n        CheckMatch(NavManager!.Uri);\n    }\n        \n    <b class=\"fm-bold\">private bool Enabled { get; set; } = true;<\/b>\n        \n    <b class=\"fm-bold\">public void SetEnabled(bool enabled) {<\/b>\n        <b class=\"fm-bold\">Enabled = enabled;<\/b>\n        <b class=\"fm-bold\">CheckMatch(NavManager!.Uri);<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">In listing 35.19, I have updated the shared layout so that it retains references to the <code class=\"fm-code-in-text\">MultiNavLink<\/code> components and a <code class=\"fm-code-in-text\">button<\/code> that toggles their <code class=\"fm-code-in-text\">Enabled<\/code> property value.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.19 Retaining references in the NavLayout.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@inherits LayoutComponentBase\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row\"&gt;\n        &lt;div class=\"col-3\"&gt;\n            &lt;div class=\"d-grid gap-2\"&gt;\n                @foreach (string key in NavLinks.Keys) {\n                    &lt;MultiNavLink class=\"btn btn-outline-primary btn-block\"\n                        href=\"@NavLinks[key]\" \n                        ActiveClass=\"btn-primary text-white\"\n                        <b class=\"fm-bold\">DisabledClasses=\"btn btn-dark text-light<\/b> \n                            <b class=\"fm-bold\">btn-block disabled\"<\/b>\n                         <b class=\"fm-bold\">@ref=\"Refs[key]\"&gt;<\/b>\n                         @key   \n                    &lt;\/MultiNavLink&gt;\n                }\n            <b class=\"fm-bold\">&lt;button class=\"btn btn-secondary btn-block mt-5\"<\/b> \n                    <b class=\"fm-bold\">@onclick=\"ToggleLinks\"&gt;<\/b>\n                <b class=\"fm-bold\">Toggle Links<\/b>\n            <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n            &lt;\/div&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            @Body\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n@code {\n\n    public Dictionary&lt;string, string[]&gt; NavLinks\n        = new Dictionary&lt;string, string[]&gt; {\n            {\"People\", new string[] {\"\/people\", \"\/\" } },\n            {\"Departments\", new string[] {\"\/depts\", \"\/departments\" } },\n            {\"Details\", new string[] { \"\/person\" } }\n        };\n                \n    <b class=\"fm-bold\">public Dictionary&lt;string, MultiNavLink?&gt; Refs<\/b> \n        <b class=\"fm-bold\">= new Dictionary&lt;string, MultiNavLink?&gt;();<\/b>\n                \n    <b class=\"fm-bold\">private bool LinksEnabled = true;<\/b>\n        \n    <b class=\"fm-bold\">public void ToggleLinks() {<\/b>\n        <b class=\"fm-bold\">LinksEnabled = !LinksEnabled;<\/b>\n        <b class=\"fm-bold\">foreach (MultiNavLink? link in Refs.Values) {<\/b>\n            <b class=\"fm-bold\">link?.SetEnabled(LinksEnabled);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">References to components are created by adding an <code class=\"fm-code-in-text\">@ref<\/code> attribute and specifying the name of a field or property to which the component should be assigned. Since the <code class=\"fm-code-in-text\">MultiNavLink<\/code> components are created in a <code class=\"fm-code-in-text\">@foreach<\/code> loop driven by a <code class=\"fm-code-in-text\">Dictionary<\/code>, the simplest way to retain references is also in a <code class=\"fm-code-in-text\">Dictionary<\/code>, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;MultiNavLink class=\"btn btn-outline-primary btn-block\"\n    href=\"@NavLinks[key]\" ActiveClass=\"btn-primary text-white\" \n    DisabledClasses=\"btn btn-dark text-light btn-block disabled\"\n    <b class=\"fm-bold\">@ref=\"Refs[key]\"<\/b>&gt;\n...<\/pre>\n<p class=\"body\">As each <code class=\"fm-code-in-text\">MultiNavLink<\/code> component is created, it is added to the <code class=\"fm-code-in-text\">Refs<\/code> dictionary. Razor Components are compiled into standard C# classes, which means that a collection of <code class=\"fm-code-in-text\">MultiNavLink<\/code> components is a collection of <code class=\"fm-code-in-text\">MultiNavlink<\/code> objects.<\/p>\n<pre class=\"programlisting\">...\npublic Dictionary&lt;string, <b class=\"fm-bold\">MultiNavLink<\/b>&gt; Refs \n        = new Dictionary&lt;string, <b class=\"fm-bold\">MultiNavLink<\/b>&gt;();\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000, and click the Toggle Links button. The event handler invokes the <code class=\"fm-code-in-text\">ToggleLinks<\/code> method, which sets the value of the <code class=\"fm-code-in-text\">Enabled<\/code> property for each of the <code class=\"fm-code-in-text\">MultiNavLink<\/code> components, as shown in figure 35.9.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> References can be used only after the component\u2019s content has been rendered and the <code class=\"fm-code-in-text1\">OnAfterRender<\/code>\/<code class=\"fm-code-in-text1\">OnAfterRenderAsync<\/code> lifecycle methods have been invoked. This makes references ideal for use in event handlers but not the earlier lifecycle methods.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre388\" src=\"\/images\/proaspnetcore7\/000394.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.9 Retaining references to components<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-652\">35.4.2 Interacting with components from other code<\/h3>\n<p class=\"body\">Components can be used by other code in the ASP.NET Core application, allowing a richer interaction between parts of complex projects. Listing 35.20 alters the method in the <code class=\"fm-code-in-text\">MultiNavLink<\/code> component so it can be invoked by other parts of the ASP.NET Core application to enable and disable navigation.<a id=\"calibre_link-2810\"><\/a><a id=\"calibre_link-811\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.20 Replacing a method in the MultiNavLink.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;a class=\"@ComputedClass\" @onclick=\"HandleClick\"  href=\"\"&gt;\n    @if (Enabled) {  \n        @ChildContent \n    } else { \n        @(\"Disabled\") \n    }\n&lt;\/a&gt;\n\n@code {\n\n    \/\/ ...<i class=\"fm-italics\">other properties and methods omitted for brevity<\/i>...\n        \n    private bool Enabled { get; set; } = true;\n        \n    <b class=\"fm-bold\">public void SetEnabled(bool enabled) {<\/b>\n        <b class=\"fm-bold\">InvokeAsync(() =&gt; {<\/b>\n            Enabled = enabled;\n            CheckMatch(NavManager!.Uri);\n            <b class=\"fm-bold\">StateHasChanged();<\/b>\n        });\n    }\n}<\/pre>\n<p class=\"body\">Razor Components provide two methods that are used in code that is invoked outside of the Blazor environment, as described in table 35.6.<a id=\"calibre_link-2811\"><\/a><a id=\"calibre_link-2812\"><\/a><a id=\"calibre_link-810\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 35.6 The Razor component external invocation methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2813\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">InvokeAsync(func)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to execute a function inside the Blazor environment.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">StateHasChanged()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is called when a change occurs outside of the normal lifecycle, as shown in the next section.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">InvokeAsync<\/code> method is used to invoke a function within the Blazor environment, ensuring that changes are processed correctly. The <code class=\"fm-code-in-text\">StateHasChanged<\/code> method is invoked when all the changes have been applied, triggering a Blazor update and ensuring changes are reflected in the component\u2019s output.<\/p>\n<p class=\"body\">To create a service that will be available throughout the application, create the <code class=\"fm-code-in-text\">Advanced\/Services<\/code> folder and add to it a class file named <code class=\"fm-code-in-text\">ToggleService.cs<\/code>, with the code shown in listing 35.21.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.21 The contents of the ToggleService.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">using Advanced.Blazor;\n\nnamespace Advanced.Services {\n    public class ToggleService {\n        private List&lt;MultiNavLink&gt; components = new List&lt;MultiNavLink&gt;();\n        private bool enabled = true;\n                \n        public void EnrolComponents(IEnumerable&lt;MultiNavLink&gt; comps) {\n            components.AddRange(comps);\n        }\n \n        public bool ToggleComponents() {\n            enabled = !enabled;\n            components.ForEach(c =&gt; c.SetEnabled(enabled));\n            return enabled;\n        }\n    }\n}<\/pre>\n<p class=\"body\">This service manages a collection of components and invokes the <code class=\"fm-code-in-text\">SetEnabled<\/code> method on all of them when its <code class=\"fm-code-in-text\">ToggleComponents<\/code> method is called. There is nothing specific to Blazor in this service, which relies on the C# classes that are produced when Razor Component files are compiled. Listing 35.22 updates the application configuration to configure the <code class=\"fm-code-in-text\">ToggleService<\/code> class as a singleton service.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.22 Configuring a service in the Program.cs file in the Advanced folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddServerSideBlazor();\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\n<b class=\"fm-bold\">builder.Services.AddSingleton&lt;Advanced.Services.ToggleService&gt;();<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\napp.MapControllers();\napp.MapControllerRoute(\"controllers\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\napp.MapBlazorHub();\napp.MapFallbackToPage(\"\/_Host\");\n\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">Listing 35.23 updates the Blazor layout so that references to the <code class=\"fm-code-in-text\">MultiNavLink<\/code> components are retained and registered with the new service.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.23 Using the service in the NavLayout.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@inherits LayoutComponentBase\n<b class=\"fm-bold\">@using Advanced.Services<\/b>\n\n&lt;div class=\"container-fluid\"&gt;\n    &lt;div class=\"row\"&gt;\n        &lt;div class=\"col-3\"&gt;\n            &lt;div class=\"d-grid gap-2\"&gt;\n                @foreach (string key in NavLinks.Keys) {\n                    &lt;MultiNavLink class=\"btn btn-outline-primary btn-block\"\n                        href=\"@NavLinks[key]\" \n                        ActiveClass=\"btn-primary text-white\"\n                        DisabledClasses=\"btn btn-dark text-light \n                            btn-block disabled\"\n                         @ref=\"Refs[key]\"&gt;\n                         @key   \n                    &lt;\/MultiNavLink&gt;\n                }\n            &lt;button class=\"btn btn-secondary btn-block mt-5\" \n                    @onclick=\"ToggleLinks\"&gt;\n                Toggle Links\n            &lt;\/button&gt;\n            &lt;\/div&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            @Body\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n@code {\n\n    <b class=\"fm-bold\">[Inject]<\/b>\n    <b class=\"fm-bold\">public ToggleService? Toggler { get; set; }<\/b>\n        \n    public Dictionary&lt;string, string[]&gt; NavLinks\n        = new Dictionary&lt;string, string[]&gt; {\n            {\"People\", new string[] {\"\/people\", \"\/\" } },\n            {\"Departments\", new string[] {\"\/depts\", \"\/departments\" } },\n            {\"Details\", new string[] { \"\/person\" } }\n        };\n                \n    public Dictionary&lt;string, MultiNavLink?&gt; Refs \n        = new Dictionary&lt;string, MultiNavLink?&gt;();\n                \n    <b class=\"fm-bold\">\/\/private bool LinksEnabled = true;<\/b>\n        \n    <b class=\"fm-bold\">protected override void OnAfterRender(bool firstRender) {<\/b>\n        <b class=\"fm-bold\">if (firstRender &amp;&amp; Toggler != null) {<\/b>\n            <b class=\"fm-bold\">Toggler.EnrolComponents(<\/b>\n                <b class=\"fm-bold\">Refs.Values as IEnumerable&lt;MultiNavLink&gt;);<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">}<\/b>\n        \n    <b class=\"fm-bold\">public void ToggleLinks() {<\/b>\n        <b class=\"fm-bold\">Toggler?.ToggleComponents();<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">As noted in the previous section, component references are not available until after the content has been rendered. Listing 35.23 uses the <code class=\"fm-code-in-text\">OnAfterRender<\/code> lifecycle method to register the component references with the service, which is received via dependency injection.<\/p>\n<p class=\"body\">The final step is to use the service from a different part of the ASP.NET Core application. Listing 35.24 adds a simple action method to the <code class=\"fm-code-in-text\">Home<\/code> controller that invokes the <code class=\"fm-code-in-text\">ToggleService.ToggleComponents<\/code> method every time it handles a request.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.24 Adding an action in the HomeController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Advanced.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\n<b class=\"fm-bold\">using Advanced.Services;<\/b>\n\nnamespace Advanced.Controllers {\n    public class HomeController : Controller {\n        private DataContext context;\n        <b class=\"fm-bold\">private ToggleService toggleService;<\/b>\n                \n        <b class=\"fm-bold\">public HomeController(DataContext dbContext, ToggleService ts) {<\/b>\n            <b class=\"fm-bold\">context = dbContext;<\/b>\n            <b class=\"fm-bold\">toggleService = ts;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        public IActionResult Index([FromQuery] string selectedCity) {\n            return View(new PeopleListViewModel {\n                People = context.People\n                    .Include(p =&gt; p.Department).Include(p =&gt; p.Location),\n                Cities = context.Locations.Select(l =&gt; l.City).Distinct(),\n                SelectedCity = selectedCity\n            });\n        }\n \n        <b class=\"fm-bold\">public string Toggle() =&gt;<\/b> \n            <b class=\"fm-bold\">$\"Enabled: {toggleService.ToggleComponents()}\";<\/b>\n    }\n}\n\npublic class PeopleListViewModel {\n\n    public IEnumerable&lt;Person&gt; People { get; set; } \n        = Enumerable.Empty&lt;Person&gt;();\n                \n    public IEnumerable&lt;string&gt; Cities { get; set; } \n        = Enumerable.Empty&lt;string&gt;();\n                \n    public string SelectedCity { get; set; } = String.Empty;\n        \n    public string GetClass(string? city) =&gt;\n        SelectedCity == city ? \"bg-info text-white\" : \"\";\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000. Open a separate browser window and request http:\/\/localhost:5000\/controllers\/home\/toggle. When the second request is processed by the ASP.NET Core application, the action method will use the service, which toggles the state of the navigation button. Each time you request <code class=\"fm-code-in-text\">\/controllers\/home\/toggle<\/code>, the state of the navigation buttons will change, as shown in figure 35.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre389\" src=\"\/images\/proaspnetcore7\/000395.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.10 Invoking component methods<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-653\">35.4.3 Interacting with components using JavaScript<\/h3>\n<p class=\"body\">Blazor provides a range of tools for interaction between JavaScript and server-side C# code, as described in the following sections.<a id=\"calibre_link-2814\"><\/a><a id=\"calibre_link-812\"><\/a><\/p>\n<p class=\"fm-head2\">Invoking a JavaScript function from a component<\/p>\n<p class=\"body\">To prepare for these examples, add a JavaScript file named <code class=\"fm-code-in-text\">interop.js<\/code> to the <code class=\"fm-code-in-text\">wwwroot<\/code> folder and add the code shown in listing 35.25.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.25 The contents of the interop.js file in the wwwroot folder<\/p>\n<pre class=\"programlisting\">function addTableRows(colCount) {\n    let elem = document.querySelector(\"tbody\");\n    let row = document.createElement(\"tr\");\n    elem.append(row);\n    for (let i = 0; i &lt; colCount; i++) {\n        let cell = document.createElement(\"td\");\n        cell.innerText = \"New Elements\"\n        row.append(cell);\n    }\n}<\/pre>\n<p class=\"body\">The JavaScript code uses the API provided by the browser to locate a <code class=\"fm-code-in-text\">tbody<\/code> element, which denotes the body of a table and adds a new row containing the number of cells specified by the function parameter.<\/p>\n<p class=\"body\">To incorporate the JavaScript file into the application, add the element shown in listing 35.26 to the <code class=\"fm-code-in-text\">_Host<\/code> Razor Page, which was configured as the fallback page that delivers the Blazor application to the browser.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.26 Adding an element in the _Host.cshtml file in the Pages folder<\/p>\n<pre class=\"programlisting\">@page \"\/\"\n@{ Layout = null; }\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    &lt;base href=\"~\/\" \/&gt;  \n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;component type=\"typeof(Advanced.Blazor.Routed)\"\n             render-mode=\"Server\" \/&gt;\n    &lt;\/div&gt;\n    &lt;script src=\"_framework\/blazor.server.js\"&gt;&lt;\/script&gt;\n    <b class=\"fm-bold\">&lt;script src=\"~\/interop.js\"&gt;&lt;\/script&gt;<\/b>\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Listing 35.27 revises the <code class=\"fm-code-in-text\">PersonDisplay<\/code> component so that it renders a button that invokes the JavaScript function when the <code class=\"fm-code-in-text\">onclick<\/code> event is triggered. I have also removed the delay that I added earlier to demonstrate the use of the component lifecycle methods.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.27 Invoking a function in the PersonDisplay.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@page \"\/person\"\n@page \"\/person\/{id:long}\"\n\n@if (Person == null) {\n    &lt;h5 class=\"bg-info text-white text-center p-2\"&gt;Loading...&lt;\/h5&gt;\n} else {\n    &lt;table class=\"table table-striped table-bordered\"&gt;\n        &lt;tbody&gt;\n            &lt;tr&gt;&lt;th&gt;Id&lt;\/th&gt;&lt;td&gt;@Person.PersonId&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;tr&gt;&lt;th&gt;Surname&lt;\/th&gt;&lt;td&gt;@Person.Surname&lt;\/td&gt;&lt;\/tr&gt;\n            &lt;tr&gt;&lt;th&gt;Firstname&lt;\/th&gt;&lt;td&gt;@Person.Firstname&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n}\n\n<b class=\"fm-bold\">&lt;button class=\"btn btn-outline-primary\" @onclick=\"@HandleClick\"&gt;<\/b>\n    <b class=\"fm-bold\">Invoke JS Function<\/b>\n<b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n        \n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    <b class=\"fm-bold\">[Inject]<\/b>\n    <b class=\"fm-bold\">public IJSRuntime? JSRuntime { get; set; }<\/b>\n        \n    [Parameter]\n    public long Id { get; set; } = 0;\n        \n    public Person? Person { get; set; }\n        \n    protected async override Task OnParametersSetAsync() {\n        <b class=\"fm-bold\">\/\/await Task.Delay(1000);<\/b>\n        if (Context != null) {\n            Person = await Context.People\n                .FirstOrDefaultAsync(p =&gt; p.PersonId == Id)\n                  ?? new Person();\n        }\n    }\n        \n    <b class=\"fm-bold\">public async Task HandleClick() {<\/b>\n        <b class=\"fm-bold\">await JSRuntime!.InvokeVoidAsync(\"addTableRows\", 2);<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">Invoking a JavaScript function is done through the <code class=\"fm-code-in-text\">IJSRuntime<\/code> interface, which components receive through dependency injection. The service is created automatically as part of the Blazor configuration and provides the methods described in table 35.7.<a id=\"calibre_link-2815\"><\/a><a id=\"calibre_link-2816\"><\/a><a id=\"calibre_link-814\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 35.7 The IJSRuntime methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2817\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">InvokeAsync&lt;T&gt;(name, args)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method invokes the specified function with the arguments provided. The result type is specified by the generic type parameter.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">InvokeVoidAsync(name, args)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method invokes a function that doesn\u2019t produce a result.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">In listing 35.27, I use the <code class=\"fm-code-in-text\">InvokeVoidAsync<\/code> method to invoke the <code class=\"fm-code-in-text\">addTableRows<\/code> JavaScript function, providing a value for the function parameter. Restart ASP.NET Core, navigate to http:\/\/localhost:5000\/person\/1, and click the Invoke JS Function button. Blazor will invoke the JavaScript function, which adds a row to the end of the table, as shown in figure 35.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre390\" src=\"\/images\/proaspnetcore7\/000396.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.11 Invoking a JavaScript function<\/p>\n<\/div>\n<p class=\"fm-head2\">Retaining References to HTML Elements<\/p>\n<p class=\"body\">Razor Components can retain references to the HTML elements they create and pass those references to JavaScript code. Listing 35.28 changes the JavaScript function from the previous example so that it operates on an HTML element it receives through a parameter.<a id=\"calibre_link-2818\"><\/a><a id=\"calibre_link-808\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.28 Defining a parameter in the interop.js file in the wwwroot folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">function addTableRows(colCount, elem) {<\/b>\n    <b class=\"fm-bold\">\/\/let elem = document.querySelector(\"tbody\");<\/b>\n    let row = document.createElement(\"tr\");\n    <b class=\"fm-bold\">elem.parentNode.insertBefore(row, elem);<\/b>\n    for (let i = 0; i &lt; colCount; i++) {\n        let cell = document.createElement(\"td\");\n        cell.innerText = \"New Elements\"\n        row.append(cell);\n    }\n}<\/pre>\n<p class=\"body\">In listing 35.29, the <code class=\"fm-code-in-text\">PersonDisplay<\/code> component retains a reference to one of the HTML elements it creates and passes it as an argument to the JavaScript function.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.29 Retaining a reference in the PersonDisplay.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">@page \"\/person\"\n@page \"\/person\/{id:long}\"\n\n@if (Person == null) {\n    &lt;h5 class=\"bg-info text-white text-center p-2\"&gt;Loading...&lt;\/h5&gt;\n} else {\n    &lt;table class=\"table table-striped table-bordered\"&gt;\n        &lt;tbody&gt;\n            &lt;tr&gt;&lt;th&gt;Id&lt;\/th&gt;&lt;td&gt;@Person.PersonId&lt;\/td&gt;&lt;\/tr&gt;\n            <b class=\"fm-bold\">&lt;tr @ref=\"RowReference\"&gt;<\/b>\n                &lt;th&gt;Surname&lt;\/th&gt;&lt;td&gt;@Person.Surname&lt;\/td&gt;\n            &lt;\/tr&gt;\n            &lt;tr&gt;&lt;th&gt;Firstname&lt;\/th&gt;&lt;td&gt;@Person.Firstname&lt;\/td&gt;&lt;\/tr&gt;\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n}\n\n&lt;button class=\"btn btn-outline-primary\" @onclick=\"@HandleClick\"&gt;\n    Invoke JS Function\n&lt;\/button&gt;\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n        \n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    [Inject]\n    public IJSRuntime? JSRuntime { get; set; }\n \n    [Parameter]\n    public long Id { get; set; } = 0;\n        \n    public Person? Person { get; set; }\n        \n    protected async override Task OnParametersSetAsync() {\n        if (Context != null) {\n            Person = await Context.People\n                .FirstOrDefaultAsync(p =&gt; p.PersonId == Id)\n                  ?? new Person();\n        }\n    }\n        \n    <b class=\"fm-bold\">public ElementReference RowReference { get; set; }<\/b>\n        \n    <b class=\"fm-bold\">public async Task HandleClick() {<\/b>\n        <b class=\"fm-bold\">await JSRuntime!.InvokeVoidAsync(\"addTableRows\", 2, RowReference);<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">@ref<\/code> attribute assigns the HTML element to a property, whose type must be <code class=\"fm-code-in-text\">ElementReference<\/code>. Restart ASP.NET Core, request http:\/\/localhost:5000\/person\/1, and click the Invoke JS Function button. The value of the <code class=\"fm-code-in-text\">ElementReference<\/code> property is passed as an argument to the JavaScript function through the <code class=\"fm-code-in-text\">InvokeVoidAsync<\/code> method, producing the result shown in figure 35.12.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The only use for a reference to a regular HTML element is to pass it to a JavaScript function. Use the binding and event features described in earlier chapters to interact with the elements rendered by a component.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre391\" src=\"\/images\/proaspnetcore7\/000397.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.12 Retaining a reference to an HTML element<\/p>\n<\/div>\n<p class=\"fm-head2\">Invoking a component method from JavaScript<\/p>\n<p class=\"body\">The basic approach for invoking a C# method from JavaScript is to use a <code class=\"fm-code-in-text\">static<\/code> method. Listing 35.30 adds a static method to the <code class=\"fm-code-in-text\">MultiNavLink<\/code> component that changes the enabled state.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.30 Introducing static members in the MultiNavLink.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">&lt;a class=\"@ComputedClass\" @onclick=\"HandleClick\"  href=\"\"&gt;\n    @if (Enabled) {  \n        @ChildContent \n    } else { \n        @(\"Disabled\") \n    }\n&lt;\/a&gt;\n\n@code {\n\n    \/\/ ...<i class=\"fm-italics\">other methods and properties omitted for brevity<\/i>...\n        \n    <b class=\"fm-bold\">[JSInvokable]<\/b>\n    <b class=\"fm-bold\">public static void ToggleEnabled() =&gt;<\/b> \n        <b class=\"fm-bold\">ToggleEvent?.Invoke(null, new EventArgs());<\/b>\n                \n    <b class=\"fm-bold\">private static event EventHandler? ToggleEvent;<\/b>\n        \n    <b class=\"fm-bold\">protected override void OnInitialized() {<\/b>\n        <b class=\"fm-bold\">ToggleEvent += (sender, args) =&gt; SetEnabled(!Enabled);<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">Static methods must be decorated with the <code class=\"fm-code-in-text\">JSInvokable<\/code> attribute before they can be invoked from JavaScript code. The main limitation of using <code class=\"fm-code-in-text\">static<\/code> methods is that it makes it difficult to update individual components, so I have defined a <code class=\"fm-code-in-text\">static<\/code> event that each instance of the component will handle. The event is named <code class=\"fm-code-in-text\">ToggleEvent<\/code>, and it is triggered by the static method that will be called from JavaScript. To listen for the event, I have used the <code class=\"fm-code-in-text\">OnInitialized<\/code> lifecycle event. When the event is received, the enabled state of the component is toggled through the instance method <code class=\"fm-code-in-text\">SetEnabled<\/code>, which uses the <code class=\"fm-code-in-text\">InvokeAsync<\/code> and <code class=\"fm-code-in-text\">StateHasChanged<\/code> methods required when a change is made outside of Blazor.<\/p>\n<p class=\"body\">Listing 35.31 adds a function to the JavaScript file that creates a button element that invokes the static C# method when it is clicked.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.31 Adding a function in the interop.js file in the wwwroot folder<\/p>\n<pre class=\"programlisting\">function addTableRows(colCount, elem) {\n    \/\/let elem = document.querySelector(\"tbody\");\n    let row = document.createElement(\"tr\");\n    elem.parentNode.insertBefore(row, elem);\n    for (let i = 0; i &lt; colCount; i++) {\n        let cell = document.createElement(\"td\");\n        cell.innerText = \"New Elements\"\n        row.append(cell);\n    }\n}\n\n<b class=\"fm-bold\">function createToggleButton() {<\/b>\n    <b class=\"fm-bold\">let sibling = document.querySelector(\"button:last-of-type\");<\/b>\n    <b class=\"fm-bold\">let button = document.createElement(\"button\");<\/b>\n    <b class=\"fm-bold\">button.classList.add(\"btn\", \"btn-secondary\", \"btn-block\");<\/b>\n    <b class=\"fm-bold\">button.innerText = \"JS Toggle\";<\/b>\n    <b class=\"fm-bold\">sibling.parentNode.insertBefore(button, sibling.nextSibling);<\/b>\n    <b class=\"fm-bold\">button.onclick = () =&gt; DotNet.invokeMethodAsync(\"Advanced\",<\/b> \n        <b class=\"fm-bold\">\"ToggleEnabled\");<\/b>\n<b class=\"fm-bold\">}<\/b><\/pre>\n<p class=\"body\">The new function locates one of the existing <code class=\"fm-code-in-text\">button<\/code> elements and adds a new button after it. When the button is clicked, the component method is invoked, like this:<\/p>\n<pre class=\"programlisting\">...\nbutton.onclick = () =&gt; <b class=\"fm-bold\">DotNet.invokeMethodAsync<\/b>(\"Advanced\", \n    \"ToggleEnabled\");\n...<\/pre>\n<p class=\"body\">It is important to pay close attention to the capitalization of the JavaScript function used for C# methods: it is <code class=\"fm-code-in-text\">DotNet<\/code>, followed by a period, followed by <code class=\"fm-code-in-text\">invokeMethodAsync<\/code>, with a lowercase <code class=\"fm-code-in-text\">i<\/code>. The arguments are the name of the assembly and the name of the static method. (The name of the component is not required.)<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">button<\/code> element that the function in listing 35.31 looks for isn\u2019t available until after Blazor has rendered content for the user. For this reason, listing 35.32 adds a statement to the <code class=\"fm-code-in-text\">OnAfterRenderAsync<\/code> method defined by the <code class=\"fm-code-in-text\">NavLayout<\/code> component to invoke the JavaScript function only when the content has been rendered. (The <code class=\"fm-code-in-text\">NavLayout<\/code> component is the parent to the <code class=\"fm-code-in-text\">MultiNavLink<\/code> components that will be affected when the <code class=\"fm-code-in-text\">static<\/code> method is invoked and allows me to ensure the JavaScript function is invoked only once.)<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.32 Invoking a JavaScript function in the NavLayout.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">...\n@code {\n\n    [Inject]\n    public ToggleService? Toggler { get; set; }\n        \n    <b class=\"fm-bold\">[Inject]<\/b>\n    <b class=\"fm-bold\">public IJSRuntime? JSRuntime { get; set; }<\/b>\n\n    public Dictionary&lt;string, string[]&gt; NavLinks\n        = new Dictionary&lt;string, string[]&gt; {\n            {\"People\", new string[] {\"\/people\", \"\/\" } },\n            {\"Departments\", new string[] {\"\/depts\", \"\/departments\" } },\n            {\"Details\", new string[] { \"\/person\" } }\n        };\n                \n    public Dictionary&lt;string, MultiNavLink?&gt; Refs \n        = new Dictionary&lt;string, MultiNavLink?&gt;();\n                \n    \/\/private bool LinksEnabled = true;\n        \n    <b class=\"fm-bold\">protected async override Task OnAfterRenderAsync(bool firstRender) {<\/b>\n        if (firstRender &amp;&amp; Toggler != null) {\n            Toggler.EnrolComponents(\n                Refs.Values as IEnumerable&lt;MultiNavLink&gt;);\n            <b class=\"fm-bold\">await JSRuntime!.InvokeVoidAsync(\"createToggleButton\");<\/b>\n        }\n    }\n        \n    public void ToggleLinks() {\n        Toggler?.ToggleComponents();\n    }\n}\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000. Once Blazor has rendered its content, the JavaScript function will be called and creates a new button. Clicking the button invokes the <code class=\"fm-code-in-text\">static<\/code> method, which triggers the event that toggles the state of the navigation buttons and causes a Blazor update, as shown in figure 35.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre392\" src=\"\/images\/proaspnetcore7\/000398.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 35.13 Invoking a component method from JavaScript<\/p>\n<\/div>\n<p class=\"fm-head2\">Invoking an instance method from a JavaScript function<\/p>\n<p class=\"body\">Part of the complexity in the previous example comes from responding to a <code class=\"fm-code-in-text\">static<\/code> method to update the Razor Component objects. An alternative approach is to provide the JavaScript code with a reference to an instance method, which it can then invoke directly.<\/p>\n<p class=\"body\">The first step is to add the <code class=\"fm-code-in-text\">JSInvokable<\/code> attribute to the method that the JavaScript code will invoke. I am going to invoke the <code class=\"fm-code-in-text\">ToggleComponents<\/code> methods defined by the <code class=\"fm-code-in-text\">ToggleService<\/code> class, as shown in listing 35.33.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.33 Applying an attribute in the ToggleService.cs file in the Services folder<\/p>\n<pre class=\"programlisting\">using Advanced.Blazor;\n<b class=\"fm-bold\">using Microsoft.JSInterop;<\/b>\n\nnamespace Advanced.Services {\n    public class ToggleService {\n        \n        private List&lt;MultiNavLink&gt; components = new List&lt;MultiNavLink&gt;();\n        private bool enabled = true;\n                \n        public void EnrolComponents(IEnumerable&lt;MultiNavLink&gt; comps) {\n            components.AddRange(comps);\n        }\n                \n        <b class=\"fm-bold\">[JSInvokable]<\/b>\n        public bool ToggleComponents() {\n            enabled = !enabled;\n            components.ForEach(c =&gt; c.SetEnabled(enabled));\n            return enabled;\n        }\n    }\n}<\/pre>\n<p class=\"body\">The next step is to provide the JavaScript function with a reference to the object whose method will be invoked, as shown in listing 35.34.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.34 Providing an instance in the NavLayout.razor file in the Blazor folder<\/p>\n<pre class=\"programlisting\">...\nprotected async override Task OnAfterRenderAsync(bool firstRender) {\n    if (firstRender &amp;&amp; Toggler != null) {\n        Toggler.EnrolComponents(\n            Refs.Values as IEnumerable&lt;MultiNavLink&gt;);\n        <b class=\"fm-bold\">await JSRuntime!.InvokeVoidAsync(\"createToggleButton\",<\/b>\n            <b class=\"fm-bold\">DotNetObjectReference.Create(Toggler));<\/b>\n    }\n}\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">DotNetObjectReference.Create<\/code> method creates a reference to an object, which is passed to the JavaScript function as an argument using the <code class=\"fm-code-in-text\">JSRuntime.InvokeVoidAsync<\/code> method. The final step is to receive the object reference in JavaScript and invoke its method when the button element is clicked, as shown in listing 35.35.<a id=\"calibre_link-2819\"><\/a><a id=\"calibre_link-813\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 35.35 Invoking a C# method in the interop.js file in the wwwroot folder<\/p>\n<pre class=\"programlisting\">function addTableRows(colCount, elem) {\n    \/\/let elem = document.querySelector(\"tbody\");\n    let row = document.createElement(\"tr\");\n    elem.parentNode.insertBefore(row, elem);\n    for (let i = 0; i &lt; colCount; i++) {\n        let cell = document.createElement(\"td\");\n        cell.innerText = \"New Elements\"\n        row.append(cell);\n    }\n}\n\n<b class=\"fm-bold\">function createToggleButton(toggleServiceRef) {<\/b>\n    let sibling = document.querySelector(\"button:last-of-type\");\n    let button = document.createElement(\"button\");\n    button.classList.add(\"btn\", \"btn-secondary\", \"btn-block\");\n    button.innerText = \"JS Toggle\";\n    sibling.parentNode.insertBefore(button, sibling.nextSibling);\n    <b class=\"fm-bold\">button.onclick = () =&gt;<\/b> \n        <b class=\"fm-bold\">toggleServiceRef.invokeMethodAsync(\"ToggleComponents\");<\/b>\n}<\/pre>\n<p class=\"body\">The JavaScript function receives the reference to the C# object as a parameter and invokes its methods using <code class=\"fm-code-in-text\">invokeMethodAsync<\/code>, specifying the name of the method as the argument. (Arguments to the method can also be provided but are not required in this example.)<\/p>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000, and click the JS Toggle button. The result is the same as shown in figure 35.13, but the change in the components is managed through the <code class=\"fm-code-in-text\">ToggleService<\/code> object.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-2820\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The component used to handle a request can be selected using routes, which are defined using the <code class=\"fm-code-in-text\">@page<\/code> expression.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The <code class=\"fm-code-in-text\">NavLink<\/code> component is used to navigate between components with routes.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Components have a well-defined lifecycle, through which methods are invoked at key moments, including initialization, configuration, and content rendering.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Parent components can obtain references to child components using the <code class=\"fm-code-in-text\">@ref<\/code> expression.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Blazor supports interaction with JavaScript code running the browser. Components can invoke JavaScript functions and JavaScript code can invoke C# component methods.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-654\">\n<div class=\"calibre1\" id=\"calibre_link-2821\">\n<h1 class=\"tochead\" id=\"calibre_link-2822\"><a id=\"calibre_link-2823\"><\/a>36 Blazor forms and data<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using built-in components to create HTML forms<\/li>\n<li class=\"co-summary-bullet\">Validating form data<\/li>\n<li class=\"co-summary-bullet\">Responding to form events<\/li>\n<li class=\"co-summary-bullet\">Using Entity Framework Core with Blazor components<\/li>\n<li class=\"co-summary-bullet\">Performing CRUD operations<\/li>\n<li class=\"co-summary-bullet\">Extending Blazor<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I describe the features that Blazor provides for dealing with HTML forms, including support for data validation. I describe the built-in components that Blazor provides and show you how they are used. In this chapter, I also explain how the Blazor model can cause unexpected results with Entity Framework Core and show you how to address these issues. I finish the chapter by creating a simple form application for creating, reading, updating, and deleting data (the CRUD operations) and explain how to extend the Blazor form features to improve the user\u2019s experience. Table 36.1 puts the Blazor form features in context.<\/p>\n<p class=\"fm-table-caption\">Table 36.1 Putting Blazor form features in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2824\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What are they?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Blazor provides a set of built-in components that present the user with a form that can be easily validated.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why are they useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Forms remain one of the core building blocks of web applications, and these components provide functionality that most projects will require.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How are they used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">EditForm<\/code> component is used as a parent for individual form field components.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">There can be issues with the way that Entity Framework Core and Blazor work together, and these become especially apparent when using forms.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">You could create your own form components and validation features, although the features described in this chapter are suitable for most projects and, as I demonstrate, can be easily extended.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 36.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 36.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2825\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creating an HTML form<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">EditForm<\/code> and <code class=\"fm-code-in-text1\">Input*<\/code> components.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">7&ndash;9, 13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Validating data<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the standard validation attributes and the events emitted by the <code class=\"fm-code-in-text1\">EditForm<\/code> component.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">10&ndash;12<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Discarding unsaved data<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Explicitly release the data or create new scopes for components.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14&ndash;16<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Avoiding repeatedly querying the database<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Manage query execution explicitly.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">17&ndash;19<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-655\">36.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the Advanced project from chapter 35. To prepare for this chapter, create the <code class=\"fm-code-in-text\">Blazor\/Forms<\/code> folder and add to it a Razor Component named <code class=\"fm-code-in-text\">EmptyLayout.razor<\/code> with the content shown in listing 36.1. I will use this component as the main layout for this chapter.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.1 The contents of the EmptyLayout.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@inherits LayoutComponentBase\n\n&lt;div class=\"m-2\"&gt;\n    @Body\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">Add a <code class=\"fm-code-in-text\">RazorComponent<\/code> named <code class=\"fm-code-in-text\">FormSpy.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor\/Forms<\/code> folder with the content shown in listing 36.2. This is a component I will use to display form elements alongside the values that are being edited.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.2 The contents of the FormSpy.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">&lt;div class=\"container-fluid no-gutters\"&gt;\n    &lt;div class=\"row\"&gt;\n        &lt;div class=\"col\"&gt;\n            @ChildContent\n        &lt;\/div&gt;\n        &lt;div class=\"col\"&gt;\n            &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n                &lt;thead&gt;\n                    &lt;tr&gt;\n                        &lt;th colspan=\"2\" class=\"text-center\"&gt;\n                            Data Summary\n                        &lt;\/th&gt;\n                    &lt;\/tr&gt;\n                &lt;\/thead&gt;\n                &lt;tbody&gt;\n                    &lt;tr&gt;\n                        &lt;th&gt;ID&lt;\/th&gt;&lt;td&gt;@PersonData?.PersonId&lt;\/td&gt;\n                    &lt;\/tr&gt;\n                    &lt;tr&gt;\n                        &lt;th&gt;Firstname&lt;\/th&gt;&lt;td&gt;@PersonData?.Firstname&lt;\/td&gt;\n                    &lt;\/tr&gt;\n                    &lt;tr&gt;\n                        &lt;th&gt;Surname&lt;\/th&gt;&lt;td&gt;@PersonData?.Surname&lt;\/td&gt;\n                    &lt;\/tr&gt;\n                    &lt;tr&gt;\n                        &lt;th&gt;Dept ID&lt;\/th&gt;\n                        &lt;td&gt;@PersonData?.DepartmentId&lt;\/td&gt;\n                    &lt;\/tr&gt;\n                    &lt;tr&gt;\n                        &lt;th&gt;Location ID&lt;\/th&gt;\n                        &lt;td&gt;@PersonData?.LocationId&lt;\/td&gt;\n                    &lt;\/tr&gt;\n                &lt;\/tbody&gt;\n            &lt;\/table&gt;            \n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n@code {\n\n    [Parameter]\n    public RenderFragment? ChildContent { get; set; }\n \n    [Parameter]\n    public Person PersonData { get; set; } = new();\n}<\/pre>\n<p class=\"body\">Next, add a component named <code class=\"fm-code-in-text\">Editor.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor\/Forms<\/code> folder and add the content shown in listing 36.3. This component will edit existing <code class=\"fm-code-in-text\">Person<\/code> objects and create new ones.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Do not use the <code class=\"fm-code-in-text1\">Editor<\/code> and <code class=\"fm-code-in-text1\">List<\/code> components in real projects until you have read the rest of the chapter. I have included common pitfalls that I explain later in the chapter.<a id=\"calibre_link-809\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.3 The contents of the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n@layout EmptyLayout\n\n&lt;h4 class=\"bg-primary text-center text-white p-2\"&gt;Edit&lt;\/h4&gt;\n\n&lt;FormSpy PersonData=\"PersonData\"&gt;\n    &lt;h4 class=\"text-center\"&gt;Form Placeholder&lt;\/h4&gt;\n    &lt;div class=\"text-center\"&gt;\n        &lt;NavLink class=\"btn btn-secondary mt-2\" href=\"\/forms\"&gt;\n            Back\n        &lt;\/NavLink&gt;\n    &lt;\/div&gt;\n&lt;\/FormSpy&gt;\n\n@code {\n\n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    [Inject]\n    DataContext? Context { get; set; }\n \n    [Parameter]\n    public long Id { get; set; }\n \n    public Person PersonData { get; set; } = new();\n        \n    protected async override Task OnParametersSetAsync() {\n        if (Context != null) {\n            PersonData = await Context.People.FindAsync(Id) \n                ?? new Person();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The component in listing 36.3 uses an <code class=\"fm-code-in-text\">@layout<\/code> expression to override the default layout and select <code class=\"fm-code-in-text\">EmptyLayout<\/code>. The side-by-side layout is used to present the <code class=\"fm-code-in-text\">PersonTable<\/code> component alongside a placeholder, which is where I will add a form.<\/p>\n<p class=\"body\">Finally, create a component named <code class=\"fm-code-in-text\">List.razor<\/code> in the <code class=\"fm-code-in-text\">Blazor\/Forms<\/code> folder and add the content shown in listing 36.4 to define a component that will present the user with a table that lists <code class=\"fm-code-in-text\">Person<\/code> objects.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.4 The contents of the List.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\"\n@page \"\/forms\/list\"\n@layout EmptyLayout\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;People&lt;\/h5&gt;\n\n&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;\n            &lt;th&gt;Name&lt;\/th&gt;\n            &lt;th&gt;Dept&lt;\/th&gt;\n            &lt;th&gt;Location&lt;\/th&gt;\n            &lt;th&gt;&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @if (People.Count() == 0) {\n            &lt;tr&gt;\n                &lt;th colspan=\"5\" class=\"p-4 text-center\"&gt;\n                    Loading Data...\n                &lt;\/th&gt;\n            &lt;\/tr&gt;\n        } else {\n            @foreach (Person p in People) {\n                &lt;tr&gt;\n                    &lt;td&gt;@p.PersonId&lt;\/td&gt;\n                    &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n                    &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n                    &lt;td&gt;@p.Location?.City&lt;\/td&gt;\n                    &lt;td&gt;\n                        &lt;NavLink class=\"btn btn-sm btn-warning\"\n                         href=\"@GetEditUrl(p.PersonId)\"&gt;\n                            Edit\n                        &lt;\/NavLink&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt; People { get; set; } \n        = Enumerable.Empty&lt;Person&gt;();\n                \n    protected override void OnInitialized() {\n        People = Context?.People?.Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location)\n                ?? Enumerable.Empty&lt;Person&gt;();\n    }\n        \n    string GetEditUrl(long id) =&gt; $\"\/forms\/edit\/{id}\";\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-656\">36.1.1 Dropping the database and running the application<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">Advanced.csproj<\/code> file, and run the command shown in listing 36.5 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.5 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 36.6.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.6 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/forms, which will produce a data table. Click one of the Edit buttons, and you will see a placeholder for the form and a summary showing the current property values of the selected <code class=\"fm-code-in-text\">Person<\/code> object, as shown in figure 36.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre393\" src=\"\/images\/proaspnetcore7\/000399.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-657\">36.2 Using the Blazor form components<\/h2>\n<p class=\"body\">Blazor provides a set of built-in components that are used to render form elements, ensuring that the server-side component properties are updated after user interaction and integrating validation. Table 36.3 describes the components that Blazor provides.<a id=\"calibre_link-2826\"><\/a><a id=\"calibre_link-2827\"><\/a><a id=\"calibre_link-2828\"><\/a><a id=\"calibre_link-2829\"><\/a><a id=\"calibre_link-2830\"><\/a><a id=\"calibre_link-2831\"><\/a><a id=\"calibre_link-2832\"><\/a><a id=\"calibre_link-2833\"><\/a><a id=\"calibre_link-2834\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 36.3 The Blazor form components<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2835\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">EditForm<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This component renders a <code class=\"fm-code-in-text1\">form<\/code> element that is wired up for data validation.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">InputText<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This component renders an <code class=\"fm-code-in-text1\">input<\/code> element that is bound to a C# <code class=\"fm-code-in-text1\">string<\/code> property.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">InputCheckbox<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This component renders an input element whose <code class=\"fm-code-in-text1\">type<\/code> attribute is <code class=\"fm-code-in-text1\">checkbox<\/code> and that is bound to a C# <code class=\"fm-code-in-text1\">bool<\/code> property.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">InputDate<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This component renders an input element those <code class=\"fm-code-in-text1\">type<\/code> attribute is <code class=\"fm-code-in-text1\">date<\/code> and that is bound to a C# <code class=\"fm-code-in-text1\">DateTime<\/code> or <code class=\"fm-code-in-text1\">DateTimeOffset<\/code> property.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">InputNumber<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This component renders an input element those <code class=\"fm-code-in-text1\">type<\/code> attribute is <code class=\"fm-code-in-text1\">number<\/code>and that is bound to a C# <code class=\"fm-code-in-text1\">int<\/code>, <code class=\"fm-code-in-text1\">long<\/code>, <code class=\"fm-code-in-text1\">float<\/code>, <code class=\"fm-code-in-text1\">double<\/code>, or <code class=\"fm-code-in-text1\">decimal<\/code> value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">InputTextArea<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This component renders a <code class=\"fm-code-in-text1\">textarea<\/code> component that is bound to a <code class=\"fm-code-in-text1\">C#<\/code> string property.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">EditForm<\/code> component must be used for any of the other components to work. In listing 36.7, I have added an <code class=\"fm-code-in-text\">EditForm<\/code>, along with <code class=\"fm-code-in-text\">InputText<\/code> components that represent two of the properties defined by the <code class=\"fm-code-in-text\">Person<\/code> class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.7 Using form components in the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n@layout EmptyLayout\n\n&lt;h4 class=\"bg-primary text-center text-white p-2\"&gt;Edit&lt;\/h4&gt;\n\n&lt;FormSpy PersonData=\"PersonData\"&gt;\n    <b class=\"fm-bold\">&lt;EditForm Model=\"PersonData\"&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;Person ID&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;InputNumber class=\"form-control\"<\/b>\n                <b class=\"fm-bold\">@bind-Value=\"PersonData.PersonId\" disabled \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;Firstname&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;InputText class=\"form-control\"<\/b> \n                <b class=\"fm-bold\">@bind-Value=\"PersonData.Firstname\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;Surname&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;InputText class=\"form-control\"<\/b> \n                <b class=\"fm-bold\">@bind-Value=\"PersonData.Surname\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;Dept ID&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;InputNumber class=\"form-control\"<\/b>\n                         <b class=\"fm-bold\">@bind-Value=\"PersonData.DepartmentId\" \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"text-center\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;NavLink class=\"btn btn-secondary\" href=\"\/forms\"&gt;<\/b>\n                <b class=\"fm-bold\">Back<\/b>\n            <b class=\"fm-bold\">&lt;\/NavLink&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">&lt;\/EditForm&gt;<\/b>\n&lt;\/FormSpy&gt;\n\n@code {\n\n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    [Inject]\n    DataContext? Context { get; set; }\n \n    [Parameter]\n    public long Id { get; set; }\n \n    public Person PersonData { get; set; } = new();\n        \n    protected async override Task OnParametersSetAsync() {\n        if (Context != null) {\n            PersonData = await Context.People.FindAsync(Id) \n                ?? new Person();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">EditForm<\/code> component renders a <code class=\"fm-code-in-text\">form<\/code> element and provides the foundation for the validation features described in the \u201cValidating Form Data\u201d section. The <code class=\"fm-code-in-text\">Model<\/code> attribute provides the <code class=\"fm-code-in-text\">EditForm<\/code> with the object that the form uses to edit and validate.<\/p>\n<p class=\"body\">The components in table 36.3 whose names begin with <code class=\"fm-code-in-text\">Input<\/code> are used to display an <code class=\"fm-code-in-text\">input<\/code> or <code class=\"fm-code-in-text\">textarea<\/code> element for a single model property. These components define a custom binding named <code class=\"fm-code-in-text\">Value<\/code> that is associated with the model property using the <code class=\"fm-code-in-text\">@bind-Value<\/code> attribute. The property-level components must be matched to the type of the property they present to the user. It is for this reason that I have used the <code class=\"fm-code-in-text\">InputText<\/code> component for the <code class=\"fm-code-in-text\">Firstname<\/code> and <code class=\"fm-code-in-text\">Surname<\/code> properties of the <code class=\"fm-code-in-text\">Person<\/code> class, while the <code class=\"fm-code-in-text\">InputNumber<\/code> component is used for the <code class=\"fm-code-in-text\">PersonId<\/code> and <code class=\"fm-code-in-text\">DepartmentId<\/code> properties. If you use a property-level component with a model property of the wrong type, you will receive an error when the component attempts to parse a value entered into the HTML element.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/forms\/edit\/2, and you will see the three <code class=\"fm-code-in-text\">input<\/code> elements displayed. Edit the values and move the focus by pressing the Tab key, and you will see the summary data on the right of the window update, as shown in figure 36.2. The built-in form components support attribute splatting, which is why the <code class=\"fm-code-in-text\">disabled<\/code> attribute applied to the <code class=\"fm-code-in-text\">InputNumber<\/code> component for the <code class=\"fm-code-in-text\">PersonId<\/code> property has been applied to the <code class=\"fm-code-in-text\">input<\/code> element.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre394\" src=\"\/images\/proaspnetcore7\/000400.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.2 Using the Blazor form elements<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-658\">36.2.1 Creating custom form components<\/h3>\n<p class=\"body\">Blazor provides built-in components for only <code class=\"fm-code-in-text\">input<\/code> and <code class=\"fm-code-in-text\">textarea<\/code> elements. Fortunately, creating a custom component that integrates into the Blazor form features is a simple process. Add a Razor Component named <code class=\"fm-code-in-text\">CustomSelect.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor\/Forms<\/code> folder and use it to define the component shown in listing 36.8.<a id=\"calibre_link-2836\"><\/a><a id=\"calibre_link-806\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.8 The contents of the CustomSelect.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@typeparam TValue\n@inherits InputBase&lt;TValue&gt;\n@using System.Diagnostics.CodeAnalysis\n\n&lt;select class=\"form-control @CssClass\" value=\"@CurrentValueAsString\" \n            @onchange=\"@(ev =&gt; CurrentValueAsString = ev.Value as string)\"&gt;\n        @ChildContent\n        @foreach (KeyValuePair&lt;string, TValue&gt; kvp in Values) {\n            &lt;option value=\"@kvp.Value\"&gt;@kvp.Key&lt;\/option&gt;\n        }\n&lt;\/select&gt;\n\n@code {\n\n    [Parameter]\n    public RenderFragment? ChildContent { get; set; }\n \n    [Parameter]\n    public IDictionary&lt;string, TValue&gt; Values { get; set; }\n        = new Dictionary&lt;string, TValue&gt;();\n \n    [Parameter]\n    public Func&lt;string, TValue&gt;? Parser { get; set; }\n        \n    protected override bool TryParseValueFromString(string? value, \n           [MaybeNullWhen(false)] out TValue? result, \n           [NotNullWhen(false)] out string? validationErrorMessage) {\n        try {\n            if (Parser != null &amp;&amp; value != null) {\n                result = Parser(value);\n                validationErrorMessage = null;\n                return true;    \n            }\n            result = default(TValue);\n            validationErrorMessage = \"Value or parser not defined\";\n            return false;\n        } catch {\n            result = default(TValue);\n            validationErrorMessage = \"The value is not valid\";\n            return false;\n        }\n    }\n}<\/pre>\n<p class=\"body\">The base class for form components is <code class=\"fm-code-in-text\">InputBase&lt;TValue&gt;<\/code>, where the generic type argument is the model property type the component represents.<\/p>\n<p class=\"body\">The base class takes care of most of the work and provides the <code class=\"fm-code-in-text\">CurrentValueAsString<\/code> property, which is used to provide the current value in event handlers when the user selects a new value, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;select class=\"form-control @CssClass\" <b class=\"fm-bold\">value=\"@CurrentValueAsString\"<\/b> \n            <b class=\"fm-bold\">@onchange=\"@(ev =&gt; CurrentValueAsString = ev.Value as string)<\/b>\"&gt;\n...<\/pre>\n<p class=\"body\">In preparation for data validation, which I describe in the next section, this component includes the value of the <code class=\"fm-code-in-text\">CssClass<\/code> property in the <code class=\"fm-code-in-text\">select<\/code> element\u2019s <code class=\"fm-code-in-text\">class<\/code> attribute, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;select class=\"form-control <b class=\"fm-bold\">@CssClass<\/b>\" value=\"@CurrentValueAsString\" \n            @onchange=\"@(ev =&gt; CurrentValueAsString = ev.Value as string)\"&gt;\n...<\/pre>\n<p class=\"body\">The abstract <code class=\"fm-code-in-text\">TryParseValueFromString<\/code> method has to be implemented so that the base class is able to map between string values used by HTML elements and the corresponding value for the C# model property. I don\u2019t want to implement my custom <code class=\"fm-code-in-text\">select<\/code> element to any specific C# data type, so I have used an <code class=\"fm-code-in-text\">@typeparam<\/code> expression to define a generic type parameter.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> At the time of writing, typing override and selecting the <code class=\"fm-code-in-text1\">TryParseValueFromString<\/code> from the list of options in Visual Studio creates a method with the wrong signature. It is important to pay close attention to the parameters, especially when the code analysis attributes, described in chapter 5, are used.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Values<\/code> property is used to receive a dictionary mapping string values that will be displayed to the user and <code class=\"fm-code-in-text\">TValue<\/code> values that will be used as C# values. The method receives two <code class=\"fm-code-in-text\">out<\/code> parameters that are used to set the parsed value and a parser validation error message that will be displayed to the user if there is a problem. Since I am working with generic types, the <code class=\"fm-code-in-text\">Parser<\/code> property receives a function that is invoked to parse a string value into a <code class=\"fm-code-in-text\">TValue<\/code> value.<\/p>\n<p class=\"body\">Listing 36.9 applies the new form component so the user can select values for the <code class=\"fm-code-in-text\">DepartmentId<\/code> and <code class=\"fm-code-in-text\">LocationId<\/code> properties defined by the <code class=\"fm-code-in-text\">Person<\/code> class.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.9 Using a custom element in the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n@layout EmptyLayout\n\n&lt;h4 class=\"bg-primary text-center text-white p-2\"&gt;Edit&lt;\/h4&gt;\n\n&lt;FormSpy PersonData=\"PersonData\"&gt;\n    &lt;EditForm Model=\"PersonData\"&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Person ID&lt;\/label&gt;\n            &lt;InputNumber class=\"form-control\"\n                         @bind-Value=\"PersonData.PersonId\" disabled \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Firstname&lt;\/label&gt;\n            &lt;InputText class=\"form-control\"\n                       @bind-Value=\"PersonData.Firstname\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Surname&lt;\/label&gt;\n            &lt;InputText class=\"form-control\"\n                       @bind-Value=\"PersonData.Surname\" \/&gt;\n        &lt;\/div&gt;\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;Dept ID&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;CustomSelect TValue=\"long\" Values=\"Departments\"<\/b>\n                          <b class=\"fm-bold\">Parser=\"@((string str) =&gt; long.Parse(str))\"<\/b>\n                          <b class=\"fm-bold\">@bind-Value=\"PersonData.DepartmentId\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;option selected disabled value=\"0\"&gt;<\/b>\n                    <b class=\"fm-bold\">Choose a Department<\/b>\n                <b class=\"fm-bold\">&lt;\/option&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/CustomSelect&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;Location ID&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;CustomSelect TValue=\"long\" Values=\"Locations\"<\/b>\n                          <b class=\"fm-bold\">Parser=\"@((string str) =&gt; long.Parse(str))\"<\/b>\n                          <b class=\"fm-bold\">@bind-Value=\"PersonData.LocationId\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;option selected disabled value=\"0\"&gt;<\/b>\n                    <b class=\"fm-bold\">Choose a Location<\/b>\n                <b class=\"fm-bold\">&lt;\/option&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/CustomSelect&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n        &lt;div class=\"text-center\"&gt;\n            &lt;NavLink class=\"btn btn-secondary mt-2\" href=\"\/forms\"&gt;\n                Back\n            &lt;\/NavLink&gt;\n        &lt;\/div&gt;\n    &lt;\/EditForm&gt;\n&lt;\/FormSpy&gt;\n\n@code {\n\n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    [Inject]\n    DataContext? Context { get; set; }\n \n    [Parameter]\n    public long Id { get; set; }\n \n    public Person PersonData { get; set; } = new();\n        \n    public IDictionary&lt;string, long&gt; Departments { get; set; }\n        = new Dictionary&lt;string, long&gt;();\n                \n    public IDictionary&lt;string, long&gt; Locations { get; set; }\n        = new Dictionary&lt;string, long&gt;();\n                \n    protected async override Task OnParametersSetAsync() {\n        if (Context != null) {\n            PersonData = await Context.People.FindAsync(Id) \n                ?? new Person();\n            <b class=\"fm-bold\">Departments = await Context.Departments<\/b>\n                <b class=\"fm-bold\">.ToDictionaryAsync(d =&gt; d.Name, d =&gt; d.Departmentid);<\/b>\n            <b class=\"fm-bold\">Locations = await Context.Locations<\/b>\n                <b class=\"fm-bold\">.ToDictionaryAsync(l =&gt; $\"{l.City}, {l.State}\",<\/b> \n                    <b class=\"fm-bold\">l =&gt; l.LocationId);<\/b>\n        }\n    }\n}<\/pre>\n<p class=\"body\">I use the Entity Framework Core <code class=\"fm-code-in-text\">ToDictionaryAsync<\/code> method to create collections of values and labels from the <code class=\"fm-code-in-text\">Department<\/code> and <code class=\"fm-code-in-text\">Location<\/code> data and use them to configure the <code class=\"fm-code-in-text\">CustomSelect<\/code> components. Restart ASP.NET Core and request http:\/\/localhost:5000\/forms\/edit\/2; you will see the <code class=\"fm-code-in-text\">select<\/code> elements shown in figure 36.3. When you pick a new value, the <code class=\"fm-code-in-text\">CustomSelect<\/code> component will update the <code class=\"fm-code-in-text\">CurrentValueAsString<\/code> property, which will result in a call to the <code class=\"fm-code-in-text\">TryParseValueFromString<\/code> method, with the result used to update the <code class=\"fm-code-in-text\">Value<\/code> binding.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre395\" src=\"\/images\/proaspnetcore7\/000401.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.3 Using a custom form element<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-659\">36.2.2 Validating form data<\/h3>\n<p class=\"body\">Blazor provides components that perform validation using the standard attributes. Table 36.4 describes the validation components.<a id=\"calibre_link-2837\"><\/a><a id=\"calibre_link-2838\"><\/a><a id=\"calibre_link-2839\"><\/a><a id=\"calibre_link-2840\"><\/a><a id=\"calibre_link-807\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 36.4 The Blazor validation components<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2841\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"35%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"65%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">DataAnnotationsValidator<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This component integrates the validation attributes applied to the model class into the Blazor form features.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ValidationMessage<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This component displays validation error messages for a single property.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ValidationSummary<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This component displays validation error messages for the entire model object.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The validation components generate elements assigned to classes, described in table 36.5, which can be styled with CSS to draw the user\u2019s attention.<a id=\"calibre_link-2842\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 36.5 The classes used by the Blazor validation components<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2843\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"35%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"65%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">validation-errors<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">ValidationSummary<\/code> component generates a <code class=\"fm-code-in-text1\">ul<\/code> element that is assigned to this class and is the top-level container for the summary of validation messages.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">validation-message<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">The <code class=\"fm-code-in-text1\">ValidationSummary<\/code> component populates its <code class=\"fm-code-in-text1\">ul<\/code> element with <code class=\"fm-code-in-text1\">li<\/code> elements assigned to this class for each validation message. The <code class=\"fm-code-in-text1\">ValidationMessage<\/code> component renders a <code class=\"fm-code-in-text1\">div<\/code> element assigned to this class for its property-level messages.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The Blazor <code class=\"fm-code-in-text\">Input*<\/code> components add the HTML elements they generate to the classes described in table 36.6 to indicate validation status. This includes the <code class=\"fm-code-in-text\">InputBase&lt;TValue&gt;<\/code> class from which I derived the <code class=\"fm-code-in-text\">CustomSelect<\/code> component and is the purpose of the <code class=\"fm-code-in-text\">CssClass<\/code> property in listing 36.8.<\/p>\n<p class=\"fm-table-caption\">Table 36.6 The validation classes added to form elements<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2844\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"35%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"65%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">modified<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Elements are added to this class once the user has edited the value.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">valid<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Elements are added to this class if the value they contain passes validation.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">invalid<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Elements are added to this class if the value they contain fails validation.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">This combination of components and classes can be confusing at first, but the key is to start by defining the CSS styles you require based on the classes in table 36.5 and 36.6. Add a CSS Stylesheet named <code class=\"fm-code-in-text\">blazorValidation.css<\/code> to the <code class=\"fm-code-in-text\">wwwroot<\/code> folder with the content shown in listing 36.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.10 The Contents of the blazorValidation.css File in the wwwroot Folder<\/p>\n<pre class=\"programlisting\">.validation-errors {\n    background-color: rgb(220, 53, 69); color: white; padding: 8px; \n    text-align: center; font-size: 16px; font-weight: 500;\n}\ndiv.validation-message { color: rgb(220, 53, 69); font-weight: 500 }\n.modified.valid { border: solid 3px rgb(40, 167, 69); }\n.modified.invalid { border: solid 3px rgb(220, 53, 69); }<\/pre>\n<p class=\"body\">These styles format error messages in red and apply a red or green border to individual form elements. Listing 36.11 imports the CSS stylesheet and applies the Blazor validation components.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.11 Applying validation in the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n@layout EmptyLayout\n\n<b class=\"fm-bold\">&lt;link href=\"\/blazorValidation.css\" rel=\"stylesheet\" \/&gt;<\/b>\n&lt;h4 class=\"bg-primary text-center text-white p-2\"&gt;Edit&lt;\/h4&gt;\n\n&lt;FormSpy PersonData=\"PersonData\"&gt;\n    &lt;EditForm Model=\"PersonData\"&gt;\n        &lt;DataAnnotationsValidator \/&gt;\n        &lt;ValidationSummary \/&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Person ID&lt;\/label&gt;\n            &lt;InputNumber class=\"form-control\"\n                         @bind-Value=\"PersonData.PersonId\" disabled \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Firstname&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;ValidationMessage For=\"@(() =&gt; PersonData.Firstname)\" \/&gt;<\/b>\n            &lt;InputText class=\"form-control\" \n                @bind-Value=\"PersonData.Firstname\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Surname&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;ValidationMessage For=\"@(() =&gt; PersonData.Surname)\" \/&gt;<\/b>\n            &lt;InputText class=\"form-control\" \n                @bind-Value=\"PersonData.Surname\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Dept ID&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;ValidationMessage For=\"@(() =&gt; PersonData.DepartmentId)\" \/&gt;<\/b>\n            &lt;CustomSelect TValue=\"long\" Values=\"Departments\"\n                          Parser=\"@((string str) =&gt; long.Parse(str))\"\n                          @bind-Value=\"PersonData.DepartmentId\"&gt;\n                &lt;option selected disabled value=\"0\"&gt;\n                    Choose a Department\n                &lt;\/option&gt;\n            &lt;\/CustomSelect&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Location ID&lt;\/label&gt;\n            <b class=\"fm-bold\">&lt;ValidationMessage For=\"@(() =&gt; PersonData.LocationId)\" \/&gt;<\/b>\n            &lt;CustomSelect TValue=\"long\" Values=\"Locations\"\n                          Parser=\"@((string str) =&gt; long.Parse(str))\"\n                          @bind-Value=\"PersonData.LocationId\"&gt;\n                &lt;option selected disabled value=\"0\"&gt;\n                    Choose a Location\n                &lt;\/option&gt;\n            &lt;\/CustomSelect&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"text-center\"&gt;\n            &lt;NavLink class=\"btn btn-secondary mt-2\" href=\"\/forms\"&gt;\n                Back\n            &lt;\/NavLink&gt;\n        &lt;\/div&gt;\n    &lt;\/EditForm&gt;\n&lt;\/FormSpy&gt;\n\n@code {\n\n   \/\/ ...members omitted for brevity...\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">DataAnnotationsValidator<\/code> and <code class=\"fm-code-in-text\">ValidationSummary<\/code> components are applied without any configuration attributes. The <code class=\"fm-code-in-text\">ValidationMessage<\/code> attribute is configured using the <code class=\"fm-code-in-text\">For<\/code> attribute, which receives a function that returns the property the component represents. For example, here is the expression that selects the <code class=\"fm-code-in-text\">Firstname<\/code> property:<\/p>\n<pre class=\"programlisting\">...\n&lt;ValidationMessage <b class=\"fm-bold\">For=\"@(() =&gt; PersonData.Firstname)<\/b>\" \/&gt;\n...<\/pre>\n<p class=\"body\">The expression defines no parameters and selects the property from the object used for the <code class=\"fm-code-in-text\">Model<\/code> attribute of the <code class=\"fm-code-in-text\">EditForm<\/code> component and not the model type. For this example, this means the expression operates on the <code class=\"fm-code-in-text\">PersonData<\/code> object and not the <code class=\"fm-code-in-text\">Person<\/code> class.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Blazor isn\u2019t always able to determine the type of the property for the <code class=\"fm-code-in-text1\">ValidationMessage<\/code> component. If you receive an exception, then you can add a <code class=\"fm-code-in-text1\">TValue<\/code> attribute to set the type explicitly. For example, if the type of the property the <code class=\"fm-code-in-text1\">ValidationMessage<\/code> component represents is <code class=\"fm-code-in-text1\">long<\/code>, then add a <code class=\"fm-code-in-text1\">TValue=\"long\"<\/code> attribute.<\/p>\n<p class=\"body\">The final step for enabling data validation is to apply attributes to the model class, as shown in listing 36.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.12 Applying validation attributes in the Person.cs file in the Models folder<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">using System.ComponentModel.DataAnnotations;<\/b>\n\nnamespace Advanced.Models {\n\n    public class Person {\n        \n        public long PersonId { get; set; }\n                \n        <b class=\"fm-bold\">[Required(ErrorMessage = \"A firstname is required\")]<\/b>\n        <b class=\"fm-bold\">[MinLength(3, ErrorMessage<\/b> \n            <b class=\"fm-bold\">= \"Firstnames must be 3 or more characters\")]<\/b>\n        public string Firstname { get; set; } = String.Empty;\n                \n        <b class=\"fm-bold\">[Required(ErrorMessage = \"A surname is required\")]<\/b>\n        <b class=\"fm-bold\">[MinLength(3, ErrorMessage<\/b> \n            <b class=\"fm-bold\">= \"Surnames must be 3 or more characters\")]<\/b>\n        public string Surname { get; set; } = String.Empty;\n                \n        <b class=\"fm-bold\">[Range(1, long.MaxValue,<\/b>\n            <b class=\"fm-bold\">ErrorMessage = \"A department must be selected\")]<\/b>\n        public long DepartmentId { get; set; }\n                \n        <b class=\"fm-bold\">[Range(1, long.MaxValue,<\/b>\n            <b class=\"fm-bold\">ErrorMessage = \"A location must be selected\")]<\/b>\n        public long LocationId { get; set; }\n                \n        public Department? Department { get; set; }\n        public Location? Location { get; set; }\n    }\n}<\/pre>\n<p class=\"body\">To see the effect of the validation components, restart ASP.NET Core and request http:\/\/localhost:5000\/forms\/edit\/2. Clear the <code class=\"fm-code-in-text\">Firstname<\/code> field and move the focus by pressing the Tab key or clicking on another field. As the focus changes, validation is performed, and error messages will be displayed. The <code class=\"fm-code-in-text\">Editor<\/code> component shows both summary and per-property messages, so you will see the same error message shown twice. Delete all but the first two characters from the Surname field, and a second validation message will be displayed when you change the focus, as shown in figure 36.4. (There is validation support for the other properties, too, but the <code class=\"fm-code-in-text\">select<\/code> element doesn\u2019t allow the user to select an invalid valid. If you change a value, the <code class=\"fm-code-in-text\">select<\/code> element will be decorated with a green border to indicate a valid selection, but you won\u2019t be able to see an invalid response until I demonstrate how the form components can be used to create new data objects.)<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre396\" src=\"\/images\/proaspnetcore7\/000402.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.4 Using the Blazor validation features<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-660\">36.2.3 Handling form events<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">EditForm<\/code> component defines events that allow an application to respond to user action, as described in table 36.7.<\/p>\n<p class=\"fm-table-caption\">Table 36.7 The EditForm events<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2845\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnValidSubmit<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This event is triggered when the form is submitted and the form data passes validation.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnInvalidSubmit<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This event is triggered when the form is submitted and the form data fails validation.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnSubmit<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This event is triggered when the form is submitted and before validation is performed.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">These events are triggered by adding a conventional submit button within the content contained by the <code class=\"fm-code-in-text\">EditForm<\/code> component. The <code class=\"fm-code-in-text\">EditForm<\/code> component handles the <code class=\"fm-code-in-text\">onsubmit<\/code> event sent by the <code class=\"fm-code-in-text\">form<\/code> element it renders, applies validation, and triggers the events described in the table. Listing 36.13 adds a submit button to the <code class=\"fm-code-in-text\">Editor<\/code> component and handles the <code class=\"fm-code-in-text\">EditForm<\/code> events.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.13 Handling events in the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n@layout EmptyLayout\n\n&lt;link href=\"\/blazorValidation.css\" rel=\"stylesheet\" \/&gt;\n&lt;h4 class=\"bg-primary text-center text-white p-2\"&gt;Edit&lt;\/h4&gt;\n<b class=\"fm-bold\">&lt;h6 class=\"bg-info text-center text-white p-2\"&gt;@FormSubmitMessage&lt;\/h6&gt;<\/b>\n&lt;FormSpy PersonData=\"PersonData\"&gt;\n    <b class=\"fm-bold\">&lt;EditForm Model=\"PersonData\" OnValidSubmit=\"HandleValidSubmit\"<\/b> \n            <b class=\"fm-bold\">OnInvalidSubmit=\"HandleInvalidSubmit\"&gt;<\/b>\n        &lt;DataAnnotationsValidator \/&gt;\n        &lt;ValidationSummary \/&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Person ID&lt;\/label&gt;\n            &lt;InputNumber class=\"form-control\"\n                         @bind-Value=\"PersonData.PersonId\" disabled \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Firstname&lt;\/label&gt;\n            &lt;ValidationMessage For=\"@(() =&gt; PersonData.Firstname)\" \/&gt;\n            &lt;InputText class=\"form-control\" \n                @bind-Value=\"PersonData.Firstname\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Surname&lt;\/label&gt;\n            &lt;ValidationMessage For=\"@(() =&gt; PersonData.Surname)\" \/&gt;\n            &lt;InputText class=\"form-control\" \n                @bind-Value=\"PersonData.Surname\" \/&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Dept ID&lt;\/label&gt;\n            &lt;ValidationMessage For=\"@(() =&gt; PersonData.DepartmentId)\" \/&gt;\n            &lt;CustomSelect TValue=\"long\" Values=\"Departments\"\n                          Parser=\"@((string str) =&gt; long.Parse(str))\"\n                          @bind-Value=\"PersonData.DepartmentId\"&gt;\n                &lt;option selected disabled value=\"0\"&gt;\n                    Choose a Department\n                &lt;\/option&gt;\n            &lt;\/CustomSelect&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;Location ID&lt;\/label&gt;\n            &lt;ValidationMessage For=\"@(() =&gt; PersonData.LocationId)\" \/&gt;\n            &lt;CustomSelect TValue=\"long\" Values=\"Locations\"\n                          Parser=\"@((string str) =&gt; long.Parse(str))\"\n                          @bind-Value=\"PersonData.LocationId\"&gt;\n                &lt;option selected disabled value=\"0\"&gt;\n                    Choose a Location\n                &lt;\/option&gt;\n            &lt;\/CustomSelect&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"text-center\"&gt;\n            <b class=\"fm-bold\">&lt;button type=\"submit\" class=\"btn btn-primary mt-2\"&gt;<\/b>\n                <b class=\"fm-bold\">Submit<\/b>\n            <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n            &lt;NavLink class=\"btn btn-secondary mt-2\" href=\"\/forms\"&gt;\n                Back\n            &lt;\/NavLink&gt;\n        &lt;\/div&gt;\n    &lt;\/EditForm&gt;\n&lt;\/FormSpy&gt;\n\n@code {\n\n    \/\/ ...members omitted brevity...\n        \n    <b class=\"fm-bold\">public string FormSubmitMessage { get; set; }<\/b> \n        <b class=\"fm-bold\">= \"Form Data Not Submitted\";<\/b>\n                \n    <b class=\"fm-bold\">public void HandleValidSubmit() =&gt; FormSubmitMessage<\/b> \n        <b class=\"fm-bold\">= \"Valid Data Submitted\";<\/b>\n                \n    <b class=\"fm-bold\">public void HandleInvalidSubmit() =&gt; FormSubmitMessage<\/b> \n        <b class=\"fm-bold\">= \"Invalid Data Submitted\";<\/b>\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/forms\/edit\/2. Clear the Firstname field, and click the Submit button. In addition to the validation error, you will see a message indicating that the form was submitted with invalid data. Enter a name into the field and click Submit again, and the message will change, as shown in figure 36.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre397\" src=\"\/images\/proaspnetcore7\/000403.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.5 Handling EditForm events<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-661\">36.3 Using Entity Framework Core with Blazor<\/h2>\n<p class=\"body\">The Blazor model changes the way that Entity Framework Core behaves, which can lead to unexpected results if you are used to writing conventional ASP.NET Core applications. In the sections that follow, I explain the issues and how to avoid the problems that can arise.<a id=\"calibre_link-2846\"><\/a><a id=\"calibre_link-2847\"><\/a><a id=\"calibre_link-820\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-662\">36.3.1 Understanding the EF Core context scope issue<\/h3>\n<p class=\"body\">To see the first issue, request http:\/\/localhost:5000\/forms\/edit\/4, clear the Firstname field, change the contents of the Surname field to La, and press Tab to change the focus.<\/p>\n<p class=\"body\">Neither of the new values passes validation, and you will see error messages as you move between the form elements. Click the Back button, and you will see that the data table reflects the changes you made, as shown in figure 36.6, even though they were not valid.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre398\" src=\"\/images\/proaspnetcore7\/000404.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.6 The effect of editing data<\/p>\n<\/div>\n<p class=\"body\"><a id=\"calibre_link-2848\"><\/a>In a conventional ASP.NET Core application, written using controllers or Razor Pages, clicking a button triggers a new HTTP request. Each request is handled in isolation, and each request receives its own Entity Framework Core context object, which is configured as a scoped service. The result is that the data created when handling one request affects other requests only once it has been written to the database.<\/p>\n<p class=\"body\">In a Blazor application, the routing system responds to URL changes without sending new HTTP requests, which means that multiple components are displayed using only the persistent HTTP connection that Blazor maintains to the server. This results in a single dependency injection scope being shared by multiple components, as shown in figure 36.7, and the changes made by one component will affect other components even if the changes are not written to the database.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre399\" src=\"\/images\/proaspnetcore7\/000405.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.7 The use of an Entity Framework Core context in a Blazor application<\/p>\n<\/div>\n<p class=\"body\">Entity Framework Core is trying to be helpful, and this approach allows complex data operations to be performed over time before being stored (or discarded). Unfortunately, much like the helpful approach Entity Framework Core takes to dealing with related data, which I described in chapter 35, it presents a pitfall for the unwary developer who expects components to handle data like the rest of ASP.NET Core.<\/p>\n<p class=\"fm-head2\">Discarding unsaved data changes<\/p>\n<p class=\"body\">If sharing a context between components is appealing, which it will be for some applications, then you can embrace the approach and ensure that components discard any changes when they are destroyed, as shown in listing 36.14.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.14 Discarding unsaved data in the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n@layout EmptyLayout\n<b class=\"fm-bold\">@implements IDisposable<\/b>\n\n&lt;!-- ...<i class=\"fm-italics\">elements omitted for brevity<\/i>... --&gt;\n\n@code {\n\n    \/\/ ...<i class=\"fm-italics\">members omitted for brevity<\/i>...\n        \n    public void HandleInvalidSubmit() =&gt; FormSubmitMessage \n        = \"Invalid Data Submitted\";\n                \n    <b class=\"fm-bold\">public void Dispose() {<\/b>\n        <b class=\"fm-bold\">if (Context != null) {<\/b>\n            <b class=\"fm-bold\">Context.Entry(PersonData).State = EntityState.Detached;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">As I noted in chapter 35, components can implement the <code class=\"fm-code-in-text\">System.IDisposable<\/code> interface, and the <code class=\"fm-code-in-text\">Dispose<\/code> method will be invoked when the component is about to be destroyed, which happens when navigation to another component occurs. In listing 36.14, the implementation of the <code class=\"fm-code-in-text\">Dispose<\/code> method tells Entity Framework Core to disregard the <code class=\"fm-code-in-text\">PersonData<\/code> object, which means it won\u2019t be used to satisfy future requests. To see the effect, restart ASP.NET Core, request http:\/\/localhost:5000\/forms\/edit\/4, clear the Firstname field, and click the Back button. The modified <code class=\"fm-code-in-text\">Person<\/code> object is disregarded when Entity Framework Core provides the <code class=\"fm-code-in-text\">List<\/code> component with its data, as shown in figure 36.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre400\" src=\"\/images\/proaspnetcore7\/000406.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.8 Discarding data objects<\/p>\n<\/div>\n<p class=\"fm-head2\">Creating new dependency injection scopes<\/p>\n<p class=\"body\">You must create new dependency injection scopes if you want to preserve the model used by the rest of ASP.NET Core and have each component receive its own Entity Framework Core context object. This is done by using the <code class=\"fm-code-in-text\">@inherits<\/code> expression to set the base class for the component to <code class=\"fm-code-in-text\">OwningComponentBase<\/code> or <code class=\"fm-code-in-text\">OwningComponentBase&lt;T&gt;<\/code>.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OwningComponentCase<\/code> class defines a <code class=\"fm-code-in-text\">ScopedServices<\/code> property that is inherited by the component and that provides an <code class=\"fm-code-in-text\">IServiceProvider<\/code> object that can be used to obtain services that are created in a scope that is specific to the component\u2019s lifecycle and will not be shared with any other component, as shown in listing 36.15.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.15 Using a new scope in the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n@layout EmptyLayout\n<b class=\"fm-bold\">@inherits OwningComponentBase<\/b>\n<b class=\"fm-bold\">@using Microsoft.Extensions.DependencyInjection<\/b>\n\n&lt;!-- ...<i class=\"fm-italics\">elements omitted for brevity<\/i>... --&gt;\n\n@code {\n\n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    <b class=\"fm-bold\">\/\/[Inject]<\/b>\n    <b class=\"fm-bold\">DataContext? Context =&gt; ScopedServices.GetService&lt;DataContext&gt;();<\/b>\n        \n    [Parameter]\n    public long Id { get; set; }\n        \n    \/\/ ...<i class=\"fm-italics\">members omitted for brevity<\/i>...\n        \n    public void HandleInvalidSubmit() =&gt; FormSubmitMessage \n        = \"Invalid Data Submitted\";\n                \n    <b class=\"fm-bold\">\/\/public void Dispose() {<\/b>\n    <b class=\"fm-bold\">\/\/    if (Context != null) {<\/b>\n    <b class=\"fm-bold\">\/\/        Context.Entry(PersonData).State = EntityState.Detached;<\/b>\n    <b class=\"fm-bold\">\/\/    }<\/b>\n    <b class=\"fm-bold\">\/\/}<\/b>\n}<\/pre>\n<p class=\"body\">In the listing, I commented out the <code class=\"fm-code-in-text\">Inject<\/code> attribute and set the value of the <code class=\"fm-code-in-text\">Context<\/code> property by obtaining a <code class=\"fm-code-in-text\">DataContext<\/code> service. The <code class=\"fm-code-in-text\">Microsoft.Extensions.DependencyInjection<\/code> namespace contains extension methods that make it easier to obtain services from an <code class=\"fm-code-in-text\">IServiceProvider<\/code> object, as described in chapter 14.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Changing the base class doesn\u2019t affect services that are received using the <code class=\"fm-code-in-text1\">Inject<\/code> attribute, which will still be obtained within the request scope. Each service that you require in the dedicated component\u2019s scope must be obtained through the <code class=\"fm-code-in-text1\">ScopedServices<\/code> property, and the <code class=\"fm-code-in-text1\">Inject<\/code> attribute should not be applied to that property.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">OwningComponentBase&lt;T&gt;<\/code> class defines an additional convenience property that provides access to a scoped service of type <code class=\"fm-code-in-text\">T<\/code> and that can be useful if a component requires only a single scoped service, as shown in listing 36.16 (although further services can still be obtained through the <code class=\"fm-code-in-text\">ScopedServices<\/code> property).<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.16 Using the typed base in the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n@layout EmptyLayout\n<b class=\"fm-bold\">@inherits OwningComponentBase&lt;DataContext&gt;<\/b>\n\n&lt;link href=\"\/blazorValidation.css\" rel=\"stylesheet\" \/&gt;\n&lt;h4 class=\"bg-primary text-center text-white p-2\"&gt;Edit&lt;\/h4&gt;\n&lt;h6 class=\"bg-info text-center text-white p-2\"&gt;@FormSubmitMessage&lt;\/h6&gt;\n\n&lt;!-- ...<i class=\"fm-italics\">elements omitted for brevity<\/i>... --&gt;\n\n@code {\n\n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    \/\/[Inject]\n    <b class=\"fm-bold\">DataContext? Context =&gt; Service;<\/b>\n        \n    [Parameter]\n    public long Id { get; set; }\n        \n    \/\/ ...<i class=\"fm-italics\">statements omitted for brevity<\/i>...\n}<\/pre>\n<p class=\"body\">The scoped service is available through a property named <code class=\"fm-code-in-text\">Service<\/code>. In this example, I specified <code class=\"fm-code-in-text\">DataContext<\/code> as the type argument for the base class.<\/p>\n<p class=\"body\">Regardless of which base class is used, the result is that the <code class=\"fm-code-in-text\">Editor<\/code> component has its own dependency injection scope and its own <code class=\"fm-code-in-text\">DataContext<\/code> object. The <code class=\"fm-code-in-text\">List<\/code> component has not been modified, so it will receive the request-scoped <code class=\"fm-code-in-text\">DataContext<\/code> object, as shown in figure 36.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre401\" src=\"\/images\/proaspnetcore7\/000407.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.9 Using scoped services for components<\/p>\n<\/div>\n<p class=\"body\">Restart ASP.NET Core, navigate to http:\/\/localhost:5000\/forms\/edit\/4, clear the Firstname field, and click the Back button. The changes made by the Editor component are not saved to the database, and since the <code class=\"fm-code-in-text\">Editor<\/code> component\u2019s data context is separate from the one used by the <code class=\"fm-code-in-text\">List<\/code> component, the edited data is discarded, producing the same response as shown in figure 36.8.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-663\">36.3.2 Understanding the repeated query issue<\/h3>\n<p class=\"body\">Blazor responds to changes in state as efficiently as possible but still has to render a component\u2019s content to determine the changes that should be sent to the browser.<\/p>\n<p class=\"body\">One consequence of the way that Blazor works is that it can lead to a sharp increase in the number of queries sent to the database. To demonstrate the issue, listing 36.17 adds a button that increments a counter to the <code class=\"fm-code-in-text\">List<\/code> component.<a id=\"calibre_link-2849\"><\/a><a id=\"calibre_link-821\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.17 Adding a button in the List.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\"\n@page \"\/forms\/list\"\n@layout EmptyLayout\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;People&lt;\/h5&gt;\n\n&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;th&gt;&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @if  (People.Count() == 0) {\n            &lt;tr&gt;\n                &lt;th colspan=\"5\" class=\"p-4 text-center\"&gt;\n                    Loading Data...\n                &lt;\/th&gt;\n            &lt;\/tr&gt;\n        } else {\n            @foreach (Person p in People) {\n                &lt;tr&gt;\n                    &lt;td&gt;@p.PersonId&lt;\/td&gt;\n                    &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n                    &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n                    &lt;td&gt;@p.Location?.City&lt;\/td&gt;\n                    &lt;td&gt;\n                        &lt;NavLink class=\"btn btn-sm btn-warning\" \n                             href=\"@GetEditUrl(p.PersonId)\"&gt;\n                            Edit\n                        &lt;\/NavLink&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n<b class=\"fm-bold\">&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; Counter++)\"&gt;<\/b>\n    <b class=\"fm-bold\">Increment<\/b>\n<b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n<b class=\"fm-bold\">&lt;span class=\"h5\"&gt;Counter: @Counter&lt;\/span&gt;<\/b>\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt; People { get; set; } \n        = Enumerable.Empty&lt;Person&gt;();\n                \n    protected override void OnInitialized() {\n        People = Context?.People?.Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location) \n                ?? Enumerable.Empty&lt;Person&gt;();\n    }\n        \n    string GetEditUrl(long id) =&gt; $\"\/forms\/edit\/{id}\";\n        \n    <b class=\"fm-bold\">public int Counter { get; set; } = 0;<\/b>\n}<\/pre>\n<p class=\"body\"><a id=\"calibre_link-2850\"><\/a>Restart ASP.NET Core and request http:\/\/localhost:5000\/forms. Click the <code class=\"fm-code-in-text\">Increment<\/code> button and watch the output from the ASP.NET Core server. Each time you click the button, the event handler is invoked, and a new database query is sent to the database, producing logging messages like these:<\/p>\n<pre class=\"programlisting\"><code class=\"fm-code-in-text1\">...<\/code>\n<code class=\"fm-code-in-text1\">info: Microsoft.EntityFrameworkCore.Database.Command[20101]<\/code>\n<code class=\"fm-code-in-text1\">      Executed DbCommand (0ms) [Parameters=[], CommandType='Text',<\/code> \n<code class=\"fm-code-in-text1\">      CommandTimeout='30']<\/code>\n<code class=\"fm-code-in-text1\">      SELECT [p].[PersonId], [p].[DepartmentId], [p].[Firstname],<\/code> \n<code class=\"fm-code-in-text1\">        [p].[LocationId],  [p].[Surname], [d].[Departmentid],<\/code> \n<code class=\"fm-code-in-text1\">        [d].[Name], [l].[LocationId], [l].[City], [l].[State]<\/code>\n<code class=\"fm-code-in-text1\">      FROM [People] AS [p]<\/code>\n<code class=\"fm-code-in-text1\">      INNER JOIN [Departments] AS [d] ON [p].[DepartmentId]<\/code> \n<code class=\"fm-code-in-text1\">        = [d].[Departmentid]<\/code>\n<code class=\"fm-code-in-text1\">      INNER JOIN [Locations] AS [l] ON [p].[LocationId] = [l].[LocationId]<\/code>\n<code class=\"fm-code-in-text1\">info: Microsoft.EntityFrameworkCore.Database.Command[20101]<\/code>\n<code class=\"fm-code-in-text1\">      Executed DbCommand (0ms) [Parameters=[], CommandType='Text',<\/code> \n<code class=\"fm-code-in-text1\">       CommandTimeout='30']<\/code>\n<code class=\"fm-code-in-text1\">      SELECT [p].[PersonId], [p].[DepartmentId], [p].[Firstname],<\/code> \n<code class=\"fm-code-in-text1\">        [p].[LocationId], [p].[Surname], [d].[Departmentid], [d].[Name],<\/code>\n<code class=\"fm-code-in-text1\">        [l].[LocationId], [l].[City], [l].[State]<\/code>\n<code class=\"fm-code-in-text1\">      FROM [People] AS [p]<\/code>\n<code class=\"fm-code-in-text1\">      INNER JOIN [Departments] AS [d] ON [p].[DepartmentId] = [d].[Departmentid]<\/code>\n<code class=\"fm-code-in-text1\">      INNER JOIN [Locations] AS [l] ON [p].[LocationId] = [l].[LocationId]<\/code>\n<code class=\"fm-code-in-text1\">...<\/code><\/pre>\n<p class=\"body\">Each time the component is rendered, Entity Framework Core sends two identical requests to the database, even when the Increment button is clicked where no data operations are performed.<\/p>\n<p class=\"body\">This issue can arise whenever Entity Framework Core is used and is exacerbated by Blazor. Although it is common practice to assign database queries to <code class=\"fm-code-in-text\">IEnumerable&lt;T&gt;<\/code> properties, doing so masks an important aspect of Entity Framework Core, which is that its LINQ expressions are expressions of queries and not results, and each time the property is read, a new query is sent to the database. The value of the <code class=\"fm-code-in-text\">People<\/code> property is read twice by the <code class=\"fm-code-in-text\">List<\/code> component: once by the <code class=\"fm-code-in-text\">Count<\/code> property to determine whether the data has loaded and once by the <code class=\"fm-code-in-text\">@foreach<\/code> expression to generate the rows for the HTML table. When the user clicks the Increment button, Blazor renders the <code class=\"fm-code-in-text\">List<\/code> component again to figure out what has changed, which causes the <code class=\"fm-code-in-text\">People<\/code> property to be read twice more, producing two additional database queries.<\/p>\n<p class=\"body\">Blazor and Entity Framework Core are both working the way they should. Blazor must rerender the component\u2019s output to figure out what HTML changes need to be sent to the browser. It has no way of knowing what effect clicking the button has until after it has rendered the elements and evaluated all the Razor expressions. Entity Framework Core is executing its query each time the property is read, ensuring that the application always has fresh data.<\/p>\n<p class=\"body\">This combination of features presents two issues. The first is that needless queries are sent to the database, which can increase the capacity required by an application (although not always because database servers are adept at handling queries).<\/p>\n<p class=\"body\">The second issue is that changes to the database will be reflected in the content presented to the user after they make an unrelated interaction. If another user adds a <code class=\"fm-code-in-text\">Person<\/code> object to the database, for example, it will appear in the table the next time the user clicks the Increment button. Users expect applications to reflect only their actions, and unexpected changes are confusing and distracting.<\/p>\n<p class=\"fm-head2\">Managing queries in a component<\/p>\n<p class=\"body\">The interaction between Blazor and Entity Framework Core won\u2019t be a problem for all projects, but if it is, then the best approach is to query the database once and requery only for operations where the user might expect an update to occur. Some applications may need to present the user with an explicit option to reload the data, especially for applications where updates are likely to occur that the user will want to see, as shown in listing 36.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.18 Controlling queries in the List.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\"\n@page \"\/forms\/list\"\n@layout EmptyLayout\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;People&lt;\/h5&gt;\n\n&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;\n            &lt;th&gt;Name&lt;\/th&gt;\n            &lt;th&gt;Dept&lt;\/th&gt;\n            &lt;th&gt;Location&lt;\/th&gt;\n            &lt;th&gt;&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @if (People.Count() == 0) {\n            &lt;tr&gt;\n                &lt;th colspan=\"5\" class=\"p-4 text-center\"&gt;\n                    Loading Data...\n                &lt;\/th&gt;\n            &lt;\/tr&gt;\n        } else {\n            @foreach (Person p in People) {\n                &lt;tr&gt;\n                    &lt;td&gt;@p.PersonId&lt;\/td&gt;\n                    &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n                    &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n                    &lt;td&gt;@p.Location?.City&lt;\/td&gt;\n                    &lt;td&gt;\n                        &lt;NavLink class=\"btn btn-sm btn-warning\"\n                         href=\"@GetEditUrl(p.PersonId)\"&gt;\n                            Edit\n                        &lt;\/NavLink&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n<b class=\"fm-bold\">&lt;button class=\"btn btn-danger\" @onclick=\"UpdateData\"&gt;Update&lt;\/button&gt;<\/b>\n\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; Counter++)\"&gt;\n    Increment\n&lt;\/button&gt;\n&lt;span class=\"h5\"&gt;Counter: @Counter&lt;\/span&gt;\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt; People { get; set; }\n        = Enumerable.Empty&lt;Person&gt;();\n                \n    <b class=\"fm-bold\">protected async override Task OnInitializedAsync() {<\/b>\n        <b class=\"fm-bold\">await UpdateData();<\/b>\n    <b class=\"fm-bold\">}<\/b>\n        \n    <b class=\"fm-bold\">private async Task UpdateData() {<\/b>\n        <b class=\"fm-bold\">if (Context != null) {<\/b>\n            <b class=\"fm-bold\">People = await Context.People.Include(p =&gt; p.Department)<\/b>\n                <b class=\"fm-bold\">.Include(p =&gt; p.Location).ToListAsync&lt;Person&gt;();<\/b>\n        <b class=\"fm-bold\">} else {<\/b>\n            <b class=\"fm-bold\">People = Enumerable.Empty&lt;Person&gt;();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">}<\/b>\n        \n    string GetEditUrl(long id) =&gt; $\"\/forms\/edit\/{id}\";\n        \n    public int Counter { get; set; } = 0;\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UpdateData<\/code> method performs the same query but applies the <code class=\"fm-code-in-text\">ToListAsync<\/code> method, which forces evaluation of the Entity Framework Core query. The results are assigned to the <code class=\"fm-code-in-text\">People<\/code> property and can be read repeatedly without triggering additional queries. To give the user control over the data, I added a button that invokes the <code class=\"fm-code-in-text\">UpdateData<\/code> method when it is clicked. Restart ASP.NET Core, request http:\/\/localhost:5000\/forms, and click the Increment button. Monitor the output from the ASP.NET Core server, and you will see that there is a query made only when the component is initialized. To explicitly trigger a query, click the Update button.<\/p>\n<p class=\"body\">Some operations may require a new query, which is easy to perform. To demonstrate, listing 36.19 adds a sort operation to the <code class=\"fm-code-in-text\">List<\/code> component, which is implemented both with and without a new query.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.19 Adding operations to the List.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\"\n@page \"\/forms\/list\"\n@layout EmptyLayout\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;People&lt;\/h5&gt;\n\n&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;\n            &lt;th&gt;Name&lt;\/th&gt;\n            &lt;th&gt;Dept&lt;\/th&gt;\n            &lt;th&gt;Location&lt;\/th&gt;\n            &lt;th&gt;&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @if (People.Count() == 0) {\n            &lt;tr&gt;\n                &lt;th colspan=\"5\" class=\"p-4 text-center\"&gt;\n                    Loading Data...\n                &lt;\/th&gt;\n            &lt;\/tr&gt;\n        } else {\n            @foreach (Person p in People) {\n                &lt;tr&gt;\n                    &lt;td&gt;@p.PersonId&lt;\/td&gt;\n                    &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n                    &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n                    &lt;td&gt;@p.Location?.City&lt;\/td&gt;\n                    &lt;td&gt;\n                        &lt;NavLink class=\"btn btn-sm btn-warning\"\n                         href=\"@GetEditUrl(p.PersonId)\"&gt;\n                            Edit\n                        &lt;\/NavLink&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n<b class=\"fm-bold\">&lt;button class=\"btn btn-danger my-2\" @onclick=\"@(() =&gt; UpdateData())\"&gt;<\/b>\n    <b class=\"fm-bold\">Update<\/b>\n<b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n<b class=\"fm-bold\">&lt;button class=\"btn btn-info my-2\" @onclick=\"SortWithQuery\"&gt;<\/b>\n    <b class=\"fm-bold\">Sort (With Query)<\/b>\n<b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n<b class=\"fm-bold\">&lt;button class=\"btn btn-info my-2\" @onclick=\"SortWithoutQuery\"&gt;<\/b>\n    <b class=\"fm-bold\">Sort (No Query)<\/b>\n<b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n\n&lt;button class=\"btn btn-primary\" @onclick=\"@(() =&gt; Counter++)\"&gt;\n    Increment\n&lt;\/button&gt;\n&lt;span class=\"h5\"&gt;Counter: @Counter&lt;\/span&gt;\n\n@code {\n\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Person&gt; People { get; set; }\n        = Enumerable.Empty&lt;Person&gt;();\n                \n    protected async override Task OnInitializedAsync() {\n        await UpdateData();\n    }\n        \n    private IQueryable&lt;Person&gt; Query =&gt;\n        Context!.People.Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location);\n                        \n    <b class=\"fm-bold\">private async Task UpdateData(IQueryable&lt;Person&gt;? query = null) =&gt;<\/b>\n        <b class=\"fm-bold\">People = await (query ?? Query).ToListAsync&lt;Person&gt;();<\/b>\n                \n    <b class=\"fm-bold\">public async Task SortWithQuery() {<\/b>\n        <b class=\"fm-bold\">await UpdateData(Query.OrderBy(p =&gt; p.Surname));<\/b>\n    <b class=\"fm-bold\">}<\/b>\n        \n    <b class=\"fm-bold\">public void SortWithoutQuery() {<\/b>\n        <b class=\"fm-bold\">People = People.OrderBy(p =&gt; p.Firstname).ToList&lt;Person&gt;();<\/b>\n    <b class=\"fm-bold\">}<\/b>\n        \n    string GetEditUrl(long id) =&gt; $\"\/forms\/edit\/{id}\";\n        \n    public int Counter { get; set; } = 0;\n}<\/pre>\n<p class=\"body\">Entity Framework Core queries are expressed as <code class=\"fm-code-in-text\">IQueryable&lt;T&gt;<\/code> objects, allowing the query to be composed with additional LINQ methods before it is dispatched to the database server. The new operations in the example both use the LINQ <code class=\"fm-code-in-text\">OrderBy<\/code> method, but one applies this to the <code class=\"fm-code-in-text\">IQueryable&lt;T&gt;<\/code>, which is then evaluated to send the query with the <code class=\"fm-code-in-text\">ToListAsync<\/code> method. The other operation applies the <code class=\"fm-code-in-text\">OrderBy<\/code> method to the existing result data, sorting it without sending a new query. To see both operations, restart ASP.NET Core, request http:\/\/localhost:5000\/forms, and click the Sort buttons, as shown in figure 36.10. When the Sort (With Query) button is clicked, you will see a log message indicating that a query has been sent to the database.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre402\" src=\"\/images\/proaspnetcore7\/000408.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.10 Managing component queries<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Avoiding the overlapping query pitfall<\/p>\n<p class=\"fm-sidebar-text\">You may encounter an exception telling you that \u201ca second operation started on this context before a previous operation completed.\u201d This happens when a child component uses the <code class=\"fm-code-in-text1\">OnParametersSetAsync<\/code> method to perform an asynchronous Entity Framework Core query and a change in the parent\u2019s data triggers a second call to <code class=\"fm-code-in-text1\">OnParametersSetAsync<\/code> before the query is complete. The second method call starts a duplicate query that causes the exception. This problem can be resolved by performing the Entity Framework Core query synchronously. You can see an example in listing 36.23, where I perform queries synchronously because the parent component will trigger an update when it receives its data.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-664\">36.4 Performing CRUD operations<\/h2>\n<p class=\"body\">To show how the features described in previous sections fit together, I am going to create a simple application that allows the user to perform create, read, update, and delete (CRUD) operations on <code class=\"fm-code-in-text\">Person<\/code> objects<a id=\"calibre_link-2851\"><\/a>.<a id=\"calibre_link-827\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-665\">36.4.1 Creating the list component<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">List<\/code> component contains the basic functionality I require. Listing 36.20 removes some of the features from earlier sections that are no longer required and adds buttons that allow the user to navigate to other functions.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.20 Preparing the component in the List.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\"\n@page \"\/forms\/list\"\n@layout EmptyLayout\n<b class=\"fm-bold\">@inherits OwningComponentBase&lt;DataContext&gt;<\/b>\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;People&lt;\/h5&gt;\n\n&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Dept&lt;\/th&gt;&lt;th&gt;Location&lt;\/th&gt;&lt;th&gt;&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @if (People.Count() == 0) {\n            &lt;tr&gt;\n                &lt;th colspan=\"5\" class=\"p-4 text-center\"&gt;\n                    Loading Data...\n                &lt;\/th&gt;\n            &lt;\/tr&gt;\n        } else {\n            @foreach (Person p in People) {\n                &lt;tr&gt;\n                    &lt;td&gt;@p.PersonId&lt;\/td&gt;\n                    &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n                    &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n                    &lt;td&gt;@p.Location?.City&lt;\/td&gt;\n                    <b class=\"fm-bold\">&lt;td class=\"text-center\"&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;NavLink class=\"btn btn-sm btn-info\"<\/b> \n                               <b class=\"fm-bold\">href=\"@GetDetailsUrl(p.PersonId)\"&gt;<\/b>\n                            <b class=\"fm-bold\">Details<\/b>\n                        <b class=\"fm-bold\">&lt;\/NavLink&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;NavLink class=\"btn btn-sm btn-warning\"<\/b> \n                               <b class=\"fm-bold\">href=\"@GetEditUrl(p.PersonId)\"&gt;<\/b>\n                            <b class=\"fm-bold\">Edit<\/b>\n                        <b class=\"fm-bold\">&lt;\/NavLink&gt;<\/b>\n                        <b class=\"fm-bold\">&lt;button class=\"btn btn-sm btn-danger\"<\/b> \n                                <b class=\"fm-bold\">@onclick=\"@(() =&gt; HandleDelete(p))\"&gt;<\/b>\n                            <b class=\"fm-bold\">Delete<\/b>\n                        <b class=\"fm-bold\">&lt;\/button&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;\/td&gt;<\/b>\n                &lt;\/tr&gt;\n            }\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n<b class=\"fm-bold\">&lt;NavLink class=\"btn btn-primary\" href=\"\/forms\/create\"&gt;Create&lt;\/NavLink&gt;<\/b>\n\n@code {\n\n    <b class=\"fm-bold\">\/\/[Inject]<\/b>\n    <b class=\"fm-bold\">public DataContext? Context =&gt; Service;<\/b>\n        \n    public IEnumerable&lt;Person&gt; People { get; set; } \n        = Enumerable.Empty&lt;Person&gt;();\n                \n    protected async override Task OnInitializedAsync() {\n        await UpdateData();\n    }\n        \n    private IQueryable&lt;Person&gt; Query =&gt; \n        Context!.People.Include(p =&gt; p.Department)\n            .Include(p =&gt; p.Location);\n                        \n    private async Task UpdateData(IQueryable&lt;Person&gt;? query = null) =&gt; \n        People = await (query ?? Query).ToListAsync&lt;Person&gt;();\n                \n    public async Task SortWithQuery() {\n        await UpdateData(Query.OrderBy(p =&gt; p.Surname));\n    }\n        \n    public void SortWithoutQuery() {\n        People = People.OrderBy(p =&gt; p.Firstname).ToList&lt;Person&gt;();\n    }\n        \n    string GetEditUrl(long id) =&gt; $\"\/forms\/edit\/{id}\";\n        \n    <b class=\"fm-bold\">string GetDetailsUrl(long id) =&gt; $\"\/forms\/details\/{id}\";<\/b>\n        \n    <b class=\"fm-bold\">public async Task HandleDelete(Person p) {<\/b>\n        <b class=\"fm-bold\">if (Context != null) {<\/b>\n            <b class=\"fm-bold\">Context.Remove(p);<\/b>\n            <b class=\"fm-bold\">await Context.SaveChangesAsync();<\/b>\n            <b class=\"fm-bold\">await UpdateData();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">The operations for creating, viewing, and editing objects navigate to other URLs, but the delete operations are performed by the <code class=\"fm-code-in-text\">List<\/code> component, taking care to reload the data after the changes have been saved to reflect the change to the user.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-666\">36.4.2 Creating the details component<\/h3>\n<p class=\"body\">The details component displays a read-only view of the data, which doesn\u2019t require the Blazor form features or present any issues with Entity Framework Core. Add a Blazor Component named <code class=\"fm-code-in-text\">Details.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor\/Forms<\/code> folder with the content shown in listing 36.21.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.21 The contents of the Details.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/details\/{id:long}\"\n@layout EmptyLayout\n@inherits OwningComponentBase&lt;DataContext&gt;\n\n&lt;h4 class=\"bg-info text-center text-white p-2\"&gt;Details&lt;\/h4&gt;\n\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;ID&lt;\/label&gt;\n    &lt;input class=\"form-control\" value=\"@PersonData.PersonId\" disabled \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;Firstname&lt;\/label&gt;\n    &lt;input class=\"form-control\" value=\"@PersonData.Firstname\" disabled \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;Surname&lt;\/label&gt;\n    &lt;input class=\"form-control\" value=\"@PersonData.Surname\" disabled \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;Department&lt;\/label&gt;\n    &lt;input class=\"form-control\" value=\"@PersonData.Department?.Name\" \n        disabled \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;Location&lt;\/label&gt;\n    &lt;input class=\"form-control\"\n           value=\"@($\"{PersonData.Location?.City}, \" \n                + PersonData.Location?.State)\"\n           disabled \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"text-center p-2\"&gt;\n    &lt;NavLink class=\"btn btn-info\" href=\"@EditUrl\"&gt;Edit&lt;\/NavLink&gt;\n    &lt;NavLink class=\"btn btn-secondary\" href=\"\/forms\"&gt;Back&lt;\/NavLink&gt;\n&lt;\/div&gt;\n\n@code {\n\n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    DataContext Context =&gt; Service;\n \n    [Parameter]\n    public long Id { get; set; }\n \n    public Person PersonData { get; set; } = new();\n        \n    protected async override Task OnParametersSetAsync() {\n        if (Context != null) {\n            PersonData = await Context.People\n                .Include(p =&gt; p.Department)\n                .Include(p =&gt; p.Location)\n                .FirstOrDefaultAsync(p =&gt; p.PersonId == Id)\n                    ?? new();\n        }\n    }\n        \n    public string EditUrl =&gt; $\"\/forms\/edit\/{Id}\";\n}<\/pre>\n<p class=\"body\">All the <code class=\"fm-code-in-text\">input<\/code> elements displayed by this component are disabled, which means there is no need to handle events or process user input.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-667\">36.4.3 Creating the editor component<\/h3>\n<p class=\"body\">The remaining features will be handled by the <code class=\"fm-code-in-text\">Editor<\/code> component. Listing 36.22 removes the features from earlier examples that are no longer required and adds support for creating and editing objects, including persisting the data.<a id=\"calibre_link-826\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.22 Adding features in the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n<b class=\"fm-bold\">@page \"\/forms\/create\"<\/b>\n@layout EmptyLayout\n@inherits OwningComponentBase&lt;DataContext&gt;\n\n&lt;link href=\"\/blazorValidation.css\" rel=\"stylesheet\" \/&gt;\n\n<b class=\"fm-bold\">&lt;h4 class=\"bg-@Theme text-center text-white p-2\"&gt;@Mode&lt;\/h4&gt;<\/b>\n\n&lt;EditForm Model=\"PersonData\" OnValidSubmit=\"HandleValidSubmit\" &gt;\n    &lt;DataAnnotationsValidator \/&gt;\n    <b class=\"fm-bold\">@if (Mode == \"Edit\") {<\/b>\n        <b class=\"fm-bold\">&lt;div class=\"form-group\"&gt;<\/b>\n            <b class=\"fm-bold\">&lt;label&gt;ID&lt;\/label&gt;<\/b>\n            <b class=\"fm-bold\">&lt;InputNumber class=\"form-control\"<\/b>\n                <b class=\"fm-bold\">@bind-Value=\"PersonData.PersonId\" readonly \/&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n    <b class=\"fm-bold\">}<\/b>\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Firstname&lt;\/label&gt;\n        &lt;ValidationMessage For=\"@(() =&gt; PersonData.Firstname)\" \/&gt;\n        &lt;InputText class=\"form-control\" \n            @bind-Value=\"PersonData.Firstname\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Surname&lt;\/label&gt;\n        &lt;ValidationMessage For=\"@(() =&gt; PersonData.Surname)\" \/&gt;\n        &lt;InputText class=\"form-control\" \n            @bind-Value=\"PersonData.Surname\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        <b class=\"fm-bold\">&lt;label&gt;Deptartment&lt;\/label&gt;<\/b>\n        &lt;ValidationMessage For=\"@(() =&gt; PersonData.DepartmentId)\" \/&gt;\n        &lt;CustomSelect TValue=\"long\" Values=\"Departments\" \n                        Parser=\"@((string str) =&gt; long.Parse(str))\"\n                        @bind-Value=\"PersonData.DepartmentId\"&gt;\n            &lt;option selected disabled value=\"0\"&gt;\n                Choose a Department\n             &lt;\/option&gt;\n        &lt;\/CustomSelect&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        <b class=\"fm-bold\">&lt;label&gt;Location&lt;\/label&gt;<\/b>\n        &lt;ValidationMessage For=\"@(() =&gt; PersonData.LocationId)\" \/&gt;\n        &lt;CustomSelect TValue=\"long\" Values=\"Locations\" \n                        Parser=\"@((string str) =&gt; long.Parse(str))\"\n                        @bind-Value=\"PersonData.LocationId\"&gt;\n            &lt;option selected disabled value=\"0\"&gt;Choose a Location&lt;\/option&gt;\n        &lt;\/CustomSelect&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"text-center\"&gt;\n        <b class=\"fm-bold\">&lt;button type=\"submit\" class=\"btn btn-@Theme mt-2\"&gt;Save&lt;\/button&gt;<\/b>\n        &lt;NavLink class=\"btn btn-secondary mt-2\" href=\"\/forms\"&gt;\n            Back\n        &lt;\/NavLink&gt;\n    &lt;\/div&gt;\n&lt;\/EditForm&gt;\n\n@code {\n\n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    \/\/[Inject]\n    DataContext? Context =&gt; Service;\n \n    [Parameter]\n    public long Id { get; set; }\n \n    public Person PersonData { get; set; } = new();\n        \n    public IDictionary&lt;string, long&gt; Departments { get; set; } \n        = new Dictionary&lt;string, long&gt;();\n                \n    public IDictionary&lt;string, long&gt; Locations { get; set; } \n        = new Dictionary&lt;string, long&gt;();\n                \n    protected async override Task OnParametersSetAsync() {\n        if (Context != null) {\n            <b class=\"fm-bold\">if (Mode == \"Edit\") {<\/b>\n                <b class=\"fm-bold\">PersonData = await Context.People.FindAsync(Id)<\/b> \n                    <b class=\"fm-bold\">?? new Person();<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            Departments = await Context.Departments\n                .ToDictionaryAsync(d =&gt; d.Name, d =&gt; d.Departmentid);\n            Locations = await Context.Locations\n                .ToDictionaryAsync(l =&gt; $\"{l.City}, {l.State}\", \n                    l =&gt; l.LocationId);\n        }\n    }\n        \n    <b class=\"fm-bold\">public string Theme =&gt; Id == 0 ? \"primary\" : \"warning\";<\/b>\n    <b class=\"fm-bold\">public string Mode =&gt; Id == 0 ? \"Create\" : \"Edit\";<\/b>\n        \n    <b class=\"fm-bold\">public async Task HandleValidSubmit()  {<\/b>\n        <b class=\"fm-bold\">if (Context != null) {<\/b>\n            <b class=\"fm-bold\">if (Mode == \"Create\") {<\/b>\n                <b class=\"fm-bold\">Context.Add(PersonData);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">await Context.SaveChangesAsync();<\/b>\n            <b class=\"fm-bold\">NavManager?.NavigateTo(\"\/forms\");<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">}<\/b>\n}<\/pre>\n<p class=\"body\">I added support for a new URL and used Bootstrap CSS themes to differentiate between creating a new object and editing an existing one. I removed the validation summary so that only property-level validation messages are displayed and added support for storing the data through Entity Framework Core. Unlike form applications created using controllers or Razor Pages, I don\u2019t have to deal with model binding because Blazor lets me work directly with the object that Entity Framework Core produces from the initial database query. Restart ASP.NET Core and request http:\/\/localhost:5000\/forms. You will see the list of <code class=\"fm-code-in-text\">Person<\/code> objects shown in figure 36.11, and clicking the Create, Details, Edit, and Delete buttons will allow you to work with the data in the database.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> Open a command prompt and run <code class=\"fm-code-in-text1\">dotnet ef database drop --force<\/code> in the <code class=\"fm-code-in-text1\">Advanced<\/code> project folder if you need to reset the database to undo the changes you have made. The database will be seeded again when you restart ASP.NET Core, and you will see the data shown in the figure.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre403\" src=\"\/images\/proaspnetcore7\/000409.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.11 Using Blazor to work with data<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-668\">36.5 Extending the Blazor form features<\/h2>\n<p class=\"body\">The Blazor form features are effective but have the rough edges that are always found in new technology. I expect future releases to round out the feature set, but, in the meantime, Blazor makes it easy to enhance the way that forms work. The <code class=\"fm-code-in-text\">EditForm<\/code> component defines a cascading <code class=\"fm-code-in-text\">EditContext<\/code> object that provides access to form validation and makes it easy to create custom form components through the events, properties, and methods described in table 36.8.<a id=\"calibre_link-2852\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 36.8 The EditContext features<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2853\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnFieldChanged<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This event is triggered when any of the form fields are modified.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnValidationRequested<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This event is triggered when validation is required and can be used to create custom validation processes.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">OnValidationStateChanged<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This event is triggered when the validation state of the overall form changes.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Model<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the value passed to the <code class=\"fm-code-in-text1\">EditForm<\/code> component\u2019s <code class=\"fm-code-in-text1\">Model<\/code> property.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Field(name)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to get a <code class=\"fm-code-in-text1\">FieldIdentifier<\/code> object that describes a single field.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsModified()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns <code class=\"fm-code-in-text1\">true<\/code> if any of the form fields have been modified.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsModified(field)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns <code class=\"fm-code-in-text1\">true<\/code> if the field specified by the <code class=\"fm-code-in-text1\">FieldIdentifier<\/code> argument has been modified.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetValidationMessages()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns a sequence containing the validation error messages for the entire form.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetValidationMessages(field)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns a sequence containing the validation error messages for a single field, using a <code class=\"fm-code-in-text1\">FieldIdentifer<\/code> object obtained from the <code class=\"fm-code-in-text1\">Field<\/code> method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MarkAsUnmodified()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method marks the form as unmodified.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">MarkAsUnmodified(field)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method marks a specific field as unmodified, using a <code class=\"fm-code-in-text1\">FieldIdentifer<\/code> object obtained from the <code class=\"fm-code-in-text1\">Field<\/code> method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">NotifyValidationStateChanged()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to indicate a change in validation status.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">NotifyFieldChanged(field)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method is used to indicate when a field has changed, using a <code class=\"fm-code-in-text1\">FieldIdentifer<\/code> object obtained from the <code class=\"fm-code-in-text1\">Field<\/code> method.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Validate()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method performs validation on the form, returning <code class=\"fm-code-in-text1\">true<\/code> if all the form fields pass validation and <code class=\"fm-code-in-text1\">false<\/code> otherwise.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-669\">36.5.1 Creating a custom validation constraint<\/h3>\n<p class=\"body\">You can create components that apply custom validation constraints if the built-in validation attributes are not sufficient. This type of component doesn\u2019t render its own content, and it is more easily defined as a class. Add a class file named <code class=\"fm-code-in-text\">DeptStateValidator.cs<\/code> to the <code class=\"fm-code-in-text\">Blazor\/Forms<\/code> folder and use it to define the component class shown in listing 36.23.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.23 The contents of the DeptStateValidator.cs file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">using Advanced.Models;\nusing Microsoft.AspNetCore.Components;\nusing Microsoft.AspNetCore.Components.Forms;\n\nnamespace Advanced.Blazor.Forms {\n\n    public class DeptStateValidator : OwningComponentBase&lt;DataContext&gt; {\n        \n        public DataContext Context =&gt; Service;\n                \n        [Parameter]\n        public long DepartmentId { get; set; }\n                \n        [Parameter]\n        public string? State { get; set; }\n                \n        [CascadingParameter]\n        public EditContext? CurrentEditContext { get; set; }\n                \n        private string? DeptName { get; set; }\n        private IDictionary&lt;long, string&gt;? LocationStates { get; set; }\n                \n        protected override void OnInitialized() {\n            if (CurrentEditContext != null) {\n                ValidationMessageStore store =\n                    new ValidationMessageStore(CurrentEditContext);\n                CurrentEditContext.OnFieldChanged += (sender, args) =&gt; {\n                    string name = args.FieldIdentifier.FieldName;\n                    if (name == \"DepartmentId\" || name == \"LocationId\") {\n                        Validate(CurrentEditContext.Model as Person, \n                            store);\n                    }\n                };\n            }\n        }\n                \n        protected override void OnParametersSet() {\n            DeptName = Context.Departments.Find(DepartmentId)?.Name;\n            LocationStates = Context.Locations\n                .ToDictionary(l =&gt; l.LocationId, l =&gt; l.State);\n        }\n                \n        private void Validate(Person? model, \n                ValidationMessageStore store) {\n            if (model?.DepartmentId == DepartmentId\n                    &amp;&amp; LocationStates != null \n                    &amp;&amp; CurrentEditContext != null\n                    &amp;&amp; (!LocationStates.ContainsKey(model.LocationId)\n                        || LocationStates[model.LocationId] != State)) {\n                store.Add(CurrentEditContext.Field(\"LocationId\"),\n                    $\"{DeptName} staff must be in: {State}\");\n            } else {\n                store.Clear();\n            }\n            CurrentEditContext?.NotifyValidationStateChanged();\n        }\n    }\n}<\/pre>\n<p class=\"body\">This component enforces a restriction on the state in which departments can be defined so that, for example, locations in California are the valid options only when the <code class=\"fm-code-in-text\">Development<\/code> department has been chosen, and any other locations will produce a validation error.<\/p>\n<p class=\"body\">The component has its own scoped <code class=\"fm-code-in-text\">DataContext<\/code> object, which it receives by using <code class=\"fm-code-in-text\">OwningComponentBase&lt;T&gt;<\/code> as its base class. The parent component provides values for the <code class=\"fm-code-in-text\">DepartmentId<\/code> and <code class=\"fm-code-in-text\">State<\/code> properties, which are used to enforce the validation rule. The cascading <code class=\"fm-code-in-text\">EditContext<\/code> property is received from the <code class=\"fm-code-in-text\">EditForm<\/code> component and provides access to the features described in table 36.8.<\/p>\n<p class=\"body\">When the component is initialized, a new <code class=\"fm-code-in-text\">ValidationMessageStore<\/code> is created. This object is used to register validation error messages and accepts the <code class=\"fm-code-in-text\">EditContext<\/code> object as its constructor argument, like this:<\/p>\n<pre class=\"programlisting\">...\nValidationMessageStore store =\n    new ValidationMessageStore(CurrentEditContext);\n...<\/pre>\n<p class=\"body\">Blazor takes care of processing the messages added to the store, and the custom validation component only needs to decide which messages are required, which is handled by the <code class=\"fm-code-in-text\">Validate<\/code> method. This method checks the <code class=\"fm-code-in-text\">DepartmentId<\/code> and <code class=\"fm-code-in-text\">LocationId<\/code> properties to make sure that the combination is allowed. If there is an issue, then a new validation message is added to the store, like this:<\/p>\n<pre class=\"programlisting\">...\nstore.<b class=\"fm-bold\">Add<\/b>(CurrentEditContext.Field(\"LocationId\"), \n    $\"{DeptName} staff must be in: {State}\");\n...<\/pre>\n<p class=\"body\">The arguments to the <code class=\"fm-code-in-text\">Add<\/code> method are a <code class=\"fm-code-in-text\">FieldIdentifier<\/code> that identifies the field the error relates to and the validation message. If there are no validation errors, then the message store\u2019s <code class=\"fm-code-in-text\">Clear<\/code> method is called, which will ensure that any stale messages that have been previously generated by the component are no longer displayed.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Validation<\/code> method is called by the handler for the <code class=\"fm-code-in-text\">OnFieldChanged<\/code> event, which allows the component to respond whenever the user makes a change.<\/p>\n<pre class=\"programlisting\">...\nCurrentEditContext.<b class=\"fm-bold\">OnFieldChanged<\/b> += (sender, args) =&gt; {\n    string name = args.FieldIdentifier.FieldName;\n    if (name == \"DepartmentId\" || name == \"LocationId\") {\n        Validate(CurrentEditContext.Model as Person, store);\n     }\n};\n...<\/pre>\n<p class=\"body\">The handler receives a <code class=\"fm-code-in-text\">FieldChangeEventArgs<\/code> object, which defines a <code class=\"fm-code-in-text\">FieldIdentifer<\/code> property that indicates which field has been modified. Listing 36.24 applies the new validation to the <code class=\"fm-code-in-text\">Editor<\/code> component.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.24 Applying validation in the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n@page \"\/forms\/create\"\n@layout EmptyLayout\n@inherits OwningComponentBase&lt;DataContext&gt;\n\n&lt;link href=\"\/blazorValidation.css\" rel=\"stylesheet\" \/&gt;\n\n&lt;h4 class=\"bg-@Theme text-center text-white p-2\"&gt;@Mode&lt;\/h4&gt;\n\n&lt;EditForm Model=\"PersonData\" OnValidSubmit=\"HandleValidSubmit\" &gt;\n    &lt;DataAnnotationsValidator \/&gt;\n    <b class=\"fm-bold\">&lt;DeptStateValidator DepartmentId=\"2\" State=\"CA\" \/&gt;<\/b>\n    @if (Mode == \"Edit\") {\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;ID&lt;\/label&gt;\n            &lt;InputNumber class=\"form-control\"\n                @bind-Value=\"PersonData.PersonId\" readonly \/&gt;\n        &lt;\/div&gt;\n    }\n        \n    &lt;!-- ...<i class=\"fm-italics\">elements omitted for brevity<\/i>... --&gt;\n        \n&lt;\/EditForm&gt;\n\n@code {\n\n    \/\/ ...<i class=\"fm-italics\">statements omitted for brevity<\/i>...\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">DepartmentId<\/code> and <code class=\"fm-code-in-text\">State<\/code> attributes specify the restriction that only locations in California can be selected for the Development department. Restart ASP.NET Core and request http:\/\/localhost:5000\/forms\/edit\/4. Choose Development for the Department field, and you will see a validation error because the location for this <code class=\"fm-code-in-text\">Person<\/code> is New York. This error will remain visible until you select a location in California or change the department, as shown in figure 36.12.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre404\" src=\"\/images\/proaspnetcore7\/000410.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.12 Creating a custom validation component<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-670\">36.5.2 Creating a valid-only submit button component<\/h3>\n<p class=\"body\">To finish this chapter, I am going to create a component that will render a submit button for the form that is enabled only when the data is valid. Add a Razor Component named <code class=\"fm-code-in-text\">ValidButton.razor<\/code> to the <code class=\"fm-code-in-text\">Blazor\/Forms<\/code> folder with the contents shown in listing 36.25.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.25 The contents of the ValidButton.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">&lt;button class=\"@ButtonClass\" @attributes=\"Attributes\" \n        disabled=\"@Disabled\"&gt;\n    @ChildContent\n&lt;\/button&gt;\n\n@code {\n\n    [Parameter]\n    public RenderFragment? ChildContent { get; set; }\n \n    [Parameter]\n    public string BtnTheme { get; set; } = \"primary\";\n \n    [Parameter]\n    public string DisabledClass { get; set; } \n        = \"btn-outline-dark disabled\";\n                \n    [Parameter(CaptureUnmatchedValues = true)]\n    public IDictionary&lt;string, object&gt;? Attributes { get; set; }\n        \n    [CascadingParameter]\n    public EditContext? CurrentEditContext { get; set; }\n \n    public bool Disabled { get; set; }\n \n    public string ButtonClass =&gt;\n        Disabled ? $\"btn btn-{BtnTheme} {DisabledClass} mt-2\"\n                : $\"btn btn-{BtnTheme} mt-2\";\n                                \n    protected override void OnInitialized() {\n        SetButtonState();\n        if (CurrentEditContext != null) {\n            CurrentEditContext.OnValidationStateChanged +=\n                (sender, args) =&gt; SetButtonState();\n            CurrentEditContext.Validate();\n        }\n    }\n        \n    public void SetButtonState() {\n        if (CurrentEditContext != null) {\n            Disabled = CurrentEditContext.GetValidationMessages().Any();\n        }\n    }\n}<\/pre>\n<p class=\"body\">This component responds to the <code class=\"fm-code-in-text\">OnValidationStateChanged<\/code> method, which is triggered when the validation state of the form changes. There is no <code class=\"fm-code-in-text\">EditContext<\/code> property that details the validation state, so the best way to see if there are any validation issues is to see whether there are any validation messages. If there are, there are validation issues. If there are no validation messages, the form is valid. To ensure the button state is displayed correctly, the <code class=\"fm-code-in-text\">Validation<\/code> method is called so that a validation check is performed as soon as the component is initialized.<\/p>\n<p class=\"body\">Listing 36.26 uses the new component to replace the conventional button in the <code class=\"fm-code-in-text\">Editor<\/code> component.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 36.26 Applying a component in the Editor.razor file in the Blazor\/Forms folder<\/p>\n<pre class=\"programlisting\">...\n&lt;div class=\"text-center\"&gt;\n    <b class=\"fm-bold\">&lt;ValidButton type=\"submit\" BtnTheme=\"@Theme\"&gt;Save&lt;\/ValidButton&gt;<\/b>\n    &lt;NavLink class=\"btn btn-secondary mt-2\" href=\"\/forms\"&gt;Back&lt;\/NavLink&gt;\n&lt;\/div&gt;\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/forms\/create; you will see the validation messages displayed for each form element, with the Save button disabled. The button will be enabled once each validation issue has been resolved, as shown in figure 36.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre405\" src=\"\/images\/proaspnetcore7\/000411.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 36.13 Creating a custom form button<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2854\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Blazor provides built-in components for common HTML form elements, including <code class=\"fm-code-in-text\">form<\/code>, <code class=\"fm-code-in-text\">input<\/code>, and <code class=\"fm-code-in-text\">textarea<\/code> elements.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Form events are presented through the <code class=\"fm-code-in-text\">EditForm<\/code> component.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Entity Framework Core context scopes must be carefully managed to avoid stale data. Scopes can be managed through the <code class=\"fm-code-in-text\">IDisposable<\/code> interface, or through automatic dependency injection.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Take care with data read from Entity Framework Core to avoid repeated queries.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-671\">\n<div class=\"calibre1\" id=\"calibre_link-2855\">\n<h1 class=\"tochead\" id=\"calibre_link-2856\"><a id=\"calibre_link-2857\"><\/a><a id=\"calibre_link-2858\"><\/a>37 Using Blazor WebAssembly<\/h1>\n<p class=\"co-summary-head\">This chapter covers<\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Using WebAssembly to create self-contained client-side applications<\/li>\n<li class=\"co-summary-bullet\">Creating WebAssembly components<\/li>\n<li class=\"co-summary-bullet\">Navigating between components in a WebAssembly application<\/li>\n<li class=\"co-summary-bullet\">Creating a forms application using WebAssembly components<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I demonstrate the use of Blazor WebAssembly, which is an implementation of Blazor written for WebAssembly.<\/p>\n<p class=\"body\">WebAssembly is a virtual machine running inside the browser. High-level languages are compiled into low-level language-neutral assembler format that can be executed at close to native performance. WebAssembly provides access to the APIs available to JavaScript applications, which means that WebAssembly applications can access the domain object model, use cascading style sheets, and initiate asynchronous HTTP requests.<\/p>\n<p class=\"body\">Blazor WebAssembly breaks the dependency on the server and executes the Blazor application entirely in the browser. The result is a true client-side application, with access to all the same features of Blazor Server but without the need for a persistent HTTP connection.<\/p>\n<p class=\"body\">It is early days for both WebAssembly and Blazor WebAssembly, and there are some serious restrictions. WebAssembly is a new technology and is supported only by the latest browser versions. You will not be able to use WebAssembly if your project needs to support legacy browsers&mdash;or even older versions of modern browsers. Blazor WebAssembly applications are restricted to the set of APIs the browser provides, which means that not all .NET features can be used in a WebAssembly application. This doesn\u2019t disadvantage Blazor when compared to client-side frameworks like Angular, but it does mean that features such as Entity Framework Core are not available because browsers restrict WebAssembly applications to making HTTP requests.<\/p>\n<p class=\"body\">Still, despite the limitations of Blazor WebAssembly, it is an exciting technology, and it offers the promise of being able to write true client-side applications using C# and ASP.NET Core, without the need for a JavaScript framework. Table 37.1 puts Blazor WebAssembly in context.<\/p>\n<p class=\"fm-table-caption\">Table 37.1 Putting Blazor WebAssembly in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2859\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What is it?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Blazor WebAssembly is an implementation of Blazor that runs in the browser using WebAssembly.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why is it useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Blazor WebAssembly allows client-side applications to be written in C# without server-side execution or the persistent HTTP connection required by Blazor Server.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How is it used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Blazor components are added to a project that is dedicated to Blazor WebAssembly.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Not all browsers support WebAssembly. A larger download is required to provide the browser with the code it requires, and not all ASP.NET Core features are available in Blazor WebAssembly components.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Blazor WebAssembly is the only combination of true client-side applications written using ASP.NET Core. Blazor Server can be used if server-side support is acceptable; otherwise, a JavaScript framework, such as Angular, React, or Vue.js, should be used.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-672\">37.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the Advanced project from chapter 36. To prepare for this chapter, add a class file named <code class=\"fm-code-in-text\">DataController.cs<\/code> to the <code class=\"fm-code-in-text\">Controllers<\/code> folder and use it to define the web service controller shown in listing 37.1.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.1 The contents of the DataController.cs file in the Controllers folder<\/p>\n<pre class=\"programlisting\">using Advanced.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace Advanced.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/people\")]\n    public class DataController : ControllerBase {\n        private DataContext context;\n\n        public DataController(DataContext ctx) {\n            context = ctx;\n        }\n                \n        [HttpGet]\n        public IEnumerable&lt;Person&gt; GetAll() {\n            IEnumerable&lt;Person&gt; people\n                = context.People\n                    .Include(p =&gt; p.Department)\n                    .Include(p =&gt; p.Location);\n            foreach (Person p in people) {\n                if (p.Department?.People != null) {\n                    p.Department.People = null;\n                }\n                if (p.Location?.People != null) {\n                    p.Location.People = null;\n                }\n            }\n            return people;\n        }\n                \n        [HttpGet(\"{id}\")]\n        public async Task&lt;Person&gt; GetDetails(long id) {\n            Person p = await context.People\n                .Include(p =&gt; p.Department)\n                .Include(p =&gt; p.Location)\n                .FirstAsync(p =&gt; p.PersonId == id);\n            if (p.Department?.People != null) {\n                p.Department.People = null;\n            }\n            if (p.Location?.People != null) {\n                p.Location.People = null;\n            }\n            return p;\n        }\n \n        [HttpPost]\n        public async Task Save([FromBody] Person p) {\n            await context.People.AddAsync(p);\n            await context.SaveChangesAsync();\n        }\n                \n        [HttpPut]\n        public async Task Update([FromBody] Person p) {\n            context.Update(p);\n            await context.SaveChangesAsync();\n        }\n                \n        [HttpDelete(\"{id}\")]\n        public async Task Delete(long id) {\n            context.People.Remove(new Person() { PersonId = id });\n            await context.SaveChangesAsync();\n        }\n                \n        [HttpGet(\"\/api\/locations\")]\n        public IAsyncEnumerable&lt;Location&gt; GetLocations() =&gt; \n            context.Locations.AsAsyncEnumerable();\n                        \n        [HttpGet(\"\/api\/departments\")]\n        public IAsyncEnumerable&lt;Department&gt; GetDepts() =&gt; \n            context.Departments.AsAsyncEnumerable();\n    }\n}<\/pre>\n<p class=\"body\">This controller provides actions that allow <code class=\"fm-code-in-text\">Person<\/code> objects to be created, read, updated, and deleted. I have also added actions that return the <code class=\"fm-code-in-text\">Location<\/code> and <code class=\"fm-code-in-text\">Department<\/code> objects. I usually create separate controllers for each type of data, but these actions are required only in support of the <code class=\"fm-code-in-text\">Person<\/code> features, so I have combined all the operations into a single controller.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-673\">37.1.1 Dropping the database and running the application<\/h3>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">Advanced.csproj<\/code> file, and run the command shown in listing 37.2 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.2 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 37.3.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.3 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/api\/people, which will produce a JSON representation of the <code class=\"fm-code-in-text\">Person<\/code> objects from the database, as shown in figure 37.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre406\" src=\"\/images\/proaspnetcore7\/000412.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 37.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-674\">37.2 Setting Up Blazor WebAssembly<\/h2>\n<p class=\"body\">Blazor WebAssembly requires a separate project so that Razor Components can be compiled ready to be executed by the browser. The compiled components can be delivered to the browser by a standard ASP.NET Core server, which can also provide data through web services. To make it easy for the Blazor WebAssembly components to consume the data provided by the ASP.NET Core server, a third project is required that contains those items that are shared between them.<\/p>\n<p class=\"body\">The process for creating the three projects is involved, partly because I am going to move some of the existing classes from the Advanced project into the data model project. Although it is possible to perform some of the steps using the Visual Studio wizards, I have set out the steps using the command-line tools to minimize errors.<a id=\"calibre_link-2860\"><\/a><a id=\"calibre_link-2861\"><\/a><a id=\"calibre_link-846\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> If you have problems following the steps, you can download all three projects from the GitHub repository for this book, at <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-675\">37.2.1 Creating the shared project<\/h3>\n<p class=\"body\">Make sure Visual Studio or Visual Studio Code is closed before you start. Open a new PowerShell command prompt and navigate to the <code class=\"fm-code-in-text\">Advanced<\/code> project folder, which is the one that contains the <code class=\"fm-code-in-text\">Advanced.csproj<\/code> file, and run the commands shown in listing 37.4.<a id=\"calibre_link-2862\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.4 Preparing the project for Blazor<\/p>\n<pre class=\"programlisting\">dotnet new classlib -o ..\/DataModel -f net7.0\nMove-Item -Path @(\"Models\/Person.cs\", \"Models\/Location.cs\",\n    \"Models\/Department.cs\") ..\/DataModel<\/pre>\n<p class=\"body\">These commands create a new project named <code class=\"fm-code-in-text\">DataModel<\/code> and move the data model classes to the new project.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-676\">37.2.2 Creating the Blazor WebAssembly project<\/h3>\n<p class=\"body\">I usually prefer to start with an empty project and add the packages and configuration files that the application requires. Use the PowerShell command prompt to run the commands shown in listing 37.5 from within the <code class=\"fm-code-in-text\">Advanced<\/code> project folder (the folder that contains the <code class=\"fm-code-in-text\">Advanced.csproj<\/code> file).<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.5 Creating the Blazor WebAssembly project<\/p>\n<pre class=\"programlisting\">dotnet new blazorwasm -o ..\/BlazorWebAssembly -f net7.0\ndotnet add ..\/BlazorWebAssembly reference ..\/DataModel<\/pre>\n<p class=\"body\">These commands create a Blazor WebAssembly project named <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> and add a reference to the <code class=\"fm-code-in-text\">DataModel<\/code> project, which makes the <code class=\"fm-code-in-text\">Person<\/code>, <code class=\"fm-code-in-text\">Department<\/code>, and <code class=\"fm-code-in-text\">Location<\/code> classes available.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-677\">37.2.3 Preparing the ASP.NET Core project<\/h3>\n<p class=\"body\">Use the PowerShell command prompt to run the commands shown in listing 37.6 in the <code class=\"fm-code-in-text\">Advanced<\/code> project folder.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.6 Preparing the advanced project<\/p>\n<pre class=\"programlisting\">dotnet add reference ..\/DataModel ..\/BlazorWebAssembly dotnet add package Microsoft.AspNetCore.Components.WebAssembly.Server\n    --version 7.0.0<\/pre>\n<p class=\"body\">These commands create references to the other projects so that the data model classes and the components in the Blazor WebAssembly project can be used.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-678\">37.2.4 Adding the solution references<\/h3>\n<p class=\"body\">Run the command shown in listing 37.7 in the <code class=\"fm-code-in-text\">Advanced<\/code> folder to add references to the new project to the solution file.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.7 Adding solution references<\/p>\n<pre class=\"programlisting\">dotnet sln add ..\/DataModel ..\/BlazorWebAssembly<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-679\">37.2.5 Opening the projects<\/h3>\n<p class=\"body\">Once you have set up all three projects, start Visual Studio or Visual Studio Code. If you are using Visual Studio, open the <code class=\"fm-code-in-text\">Advanced.sln<\/code> file in the <code class=\"fm-code-in-text\">Advanced<\/code> folder. All three projects are open for editing, as shown in figure 37.2. If you are using Visual Studio Code, open the folder that contains all three projects, as shown in figure 37.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre407\" src=\"\/images\/proaspnetcore7\/000413.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 37.2 Opening the three projects<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-680\">37.2.6 Completing the Blazor WebAssembly configuration<\/h3>\n<p class=\"body\">The next step is to configure the ASP.NET Core project so that it can deliver the contents of the Blazor WebAssembly project to clients. Add the statements shown in listing 37.8 to the <code class=\"fm-code-in-text\">Program.cs<\/code> file in the <code class=\"fm-code-in-text\">Advanced<\/code> folder.<a id=\"calibre_link-2863\"><\/a><a id=\"calibre_link-847\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> It is important to pay close attention to which files you are editing. Files with the same name exist in multiple projects, and if you don\u2019t follow the examples closely, you won\u2019t end up with a working application. Future versions of Blazor may be easier to work with, but for the moment, the details are important.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.8 Configuring the application in the Program.cs file in the Advanced project<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddServerSideBlazor();\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddSingleton&lt;Advanced.Services.ToggleService&gt;();\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\napp.MapControllers();\napp.MapControllerRoute(\"controllers\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\napp.MapBlazorHub();\napp.MapFallbackToPage(\"\/_Host\");\n\n<b class=\"fm-bold\">app.UseBlazorFrameworkFiles(\"\/webassembly\");<\/b>\n<b class=\"fm-bold\">app.MapFallbackToFile(\"\/webassembly\/{*path:nonfile}\",<\/b>\n     <b class=\"fm-bold\">\"\/webassembly\/index.xhtml\");<\/b>\n         \nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">These statements configure the ASP.NET Core request pipeline so that requests for <code class=\"fm-code-in-text\">\/webassembly<\/code> are handled by Blazor WebAssembly using the contents of the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> project.<\/p>\n<p class=\"fm-head2\">Setting the base URL<\/p>\n<p class=\"body\">The next step is to modify the HTML file that will be used to respond to requests for the <code class=\"fm-code-in-text\">\/webassembly<\/code> URL. Apply the change shown in listing 37.9 to the <code class=\"fm-code-in-text\">index.xhtml<\/code> file in the <code class=\"fm-code-in-text\">wwwroot<\/code> folder of the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> folder.<a id=\"calibre_link-2864\"><\/a><a id=\"calibre_link-2865\"><\/a><a id=\"calibre_link-839\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Make sure there are forward-slash (<code class=\"fm-code-in-text1\">\/<\/code>) characters before and after <code class=\"fm-code-in-text1\">webassembly<\/code> in the <code class=\"fm-code-in-text1\">href<\/code> attribute of the <code class=\"fm-code-in-text1\">base<\/code> element. If you omit either character, then Blazor WebAssembly will not work.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.9 Setting the URL in the index.xhtml file in the wwwroot folder of the BlazorWebAssembly project<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n\n&lt;head&gt;\n    &lt;meta charset=\"utf-8\" \/&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0,\n          maximum-scale=1.0, user-scalable=no\" \/&gt;\n    &lt;title&gt;BlazorWebAssembly&lt;\/title&gt;\n    <b class=\"fm-bold\">&lt;base href=\"\/webassembly\/\" \/&gt;<\/b>\n    &lt;link href=\"css\/bootstrap\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    &lt;link href=\"css\/app.css\" rel=\"stylesheet\" \/&gt;\n    &lt;link rel=\"icon\" type=\"image\/png\" href=\"favicon.png\" \/&gt;\n    &lt;link href=\"BlazorWebAssembly.styles.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n\n&lt;body&gt;\n    &lt;div id=\"app\"&gt;\n        &lt;svg class=\"loading-progress\"&gt;\n            &lt;circle r=\"40%\" cx=\"50%\" cy=\"50%\" \/&gt;\n            &lt;circle r=\"40%\" cx=\"50%\" cy=\"50%\" \/&gt;\n        &lt;\/svg&gt;\n        &lt;div class=\"loading-progress-text\"&gt;&lt;\/div&gt;\n    &lt;\/div&gt;\n        \n    &lt;div id=\"blazor-error-ui\"&gt;\n        An unhandled error has occurred.\n        &lt;a href=\"\" class=\"reload\"&gt;Reload&lt;\/a&gt;\n        &lt;a class=\"dismiss\"&gt; &lt;\/a&gt;\n    &lt;\/div&gt;\n    &lt;script src=\"_framework\/blazor.webassembly.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">base<\/code> element sets the URL from which all relative URLs in the document are defined and is required for the correct operation of the Blazor WebAssembly routing system.<\/p>\n<p class=\"fm-head2\">Setting the static web asset base path<\/p>\n<p class=\"body\">If you are using Visual Studio, right-click the BlazorWebAssembly project in the Solution Explorer, select Edit Project File from the pop-up menu, and add the configuration element shown in listing 37.10. If you are using Visual Studio Code, open the <code class=\"fm-code-in-text\">BlazorWebAssembly.csproj<\/code> file in the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> folder and add the configuration element shown in listing 37.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.10 Adding an element in the BlazorWebAssembly.csproj file in the BlazorWebAssembly folder<\/p>\n<pre class=\"programlisting\">...\n&lt;PropertyGroup&gt;\n    &lt;TargetFramework&gt;net6.0&lt;\/TargetFramework&gt;\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\n    <b class=\"fm-bold\">&lt;StaticWebAssetBasePath&gt;\/webassembly\/&lt;\/StaticWebAssetBasePath&gt;<\/b>\n&lt;\/PropertyGroup&gt;\n...<\/pre>\n<p class=\"body\">The element tag name is <code class=\"fm-code-in-text\">StaticWebAssetBasePath<\/code>, and the content is <code class=\"fm-code-in-text\">\/webassembly\/<\/code>, which starts and ends with a <code class=\"fm-code-in-text\">\/<\/code> character.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Make sure there are forward-slash (<code class=\"fm-code-in-text1\">\/<\/code>) characters before and after <code class=\"fm-code-in-text1\">webassembly<\/code> in the <code class=\"fm-code-in-text1\">StaticWebAssetBasePath<\/code> attribute of the <code class=\"fm-code-in-text1\">base<\/code> element. If you omit either character, then Blazor WebAssembly will not work.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-681\">37.2.7 Testing the placeholder components<\/h3>\n<p class=\"body\">Start ASP.NET Core by selecting Start Without Debugging or Run Without Debugging from the Debug menu. If you prefer to use the command prompt, run the command shown in listing 37.11 in the <code class=\"fm-code-in-text\">Advanced<\/code> project folder.<a id=\"calibre_link-2866\"><\/a><a id=\"calibre_link-845\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.11 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/webassembly, and you will see the placeholder content added by the template used to create the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> project.<\/p>\n<p class=\"body\">Using the PowerShell command prompt, run the following commands from within the <code class=\"fm-code-in-text\">Advanced<\/code> project folder. Click the Counter and Fetch Data links, and you will see different content displayed, as shown in figure 37.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre408\" src=\"\/images\/proaspnetcore7\/000414.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 37.3 The Blazor WebAssembly placeholder content<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-682\">37.3 Creating a Blazor WebAssembly component<\/h2>\n<p class=\"body\">Blazor WebAssembly uses the same approach as Blazor Server, relying on components as building blocks for applications, connected through the routing system, and displaying common content through layouts. In this section, I show how to create a Razor Component that works with Blazor WebAssembly, and then I\u2019ll re-create the simple forms application from chapter 36.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-683\">37.3.1 Importing the data model namespace<\/h3>\n<p class=\"body\"><a id=\"calibre_link-840\"><\/a>The components I will create in this chapter all use the classes in the shared <code class=\"fm-code-in-text\">DataModel<\/code> project. Rather than add <code class=\"fm-code-in-text\">@using<\/code> expressions to each component, add the namespace for the data model classes to the <code class=\"fm-code-in-text\">_Imports.razor<\/code> file in the root folder of the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> project, as shown in listing 37.12.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.12 Adding a namespace in the _Imports.razor file in the BlazorWebAssembly project<\/p>\n<pre class=\"programlisting\">@using System.Net.Http\n@using System.Net.Http.Json\n@using Microsoft.AspNetCore.Components.Forms\n@using Microsoft.AspNetCore.Components.Routing\n@using Microsoft.AspNetCore.Components.Web\n@using Microsoft.AspNetCore.Components.Web.Virtualization\n@using Microsoft.AspNetCore.Components.WebAssembly.Http\n@using Microsoft.JSInterop\n@using BlazorWebAssembly\n@using BlazorWebAssembly.Shared\n<b class=\"fm-bold\">@using Advanced.Models<\/b><\/pre>\n<p class=\"body\">Notice that although I moved the model classes to the <code class=\"fm-code-in-text\">DataModel<\/code> project, I have specified the <code class=\"fm-code-in-text\">Advanced.Models<\/code> namespace. This is because the class files I moved all have <code class=\"fm-code-in-text\">namespace<\/code> declarations that specify <code class=\"fm-code-in-text\">Advanced.Models<\/code>, which means that moving the files hasn\u2019t changed the namespace in which the classes exist.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-684\">37.3.2 Creating a component<\/h3>\n<p class=\"body\">In earlier chapters, I defined my Razor Components in a <code class=\"fm-code-in-text\">Blazor<\/code> folder to keep the new content separate from the other parts of ASP.NET Core. There is only Blazor content in the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> project, so I am going to follow the convention adopted by the project template and use the <code class=\"fm-code-in-text\">Pages<\/code> and <code class=\"fm-code-in-text\">Shared<\/code> folders.<a id=\"calibre_link-2867\"><\/a><a id=\"calibre_link-2868\"><\/a><\/p>\n<p class=\"body\">Add a Razor Component named <code class=\"fm-code-in-text\">List.razor<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder of the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> project and add the content shown in listing 37.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.13 The contents of the List.razor File in the Pages folder of the BlazorWebAssembly project<\/p>\n<pre class=\"programlisting\">@page \"\/forms\"\n@page \"\/forms\/list\"\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;\n    People (WebAssembly)\n&lt;\/h5&gt;\n\n&lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n    &lt;thead&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;\n            &lt;th&gt;Name&lt;\/th&gt;\n            &lt;th&gt;Dept&lt;\/th&gt;\n            &lt;th&gt;Location&lt;\/th&gt;\n            &lt;th&gt;&lt;\/th&gt;\n        &lt;\/tr&gt;\n    &lt;\/thead&gt;\n    &lt;tbody&gt;\n        @if (People.Count() == 0) {\n            &lt;tr&gt;\n                &lt;th colspan=\"5\" class=\"p-4 text-center\"&gt;\n                    Loading Data...\n                &lt;\/th&gt;\n            &lt;\/tr&gt;\n        } else {\n            @foreach (Person p in People) {\n                &lt;tr&gt;\n                    &lt;td&gt;@p.PersonId&lt;\/td&gt;\n                    &lt;td&gt;@p.Surname, @p.Firstname&lt;\/td&gt;\n                    &lt;td&gt;@p.Department?.Name&lt;\/td&gt;\n                    &lt;td&gt;@p.Location?.City&lt;\/td&gt;\n                    &lt;td class=\"text-center\"&gt;\n                        &lt;NavLink class=\"btn btn-sm btn-info\"\n                         href=\"@GetDetailsUrl(p.PersonId)\"&gt;\n                            Details\n                        &lt;\/NavLink&gt;\n                        &lt;NavLink class=\"btn btn-sm btn-warning\"\n                         href=\"@GetEditUrl(p.PersonId)\"&gt;\n                            Edit\n                        &lt;\/NavLink&gt;\n                        &lt;button class=\"btn btn-sm btn-danger\"\n                        @onclick=\"@(() =&gt; HandleDelete(p))\"&gt;\n                            Delete\n                        &lt;\/button&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        }\n    &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n&lt;NavLink class=\"btn btn-primary\" href=\"forms\/create\"&gt;Create&lt;\/NavLink&gt;\n\n@code {\n\n    [Inject]\n    public HttpClient? Http { get; set; }\n \n    public Person[] People { get; set; } = Array.Empty&lt;Person&gt;();\n\n    protected async override Task OnInitializedAsync() {\n        await UpdateData();\n    }\n        \n    private async Task UpdateData() {\n        if (Http != null) {\n            People = await Http.GetFromJsonAsync&lt;Person[]&gt;(\"\/api\/people\")\n                ?? Array.Empty&lt;Person&gt;();\n        }\n    }\n        \n    string GetEditUrl(long id) =&gt; $\"forms\/edit\/{id}\";\n    string GetDetailsUrl(long id) =&gt; $\"forms\/details\/{id}\";\n        \n    public async Task HandleDelete(Person p) {\n        if (Http != null) {\n            HttpResponseMessage resp =\n                await Http.DeleteAsync($\"\/api\/people\/{p.PersonId}\");\n            if (resp.IsSuccessStatusCode) {\n                await UpdateData();\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">If you compare this component with the Blazor Server equivalent from chapter 36, you will see that they are largely the same. Both types of Blazor use the same set of core features, which is why the content uses the same Razor directives, handles events with the <code class=\"fm-code-in-text\">@onclick<\/code> attributes, and uses the same <code class=\"fm-code-in-text\">@code<\/code> section for C# statements. A Blazor WebAssembly component is compiled into a C# class, just like its Blazor Server counterpart. The key difference is, of course, that the C# class that is generated is executed in the browser&mdash;and that\u2019s the reason for the differences from the component in chapter 36.<\/p>\n<p class=\"fm-head2\">Navigating in a Blazor WebAssembly component<\/p>\n<p class=\"body\">Notice that the URLs that are used for navigation are expressed without a leading forward-slash character, like this:<\/p>\n<pre class=\"programlisting\">...\n&lt;NavLink class=\"btn btn-primary\" <b class=\"fm-bold\">href=\"forms\/create\"<\/b>&gt;Create&lt;\/NavLink&gt;\n...<\/pre>\n<p class=\"body\">The root URL for the application was specified using the <code class=\"fm-code-in-text\">base<\/code> element in listing 37.13, and using relative URLs ensures that navigation is performed relative to the root. In this case, the relative <code class=\"fm-code-in-text\">forms\/create<\/code> URL is combined with the <code class=\"fm-code-in-text\">\/webassembly\/<\/code> root specified by the <code class=\"fm-code-in-text\">base<\/code> element, and navigation will be to <code class=\"fm-code-in-text\">\/webassembly\/forms\/create<\/code>. Including a leading forward slash would navigate to <code class=\"fm-code-in-text\">\/forms\/create<\/code> instead, which is outside the set of URLs that are being managed by the Blazor WebAssembly part of the application. This change is required only for navigation URLs. URLs specified with the <code class=\"fm-code-in-text\">@page<\/code> directive, for example, are not affected.<a id=\"calibre_link-2869\"><\/a><a id=\"calibre_link-844\"><\/a><\/p>\n<p class=\"fm-head2\">Getting Data in a Blazor WebAssembly component<\/p>\n<p class=\"body\">The biggest change is that Blazor WebAssembly can\u2019t use Entity Framework Core. Although the runtime may be able to execute the Entity Framework Core classes, the browser restricts WebAssembly applications to HTTP requests, preventing the use of SQL. To get data, Blazor WebAssembly applications consume web services, which is why I added the API controller to the Advanced project at the start of the chapter.<\/p>\n<p class=\"body\">As part of the Blazor WebAssembly application startup, a service is created for the <code class=\"fm-code-in-text\">HttpClient<\/code> class, which components can receive using the standard dependency injection features. The <code class=\"fm-code-in-text\">List<\/code> component receives an <code class=\"fm-code-in-text\">HttpClient<\/code> component through a property that has been decorated with the <code class=\"fm-code-in-text\">Inject<\/code> attribute, like this:<a id=\"calibre_link-2870\"><\/a><a id=\"calibre_link-842\"><\/a><\/p>\n<pre class=\"programlisting\">...\n[Inject]\npublic HttpClient? Http { get; set; }\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HttpClient<\/code> class provides the methods described in table 37.2 to send HTTP requests.<\/p>\n<p class=\"fm-table-caption\">Table 37.2 The methods defined by the HttpClient class<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2871\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetAsync(url)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sends an HTTP GET request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PostAsync(url, data)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sends an HTTP POST request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PutAsync(url, data)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sends an HTTP PUT request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PatchAync(url, data)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sends an HTTP PATCH request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">DeleteAsync(url)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sends an HTTP DELETE request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SendAsync(request)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sends an HTTP, configured using an <code class=\"fm-code-in-text1\">HttpRequestMessage<\/code> object.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The methods in table 37.2 return a <code class=\"fm-code-in-text\">Task&lt;HttpResponseMessage&gt;<\/code> result, which describes the response received from the HTTP server to the asynchronous request. Table 37.3 shows the most useful <code class=\"fm-code-in-text\">HttpResponseMessage<\/code> properties.<\/p>\n<p class=\"fm-table-caption\">Table 37.3 Useful HttpClient properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2872\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Content<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the content returned by the server.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">HttpResponseHeaders<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the response headers.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">StatusCode<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the response status code.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsSuccessStatusCode<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns <code class=\"fm-code-in-text1\">true<\/code> if the response status code is between 200 and 299, indicating a successful request.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">List<\/code> component uses the <code class=\"fm-code-in-text\">DeleteAsync<\/code> methods to ask the web service to delete objects when the user clicks a Delete button.<\/p>\n<pre class=\"programlisting\">...\npublic async Task HandleDelete(Person p) {\n    if (Http != null) {\n        HttpResponseMessage resp =\n            await Http.<b class=\"fm-bold\">DeleteAsync<\/b>($\"\/api\/people\/{p.PersonId}\");\n        if (resp.IsSuccessStatusCode) {\n            await UpdateData();\n        }\n    }\n...<\/pre>\n<p class=\"body\">These methods are useful when you don\u2019t need to work with the data the web service sends back, such as in this situation where I check to see only if the DELETE request has been successful. Notice that I specify the path for the request URL only when using the <code class=\"fm-code-in-text\">HttpClient<\/code> service because the web service is available using the same scheme, host, and port as the application.<\/p>\n<p class=\"body\">For operations where the web service returns data, the extension methods for the <code class=\"fm-code-in-text\">HttpClient<\/code> class described in table 37.4 are more useful. These methods serialize data into JSON so it can be sent to the server and parse JSON responses into C# objects. For requests that return no result, the generic type argument can be omitted.<a id=\"calibre_link-2873\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 37.4 The HttpClient extension methods<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2874\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetFromJsonAsync&lt;T&gt;(url)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sends an HTTP GET request and parses the response to type <code class=\"fm-code-in-text1\">T<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PostJsonAsync&lt;T&gt;(url, data)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sends an HTTP POST request with the serialized data value of <code class=\"fm-code-in-text1\">T<\/code>.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PutJsonAsync&lt;T&gt;(url, data)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method sends an HTTP PUT request with the serialized data value of <code class=\"fm-code-in-text1\">T<\/code>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The <code class=\"fm-code-in-text\">List<\/code> component uses the <code class=\"fm-code-in-text\">GetJsonAsync&lt;T&gt;<\/code> method to request data from the web service.<\/p>\n<pre class=\"programlisting\">...\nprivate async Task UpdateData() {\n    if (Http != null) {\n        People = await Http.<b class=\"fm-bold\">GetFromJsonAsync<\/b>&lt;Person[]&gt;(\"\/api\/people\") \n            ?? Array.Empty&lt;Person&gt;();\n    }\n} \n...<\/pre>\n<p class=\"body\">Setting the generic type argument to <code class=\"fm-code-in-text\">Person[]<\/code> tells <code class=\"fm-code-in-text\">HttpClient<\/code> to parse the response into an array of <code class=\"fm-code-in-text\">Person<\/code> objects.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The <code class=\"fm-code-in-text1\">HttpClient<\/code> class doesn\u2019t present any scope or lifecycle issues and sends requests only when one of the methods described in table 37.2 or table 37.4 is invoked. Some thought is required, however, about when to request new data. In this example, I requery the web service after an object has been deleted, rather than simply remove the object from the data that was requested when the component was initialized. This may not be suitable for all applications because it will reflect any changes to the database that have been made by other users.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-685\">37.3.3 Creating a layout<\/h3>\n<p class=\"body\">The template used to create the Blazor WebAssembly project includes a layout that presents the navigation features for the placeholder content. I don\u2019t want these navigation features, so the first step is to create a new layout. Add a Razor Component named <code class=\"fm-code-in-text\">EmptyLayout.razor<\/code> to the <code class=\"fm-code-in-text\">Shared<\/code> folder of the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> project with the content shown in listing 37.14.<a id=\"calibre_link-2875\"><\/a><a id=\"calibre_link-843\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.14 The EmptyLayout.razor file in the Shared folder of the BlazorWebAssembly project<\/p>\n<pre class=\"programlisting\">@inherits LayoutComponentBase\n\n&lt;div class=\"m-2\"&gt;\n    @Body\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">I could apply the new layout with <code class=\"fm-code-in-text\">@layout<\/code> expressions, as I did in chapter 36, but I am going to use this layout as the default by changing the routing configuration, which is defined in the <code class=\"fm-code-in-text\">App.razor<\/code> file in the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> project, as shown in listing 37.15.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.15 Applying the layout in the App.razor file in the BlazorWebAssembly project<\/p>\n<pre class=\"programlisting\">&lt;Router AppAssembly=\"@typeof(App).Assembly\"&gt;\n    &lt;Found Context=\"routeData\"&gt;\n        <b class=\"fm-bold\">&lt;RouteView RouteData=\"@routeData\"<\/b> \n            <b class=\"fm-bold\">DefaultLayout=\"@typeof(EmptyLayout)\" \/&gt;<\/b>\n        &lt;FocusOnNavigate RouteData=\"@routeData\" Selector=\"h1\" \/&gt;\n    &lt;\/Found&gt;\n    &lt;NotFound&gt;\n        &lt;PageTitle&gt;Not found&lt;\/PageTitle&gt;\n        <b class=\"fm-bold\">&lt;LayoutView Layout=\"@typeof(EmptyLayout)\"&gt;<\/b>\n            &lt;p role=\"alert\"&gt;Sorry, there's nothing at this address.&lt;\/p&gt;\n        &lt;\/LayoutView&gt;\n    &lt;\/NotFound&gt;\n&lt;\/Router&gt;<\/pre>\n<p class=\"body\">Chapter 35 describes the <code class=\"fm-code-in-text\">Router<\/code>, <code class=\"fm-code-in-text\">RouteView<\/code>, <code class=\"fm-code-in-text\">Found<\/code>, and <code class=\"fm-code-in-text\">NotFound<\/code> components.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-686\">37.3.4 Defining CSS styles<\/h3>\n<p class=\"body\">The template created the Blazor WebAssembly project with its own copy of the Bootstrap CSS framework and with an additional stylesheet that combines the styles required to configure the Blazor WebAssembly error and validation elements and manage the layout of the application. Replace the <code class=\"fm-code-in-text\">link<\/code> elements in the HTML file as shown in listing 37.16 and apply styles directly to the <code class=\"fm-code-in-text\">error<\/code> element. This has the effect of removing the styles used by the Microsoft layout and using the Bootstrap CSS stylesheet that was added to the Advanced project.<a id=\"calibre_link-2876\"><\/a><a id=\"calibre_link-841\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.16 Modifying the index.xhtml file in the wwwroot folder in the BlazorWebAssembly project<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n\n&lt;head&gt;\n    &lt;meta charset=\"utf-8\" \/&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0,\n          maximum-scale=1.0, user-scalable=no\" \/&gt;\n    &lt;title&gt;BlazorWebAssembly&lt;\/title&gt;\n    &lt;base href=\"\/webassembly\/\" \/&gt;\n    <b class=\"fm-bold\">&lt;!--&lt;link href=\"css\/bootstrap\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;--&gt;<\/b>\n    <b class=\"fm-bold\">&lt;!--&lt;link href=\"css\/app.css\" rel=\"stylesheet\" \/&gt;--&gt;<\/b>\n    <b class=\"fm-bold\">&lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;<\/b>\n    &lt;link rel=\"icon\" type=\"image\/png\" href=\"favicon.png\" \/&gt;\n    &lt;link href=\"BlazorWebAssembly.styles.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n\n&lt;body&gt;\n    &lt;div id=\"app\"&gt;\n        &lt;svg class=\"loading-progress\"&gt;\n            &lt;circle r=\"40%\" cx=\"50%\" cy=\"50%\" \/&gt;\n            &lt;circle r=\"40%\" cx=\"50%\" cy=\"50%\" \/&gt;\n        &lt;\/svg&gt;\n        &lt;div class=\"loading-progress-text\"&gt;&lt;\/div&gt;\n    &lt;\/div&gt;\n        \n    <b class=\"fm-bold\">&lt;div id=\"blazor-error-ui\"<\/b>\n         <b class=\"fm-bold\">class=\"text-center bg-danger h6 text-white p-2 fixed-top w-100\"<\/b>\n         <b class=\"fm-bold\">style=\"display:none\"&gt;<\/b>\n        An unhandled error has occurred.\n        &lt;a href=\"\" class=\"reload\"&gt;Reload&lt;\/a&gt;\n        &lt;a class=\"dismiss\"&gt; &lt;\/a&gt;\n    &lt;\/div&gt;\n    &lt;script src=\"_framework\/blazor.webassembly.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n\n&lt;\/html&gt; <\/pre>\n<p class=\"body\">To see the new component, restart ASP.NET Core and request http:\/\/localhost:5000\/webassembly\/forms, which will produce the response shown in figure 37.4.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre409\" src=\"\/images\/proaspnetcore7\/000415.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 37.4 A Blazor WebAssembly component<\/p>\n<\/div>\n<p class=\"body\">Blazor WebAssembly components follow the standard Blazor lifecycle, and the component displays the data it receives from the web service.<\/p>\n<h2 class=\"fm-head\" id=\"calibre_link-687\">37.4 Completing the Blazor WebAssembly Form application<\/h2>\n<p class=\"body\">Only the Delete button displayed by the <code class=\"fm-code-in-text\">List<\/code> component works currently. In the sections that follow, I complete the Blazor WebAssembly form application by creating additional components.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-688\">37.4.1 Creating the details component<\/h3>\n<p class=\"body\">Add a Razor Component named <code class=\"fm-code-in-text\">Details.razor<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder of the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> project with the content shown in listing 37.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.17 The contents of the Details.razor file in the Pages folder of the BlazorWebAssembly Project<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/details\/{id:long}\"\n\n&lt;h4 class=\"bg-info text-center text-white p-2\"&gt;Details (WebAssembly)&lt;\/h4&gt;\n\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;ID&lt;\/label&gt;\n    &lt;input class=\"form-control\" value=\"@PersonData.PersonId\" disabled \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;Firstname&lt;\/label&gt;\n    &lt;input class=\"form-control\" value=\"@PersonData.Firstname\" disabled \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;Surname&lt;\/label&gt;\n    &lt;input class=\"form-control\" value=\"@PersonData.Surname\" disabled \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;Department&lt;\/label&gt;\n    &lt;input class=\"form-control\" value=\"@PersonData.Department?.Name\" \n        disabled \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"form-group\"&gt;\n    &lt;label&gt;Location&lt;\/label&gt;\n    &lt;input class=\"form-control\"\n           value=\"@($\"{PersonData.Location?.City}, \" \n                + PersonData.Location?.State)\"\n           disabled \/&gt;\n&lt;\/div&gt;\n&lt;div class=\"text-center p-2\"&gt;\n    &lt;NavLink class=\"btn btn-info\" href=\"@EditUrl\"&gt;Edit&lt;\/NavLink&gt;\n    &lt;NavLink class=\"btn btn-secondary\" href=\"forms\"&gt;Back&lt;\/NavLink&gt;\n&lt;\/div&gt;\n\n@code {\n\n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n        \n    [Inject]\n    public HttpClient? Http { get; set; }\n \n    [Parameter]\n    public long Id { get; set; }\n \n    public Person PersonData { get; set; } = new Person();\n        \n    protected async override Task OnParametersSetAsync() {\n        if (Http != null) {\n            PersonData = await Http.GetFromJsonAsync&lt;Person&gt;(\n                $\"\/api\/people\/{Id}\")\n                    ?? new();\n        }\n    }\n        \n    public string EditUrl =&gt; $\"forms\/edit\/{Id}\";\n} <\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Details<\/code> component has only two differences from its Blazor Server counterpart, following the pattern established by the <code class=\"fm-code-in-text\">List<\/code> component: the data is obtained through the <code class=\"fm-code-in-text\">HttpClient<\/code> service, and navigation targets are expressed using relative URLs. In all other regards, such as obtaining parameters from routing data, Blazor WebAssembly works just the same way as Blazor Server.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-689\">37.4.2 Creating the editor component<\/h3>\n<p class=\"body\">To complete the forms application, add a Razor Component named <code class=\"fm-code-in-text\">Editor.razor<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder of the <code class=\"fm-code-in-text\">BlazorWebAssembly<\/code> project with the content shown in listing 37.18.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 37.18 The contents of the Editor.razor file in the Pages folder of the BlazorWebAssembly Project<\/p>\n<pre class=\"programlisting\">@page \"\/forms\/edit\/{id:long}\"\n@page \"\/forms\/create\"\n\n&lt;link href=\"\/blazorValidation.css\" rel=\"stylesheet\" \/&gt;\n\n&lt;h4 class=\"bg-@Theme text-center text-white p-2\"&gt;@Mode (WebAssembly)&lt;\/h4&gt;\n\n&lt;EditForm Model=\"PersonData\" OnValidSubmit=\"HandleValidSubmit\"&gt;\n    &lt;DataAnnotationsValidator \/&gt;\n    @if (Mode == \"Edit\") {\n        &lt;div class=\"form-group\"&gt;\n            &lt;label&gt;ID&lt;\/label&gt;\n            &lt;InputNumber class=\"form-control\"\n                     @bind-Value=\"PersonData.PersonId\" readonly \/&gt;\n        &lt;\/div&gt;\n    }\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Firstname&lt;\/label&gt;\n        &lt;ValidationMessage For=\"@(() =&gt; PersonData.Firstname)\" \/&gt;\n        &lt;InputText class=\"form-control\" \n            @bind-Value=\"PersonData.Firstname\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Surname&lt;\/label&gt;\n        &lt;ValidationMessage For=\"@(() =&gt; PersonData.Surname)\" \/&gt;\n        &lt;InputText class=\"form-control\" \n            @bind-Value=\"PersonData.Surname\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Department&lt;\/label&gt;\n        &lt;ValidationMessage For=\"@(() =&gt; PersonData.DepartmentId)\" \/&gt;\n        &lt;select @bind=\"PersonData.DepartmentId\" class=\"form-control\"&gt;\n            &lt;option selected disabled value=\"0\"&gt;\n                Choose a Department\n            &lt;\/option&gt;\n            @foreach (var kvp in Departments) {\n                &lt;option value=\"@kvp.Value\"&gt;@kvp.Key&lt;\/option&gt;\n            }\n        &lt;\/select&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Location&lt;\/label&gt;\n        &lt;ValidationMessage For=\"@(() =&gt; PersonData.LocationId)\" \/&gt;\n        &lt;select @bind=\"PersonData.LocationId\" class=\"form-control\"&gt;\n            &lt;option selected disabled value=\"0\"&gt;Choose a Location&lt;\/option&gt;\n            @foreach (var kvp in Locations) {\n                &lt;option value=\"@kvp.Value\"&gt;@kvp.Key&lt;\/option&gt;\n            }\n        &lt;\/select&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"text-center p-2\"&gt;\n        &lt;button type=\"submit\" class=\"btn btn-@Theme\"&gt;Save&lt;\/button&gt;\n        &lt;NavLink class=\"btn btn-secondary\" href=\"forms\"&gt;Back&lt;\/NavLink&gt;\n    &lt;\/div&gt;\n&lt;\/EditForm&gt;\n\n@code {\n\n    [Inject]\n    public HttpClient? Http { get; set; }\n        \n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n \n    [Parameter]\n    public long Id { get; set; }\n \n    public Person PersonData { get; set; } = new Person();\n        \n    public IDictionary&lt;string, long&gt; Departments { get; set; }\n        = new Dictionary&lt;string, long&gt;();\n    public IDictionary&lt;string, long&gt; Locations { get; set; }\n        = new Dictionary&lt;string, long&gt;();\n                \n    protected async override Task OnParametersSetAsync() {\n        if (Http != null) {\n            if (Mode == \"Edit\") {\n                PersonData = await Http.GetFromJsonAsync&lt;Person&gt;(\n                        $\"\/api\/people\/{Id}\")\n                    ?? new();\n            }\n            var depts = await Http.GetFromJsonAsync&lt;Department[]&gt;(\n                    \"\/api\/departments\");\n            Departments = (depts ?? Array.Empty&lt;Department&gt;())\n                .ToDictionary(d =&gt; d.Name, d =&gt; d.Departmentid);\n            var locs = await Http.GetFromJsonAsync&lt;Location[]&gt;(\n                \"\/api\/locations\");\n            Locations = (locs ?? Array.Empty&lt;Location&gt;())\n                .ToDictionary(l =&gt; $\"{l.City}, {l.State}\", \n                    l =&gt; l.LocationId);\n        }\n    }\n        \n    public string Theme =&gt; Id == 0 ? \"primary\" : \"warning\";\n    public string Mode =&gt; Id == 0 ? \"Create\" : \"Edit\";\n        \n    public async Task HandleValidSubmit() {\n        if (Http != null) {\n            if (Mode == \"Create\") {\n                await Http.PostAsJsonAsync(\"\/api\/people\", PersonData);\n            } else {\n                await Http.PutAsJsonAsync(\"\/api\/people\", PersonData);\n            }\n            NavManager?.NavigateTo(\"forms\");\n        }\n    }\n}<\/pre>\n<p class=\"body\">This component uses the Blazor form features described in chapter 36 but uses HTTP requests to read and write data to the web service created at the start of the chapter. The <code class=\"fm-code-in-text\">GetFromJsonAsync&lt;T&gt;<\/code> method is used to read data from the web service, and the <code class=\"fm-code-in-text\">PostAsJsonAsync<\/code> and <code class=\"fm-code-in-text\">PutAsJsonAsync<\/code> methods are used to send POST or PUT requests when the user submits the form.<\/p>\n<p class=\"body\">Notice that I have not used the custom <code class=\"fm-code-in-text\">select<\/code> component or validation components I created in chapter 36. Sharing components between projects&mdash;especially when Blazor WebAssembly is introduced after development has started&mdash;is awkward. I expect the process to improve in future releases, but for this chapter, I have simply done without the features. As a consequence, the <code class=\"fm-code-in-text\">select<\/code> elements do not trigger validation when a value is selected, the submit button isn\u2019t automatically disabled, and there are no restrictions on the combination of department and location.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/webassembly\/forms, and you will see the Blazor WebAssembly version of the form application. Click the Details button for the first item in the table, and you will see the fields for the selected object. Click the Edit button, and you will be presented with an editable form. Make a change and click the Save button, and the changes will be sent to the web service and displayed in the data table, as shown in figure 37.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre410\" src=\"\/images\/proaspnetcore7\/000416.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 37.5 The completed Blazor WebAssembly form application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2877\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Blazor WebAssembly creates client-side applications that do not need to maintain a persistent connection to the ASP.NET Core server.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Creating an application with WebAssembly builds on the features described in earlier chapters for Blazor Server.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Data access in a WebAssembly application must be performed through the <code class=\"fm-code-in-text\">HttpClient<\/code> object received via dependency injection.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-690\">\n<div class=\"calibre1\" id=\"calibre_link-2878\">\n<h1 class=\"tochead\" id=\"calibre_link-2879\"><a id=\"calibre_link-2880\"><\/a><a id=\"calibre_link-2881\"><\/a>38 Using ASP.NET Core Identity<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2882\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Setting up ASP.NET Core Identity in an ASP.NET Core project<\/li>\n<li class=\"co-summary-bullet\">Creating the ASP.NET Core Identity database<\/li>\n<li class=\"co-summary-bullet\">Managing user accounts and roles<\/li>\n<\/ul>\n<p class=\"body\">ASP.NET Core Identity is an API from Microsoft to manage users in ASP.NET Core applications and includes support for integrating authentication and authorization into the request pipeline.<\/p>\n<p class=\"body\">ASP.NET Core Identity is a toolkit with which you create the authorization and authentication features an application requires. There are endless integration options for features such as two-factor authentication, federation, single sign-on, and account self-service. There are options that are useful only in large corporate environments or when using cloud-hosted user management.<\/p>\n<p class=\"body\">ASP.NET Core Identity has evolved into its own framework and is too large for me to cover in detail in this book. Instead, I have focused on the parts of the Identity API that intersect with web application development, much as I have done with Entity Framework Core. In this chapter, I show you how to add ASP.NET Core Identity to a project and explain how to consume the ASP.NET Core Identity API to create tools to perform basic user and role management. In chapter 39, I show you how to use ASP.NET Core Identity to authenticate users and perform authorization. Table 38.1 puts ASP.NET Core Identity in context.<\/p>\n<p class=\"fm-table-caption\">Table 38.1 Putting ASP.NET Core Identity in context<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2883\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Question<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Answer<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">What is it?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">ASP.NET Core Identity is an API for managing users.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Why is it useful?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Most applications have some features that should not be available to all users. ASP.NET Core Identity provides features to allow users to authenticate themselves and gain access to restricted features.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">How is it used?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">ASP.NET Core Identity is added to projects as a package and stores its data in a database using Entity Framework Core. Management of users is performed through a well-defined API, and its features are applied as attributes, as I describe in chapter 39.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any pitfalls or limitations?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">ASP.NET Core Identity is complex and provides support for a wide range of authentication, authorization, and management models. It can be difficult to understand all the options, and documentation can be sparse.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Are there any alternatives?<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">There is no sensible alternative to ASP.NET Core Identity if a project needs to restrict access to features.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 38.2 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 38.2 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2884\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Preparing the application for Identity<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Create the context class and use it to prepare a migration that is applied to the database.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">4&ndash;7<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Managing user accounts<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">UserManager&lt;T&gt;<\/code> class.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">8&ndash;12, 15, 16<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Setting a username and password policy<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the options pattern to configure Identity.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">13, 14<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Managing roles<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">RoleManager&lt;T&gt;<\/code> class to manage the roles and use the <code class=\"fm-code-in-text1\">UserManager&lt;T&gt;<\/code> class to assign users to roles.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">17&ndash;20<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-691\">38.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the Advanced, DataModel, and BlazorWebAssembly projects from chapter 37. If you are using Visual Studio, open the <code class=\"fm-code-in-text\">Advanced.sln<\/code> file you created in the previous chapter to open all three projects. If you are using Visual Studio Code, open the folder that contains the three projects.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the folder that contains the <code class=\"fm-code-in-text\">Advanced.csproj<\/code> file, and run the command shown in listing 38.1 to drop the database.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.1 Dropping the database<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force<\/pre>\n<p class=\"body\">Use the PowerShell command prompt to run the command shown in listing 38.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.2 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000, which will produce the response shown in figure 38.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre411\" src=\"\/images\/proaspnetcore7\/000417.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.1 Running the example application<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-692\">38.2 Preparing the project for ASP.NET Core Identity<\/h2>\n<p class=\"body\">The process for setting up ASP.NET Core Identity requires adding a package to the project, configuring the application, and preparing the database. To get started, use a PowerShell command prompt to run the command shown in listing 38.3 in the <code class=\"fm-code-in-text\">Advanced<\/code> project folder, which installs the ASP.NET Core Identity package. If you are using Visual Studio, you can install the package by selecting Project &gt; Manage NuGet Packages.<a id=\"calibre_link-2885\"><\/a><a id=\"calibre_link-2886\"><\/a><a id=\"calibre_link-2887\"><\/a><a id=\"calibre_link-2888\"><\/a><a id=\"calibre_link-2889\"><\/a><a id=\"calibre_link-771\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.3 Installing ASP.NET Core Identity packages<\/p>\n<pre class=\"programlisting\">dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore\n    --version 7.0.0<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-693\">38.2.1 Preparing the ASP.NET Core Identity database<\/h3>\n<p class=\"body\">ASP.NET Identity requires a database, which is managed through Entity Framework Core. To create the Entity Framework Core context class that will provide access to the Identity data, add a class file named <code class=\"fm-code-in-text\">IdentityContext.cs<\/code> to the <code class=\"fm-code-in-text\">Advanced\/Models<\/code> folder with the code shown in listing 38.4.<a id=\"calibre_link-2890\"><\/a><a id=\"calibre_link-2891\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.4 The IdentityContext.cs File in the Models Folder of the Advanced Project<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Identity;\nusing Microsoft.AspNetCore.Identity.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace Advanced.Models {\n    public class IdentityContext: IdentityDbContext&lt;IdentityUser&gt; {\n        \n        public IdentityContext(DbContextOptions&lt;IdentityContext&gt; options)\n            : base(options) { }\n    }\n}<\/pre>\n<p class=\"body\">The ASP.NET Core Identity package includes the <code class=\"fm-code-in-text\">IdentityDbContext&lt;T&gt;<\/code> class, which is used to create an Entity Framework Core context class. The generic type argument <code class=\"fm-code-in-text\">T<\/code> is used to specify the class that will represent users in the database. You can create custom user classes, but I have used the basic class, called <code class=\"fm-code-in-text\">IdentityUser<\/code>, which provides the core Identity features.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> Don\u2019t worry if the classes used in listing 38.4 don\u2019t make sense. If you are unfamiliar with Entity Framework Core, then I suggest you treat the class as a black box. Changes are rarely required once the building blocks for ASP.NET Core Identity have been set up, and you can copy the files from this chapter into your own projects.<\/p>\n<p class=\"fm-head2\">Configuring the database connection string<\/p>\n<p class=\"body\">A connection string is required to tell ASP.NET Core Identity where it should store its data. In listing 38.5, I added a connection string to the <code class=\"fm-code-in-text\">appsettings.json<\/code> file, alongside the one used for the application data.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.5 Adding a connection in the appsettings.json file in the Advanced project<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\",\n      \"Microsoft.EntityFrameworkCore\": \"Information\"\n    }\n  },\n  \"AllowedHosts\": \"*\",\n  \"ConnectionStrings\": {\n    \"PeopleConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=People;\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>MultipleActiveResultSets=True\",\n    <b class=\"fm-bold\">\"IdentityConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=Identity<\/b>\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span><b class=\"fm-bold\">;MultipleActiveResultSets=True\"<\/b>\n  }\n}<\/pre>\n<p class=\"body\">The connection string specifies a LocalDB database named <code class=\"fm-code-in-text\">Identity<\/code>.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> The width of the printed page doesn\u2019t allow for sensible formatting of the connection string, which must appear in a single unbroken line. When you add the connection string to your own project, make sure that it is on a single line.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-694\">38.2.2 Configuring the application<\/h3>\n<p class=\"body\">The next step is to configure ASP.NET Core so the Identity database context is set up as a service, as shown in listing 38.6.<a id=\"calibre_link-2892\"><\/a><a id=\"calibre_link-2893\"><\/a><a id=\"calibre_link-745\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.6 Configuring identity in the Program.cs file in the Advanced project<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Identity;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddServerSideBlazor();\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddSingleton&lt;Advanced.Services.ToggleService&gt;();\n\n<b class=\"fm-bold\">builder.Services.AddDbContext&lt;IdentityContext&gt;(opts =&gt;<\/b>\n    <b class=\"fm-bold\">opts.UseSqlServer(builder.Configuration[<\/b>\n        <b class=\"fm-bold\">\"ConnectionStrings:IdentityConnection\"]));<\/b>\n<b class=\"fm-bold\">builder.Services.AddIdentity&lt;IdentityUser, IdentityRole&gt;()<\/b>\n    <b class=\"fm-bold\">.AddEntityFrameworkStores&lt;IdentityContext&gt;();<\/b>\n        \nvar app = builder.Build();\n\napp.UseStaticFiles();\n\napp.MapControllers();\napp.MapControllerRoute(\"controllers\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\napp.MapBlazorHub();\napp.MapFallbackToPage(\"\/_Host\");\napp.UseBlazorFrameworkFiles(\"\/webassembly\");\napp.MapFallbackToFile(\"\/webassembly\/{*path:nonfile}\", \n    \"\/webassembly\/index.xhtml\");\n        \nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-695\">38.2.3 Creating and applying the Identity database migration<\/h3>\n<p class=\"body\">The remaining step is to create the Entity Framework Core database migration and apply it to create the database. Open a new PowerShell window, navigate to the <code class=\"fm-code-in-text\">Advanced<\/code> project folder, and run the commands shown in listing 38.7.<a id=\"calibre_link-2894\"><\/a><a id=\"calibre_link-2895\"><\/a><a id=\"calibre_link-2896\"><\/a><a id=\"calibre_link-759\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.7 Creating and applying the database migration<\/p>\n<pre class=\"programlisting\">dotnet ef migrations add --context IdentityContext Initial\ndotnet ef database update --context IdentityContext<\/pre>\n<p class=\"body\">As I explained in earlier chapters, Entity Framework Core manages changes to database schemas through a feature called <i class=\"fm-italics\">migrations<\/i>. Now that there are two database context classes in the project, the Entity Framework Core tools require the <code class=\"fm-code-in-text\">--context<\/code> argument to determine which context class is being used. The commands in listing 38.7 create a migration that contains the ASP.NET Core Identity schema and apply it to the database.<a id=\"calibre_link-2897\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Resetting the ASP.NET Core Identity database<\/p>\n<p class=\"fm-sidebar-text\">If you need to reset the database, run the <code class=\"fm-code-in-text1\">dotnet ef database drop --force --context IdentityContext<\/code> command in the <code class=\"fm-code-in-text1\">Advanced<\/code> folder and then run the <code class=\"fm-code-in-text1\">dotnet ef database update --context IdentityContext<\/code> command. This will delete the existing database and create a new&mdash;and empty&mdash;replacement. Do not use these commands on production systems because you will delete user credentials. If you need to reset the main database, then run the <code class=\"fm-code-in-text1\">dotnet ef database drop --force --context DataContext<\/code> command, followed by <code class=\"fm-code-in-text1\">dotnet ef database update --context DataContext<\/code>.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-696\">38.3 Creating user management tools<\/h2>\n<p class=\"body\">In this section, I am going to create the tools that manage users through ASP.NET Core Identity. Users are managed through the <code class=\"fm-code-in-text\">UserManager&lt;T&gt;<\/code> class, where <code class=\"fm-code-in-text\">T<\/code> is the class chosen to represent users in the database. When I created the Entity Framework Core context class, I specified <code class=\"fm-code-in-text\">IdentityUser<\/code> as the class to represent users in the database. This is the built-in class that is provided by ASP.NET Core Identity, and it provides the core features that are required by most applications. table 38.3 describes the most useful <code class=\"fm-code-in-text\">IdentityUser<\/code> properties. (There are additional properties defined by the <code class=\"fm-code-in-text\">IdentityUser<\/code> class, but these are the ones required by most applications and are the ones I use in this book.)<a id=\"calibre_link-2898\"><\/a><a id=\"calibre_link-2899\"><\/a><a id=\"calibre_link-2900\"><\/a><a id=\"calibre_link-769\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Scaffolding the Identity management tools<\/p>\n<p class=\"fm-sidebar-text\">Microsoft provides a tool that will generate a set of Razor Pages for user management. The tool adds generic content&mdash;known as <i class=\"fm-italics\">scaffolding<\/i>&mdash;from templates to a project, which you then tailor to the application. I am not a fan of scaffolding or templates, and this is not an exception. The Microsoft Identity templates are well thought out, but they are of limited use because they focus on self-management, allowing users to create accounts, change passwords, and so on, without administrator intervention. You can adapt the templates to restrict the range of tasks that users perform, but the premise behind the features remains the same.<a id=\"calibre_link-2901\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">If you are writing the type of application where users manage their own credentials, then the scaffolding option may be worth considering and is described at <a class=\"url\" href=\"https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/security\/authentication\/scaffold-identity\">https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/security\/authentication\/scaffold-identity<\/a>. For all other approaches, the user management API provided by ASP.NET Core Identity should be used.<\/p>\n<\/div>\n<p class=\"fm-table-caption\">Table 38.3 Useful IdentityUser properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2902\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Id<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property contains the unique ID for the user.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">UserName<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the user\u2019s username.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Email<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property contains the user\u2019s e-mail address.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 38.4 describes the <code class=\"fm-code-in-text\">UserManagement&lt;T&gt;<\/code> members I use in this section to manage users.<\/p>\n<p class=\"fm-table-caption\">Table 38.4 Useful UserManager&lt;T&gt; members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2903\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Users<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns a sequence containing the users stored in the database.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">FindByIdAsync(id)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method queries the database for the user object with the specified ID.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">CreateAsync(user, password)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method stores a new user in the database using the specified password.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">UpdateAsync(user)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method modifies an existing user in the database.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">DeleteAsync(user)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method removes the specified user from the database.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-697\">38.3.1 Preparing for user management tools<\/h3>\n<p class=\"body\">In preparation for creating the management tools, add the expressions shown in listing 38.8 to the <code class=\"fm-code-in-text\">_ViewImports.cshtml<\/code> file in the <code class=\"fm-code-in-text\">Pages<\/code> folder of the Advanced project.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.8 Adding expressions in the _ViewImports.cshtml file in the Pages folder of the Advanced project<\/p>\n<pre class=\"programlisting\">@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n@using Advanced.Models\n@using Microsoft.AspNetCore.Mvc.RazorPages\n@using Microsoft.EntityFrameworkCore\n<b class=\"fm-bold\">@using System.ComponentModel.DataAnnotations<\/b>\n<b class=\"fm-bold\">@using Microsoft.AspNetCore.Identity<\/b>\n<b class=\"fm-bold\">@using Advanced.Pages<\/b><\/pre>\n<p class=\"body\">Next, create the <code class=\"fm-code-in-text\">Pages\/Users<\/code> folder in the Advanced project and add to it a Razor Layout named <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Users<\/code> folder with the content shown in listing 38.9.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.9 The _Layout.cshtml file in the Pages\/Users folder in the Advanced project<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;Identity&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;h5 class=\"bg-info text-white text-center p-2\"&gt;\n            User Administration\n        &lt;\/h5&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Add a class file named <code class=\"fm-code-in-text\">AdminPageModel.cs<\/code> to the <code class=\"fm-code-in-text\">Pages<\/code> folder and use it to define the class shown in listing 38.10.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.10 The AdminPageModel.cs file in the Pages folder in the Advanced project<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.RazorPages;\n\nnamespace Advanced.Pages {\n    public class AdminPageModel : PageModel {\n        \n    }\n}<\/pre>\n<p class=\"body\">This class will be the base for the page model classes defined in this section. As you will see in chapter 39, a common base class is useful when it comes to securing the application.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-698\">38.3.2 Enumerating user accounts<\/h3>\n<p class=\"body\">Although the database is currently empty, I am going to start by creating a Razor Page that will enumerate user accounts. Add a Razor Page named <code class=\"fm-code-in-text\">List.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Users<\/code> folder in the Advanced project with the content shown in listing 38.11.<a id=\"calibre_link-2904\"><\/a><a id=\"calibre_link-765\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.11 The contents of the List.cshtml file in the Pages\/Users folder in the Advanced project<\/p>\n<pre class=\"programlisting\">@page\n@model ListModel\n\n&lt;table class=\"table table-sm table-bordered\"&gt;\n    &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Email&lt;\/th&gt;&lt;th&gt;&lt;\/th&gt;&lt;\/tr&gt;\n    @if (Model.Users.Count() == 0) {\n        &lt;tr&gt;&lt;td colspan=\"4\" class=\"text-center\"&gt;No User Accounts&lt;\/td&gt;&lt;\/tr&gt;\n    } else {\n        foreach (IdentityUser user in Model.Users) {\n            &lt;tr&gt;\n                &lt;td&gt;@user.Id&lt;\/td&gt;\n                &lt;td&gt;@user.UserName&lt;\/td&gt;\n                &lt;td&gt;@user.Email&lt;\/td&gt;\n                &lt;td class=\"text-center\"&gt;\n                    &lt;form asp-page=\"List\" method=\"post\"&gt;\n                        &lt;input type=\"hidden\" name=\"Id\" value=\"@user.Id\" \/&gt;\n                        &lt;a class=\"btn btn-sm btn-warning\" \n                            asp-page=\"Editor\" asp-route-id=\"@user.Id\" \n                            asp-route-mode=\"edit\"&gt;\n                                Edit\n                        &lt;\/a&gt;\n                        &lt;button type=\"submit\" \n                                class=\"btn btn-sm btn-danger\"&gt;\n                            Delete\n                        &lt;\/button&gt;\n                    &lt;\/form&gt;\n                &lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    }\n&lt;\/table&gt;\n\n&lt;a class=\"btn btn-primary\" asp-page=\"create\"&gt;Create&lt;\/a&gt;\n\n@functions {\n\n    public class ListModel : AdminPageModel {\n        public UserManager&lt;IdentityUser&gt; UserManager;\n                \n        public ListModel(UserManager&lt;IdentityUser&gt; userManager) {\n            UserManager = userManager;\n        }\n \n        public IEnumerable&lt;IdentityUser&gt; Users { get; set; }\n            = Enumerable.Empty&lt;IdentityUser&gt;();\n                        \n        public void OnGet() {\n            Users = UserManager.Users;\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UserManager&lt;IdentityUser&gt;<\/code> class is set up as a service so that it can be consumed via dependency injection. The <code class=\"fm-code-in-text\">Users<\/code> property returns a collection of <code class=\"fm-code-in-text\">IdentityUser<\/code> objects, which can be used to enumerate the user accounts. This Razor Page displays the users in a table, with buttons that allow each user to be edited or deleted, although this won\u2019t be visible initially because a placeholder message is shown when there are no user objects to display. There is a button that navigates to a Razor Page named <code class=\"fm-code-in-text\">Create<\/code>, which I define in the next section.<\/p>\n<p class=\"body\">Restart ASP.NET and request http:\/\/localhost:5000\/users\/list to see the (currently empty) data table, which is shown in figure 38.2.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre412\" src=\"\/images\/proaspnetcore7\/000418.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.2 Enumerating users<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-699\">38.3.3 Creating users<\/h3>\n<p class=\"body\">Add a Razor Page named <code class=\"fm-code-in-text\">Create.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Users<\/code> folder with the content shown in listing 38.12.<a id=\"calibre_link-2905\"><\/a><a id=\"calibre_link-758\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.12 The Create.cshtml file in the Pages\/Users folder of the Advanced project<\/p>\n<pre class=\"programlisting\">@page\n@model CreateModel\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;Create User&lt;\/h5&gt;\n&lt;form method=\"post\"&gt;\n    &lt;div asp-validation-summary=\"All\" class=\"text-danger\"&gt;&lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;User Name&lt;\/label&gt;\n        &lt;input name=\"UserName\" class=\"form-control\"\n               value=\"@Model.UserName\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Email&lt;\/label&gt;\n        &lt;input name=\"Email\" class=\"form-control\"\n               value=\"@Model.Email\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Password&lt;\/label&gt;\n        &lt;input name=\"Password\" class=\"form-control\"\n               value=\"@Model.Password\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"py-2\"&gt;\n        &lt;button type=\"submit\" class=\"btn btn-primary\"&gt;Submit&lt;\/button&gt;\n        &lt;a class=\"btn btn-secondary\" asp-page=\"list\"&gt;Back&lt;\/a&gt;\n    &lt;\/div&gt;\n&lt;\/form&gt;\n\n@functions {\n\n    public class CreateModel : AdminPageModel {\n        public UserManager&lt;IdentityUser&gt; UserManager;\n                \n        public CreateModel(UserManager&lt;IdentityUser&gt; usrManager) {\n            UserManager = usrManager;\n        }\n\n        [BindProperty]\n        public string UserName { get; set; } = string.Empty;\n                \n        [BindProperty]\n        [EmailAddress]\n        public string Email { get; set; } = string.Empty;\n                \n        [BindProperty]\n        public string Password { get; set; } = string.Empty;\n                \n        public async Task&lt;IActionResult&gt; OnPostAsync() {\n            if (ModelState.IsValid) {\n                IdentityUser user =\n                    new IdentityUser { \n                        UserName = UserName, \n                        Email = Email \n                    };\n                IdentityResult result =\n                    await UserManager.CreateAsync(user, Password);\n                if (result.Succeeded) {\n                    return RedirectToPage(\"List\");\n                }\n                foreach (IdentityError err in result.Errors) {\n                    ModelState.AddModelError(\"\", err.Description);\n                }\n            }\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">Even though ASP.NET Core Identity data is stored using Entity Framework Core, you don\u2019t work directly with the database context class. Instead, data is managed through the methods provided by the <code class=\"fm-code-in-text\">UserManager&lt;T&gt;<\/code> class. New users are created using the <code class=\"fm-code-in-text\">CreateAsync<\/code> method, which accepts an <code class=\"fm-code-in-text\">IdentityUser<\/code> object and a password string as arguments.<\/p>\n<p class=\"body\">This Razor Page defines three properties that are subject to model binding. The <code class=\"fm-code-in-text\">UserName<\/code> and <code class=\"fm-code-in-text\">Email<\/code> properties are used to configure the <code class=\"fm-code-in-text\">IdentityUser<\/code> object, which is combined with the value bound to the <code class=\"fm-code-in-text\">Password<\/code> property to call the <code class=\"fm-code-in-text\">CreateAsync<\/code> method. These properties are configured with validation attributes, and values will be required because the property types are non-nullable.<\/p>\n<p class=\"body\">The result of the <code class=\"fm-code-in-text\">CreateAsync<\/code> method is a <code class=\"fm-code-in-text\">Task&lt;IdentityResult&gt;<\/code> object, which indicates the outcome of the create operation, using the properties described in table 38.5.<a id=\"calibre_link-2906\"><\/a><a id=\"calibre_link-767\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 38.5 The properties defined by the IdentityResult class<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2907\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"80%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Succeeded<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Returns <code class=\"fm-code-in-text1\">true<\/code> if the operation succeeded.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Errors<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Returns a sequence of <code class=\"fm-code-in-text1\">IdentityError<\/code> objects that describe the errors encountered while attempting the operation. Each <code class=\"fm-code-in-text1\">IdentityError<\/code> object provides a <code class=\"fm-code-in-text1\">Description<\/code> property that summarizes the problem.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">I inspect the <code class=\"fm-code-in-text\">Succeeded<\/code> property to determine whether a new user has been created in the database. If the <code class=\"fm-code-in-text\">Succeeded<\/code> property is <code class=\"fm-code-in-text\">true<\/code>, then the client is redirected to the <code class=\"fm-code-in-text\">List<\/code> page so that the list of users is displayed, reflecting the new addition.<\/p>\n<pre class=\"programlisting\">...\n<b class=\"fm-bold\">if (result.Succeeded) {<\/b>\n    <b class=\"fm-bold\">return RedirectToPage(\"List\");<\/b>\n<b class=\"fm-bold\">}<\/b>\nforeach (IdentityError err in result.Errors) {\n    ModelState.AddModelError(\"\", err.Description);\n}\n...<\/pre>\n<p class=\"body\">If the <code class=\"fm-code-in-text\">Succeeded<\/code> property is <code class=\"fm-code-in-text\">false<\/code>, then the sequence of <code class=\"fm-code-in-text\">IdentityError<\/code> objects provided by the <code class=\"fm-code-in-text\">Errors<\/code> property is enumerated, with the <code class=\"fm-code-in-text\">Description<\/code> property used to create a model-level validation error using the <code class=\"fm-code-in-text\">ModelState.AddModelError<\/code> method.<\/p>\n<p class=\"body\">To test the ability to create a new user account, restart ASP.NET Core and request http:\/\/localhost:5000\/users\/list. Click the Create button and fill in the form with the values shown in table 38.6.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> There are domains reserved for testing, including <code class=\"fm-code-in-text1\">example.com<\/code>. You can see a complete list at <a class=\"url\" href=\"https:\/\/tools.ietf.org\/html\/rfc2606\">https:\/\/tools.ietf.org\/html\/rfc2606<\/a>.<\/p>\n<p class=\"fm-table-caption\">Table 38.6 The values for creating an example user<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2908\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Field<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Name<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Joe<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Email<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">joe@example.com<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Password<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Secret123$<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Once you have entered the values, click the Submit button. ASP.NET Core Identity will create the user in the database, and the browser will be redirected, as shown in figure 38.3. (You will see a different ID value because IDs are randomly generated for each user.)<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> I used a regular <code class=\"fm-code-in-text1\">input<\/code> element for the <code class=\"fm-code-in-text1\">Password<\/code> field to make it easier to follow the examples in this chapter. For real projects, it is a good idea to set the <code class=\"fm-code-in-text1\">input<\/code> element\u2019s <code class=\"fm-code-in-text1\">type<\/code> attribute to <code class=\"fm-code-in-text1\">password<\/code> so that the characters entered cannot be seen.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre413\" src=\"\/images\/proaspnetcore7\/000419.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.3 Creating a new user<\/p>\n<\/div>\n<p class=\"body\">Click the Create button again and enter the same details into the form, using the values in table 38.6. This time you will see an error reported through the model validation summary when you click the Create button, as shown in figure 38.4. This is an example of an error returned through the <code class=\"fm-code-in-text\">IdentityResult<\/code> object produced by the <code class=\"fm-code-in-text\">CreateAsync<\/code> method.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre414\" src=\"\/images\/proaspnetcore7\/000420.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.4 An error when creating a new user<\/p>\n<\/div>\n<p class=\"fm-head2\">Validating passwords<\/p>\n<p class=\"body\"><a id=\"calibre_link-772\"><\/a>One of the most common requirements, especially for corporate applications, is to enforce a password policy. You can see the default policy by navigating to http:\/\/localhost:5000\/Users\/Create and filling out the form with the data shown in table 38.7.<a id=\"calibre_link-2909\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 38.7 The values for creating an example user<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2910\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Field<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Name<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Alice<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Email<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">alice@example.com<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Password<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">secret<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">When you submit the form, ASP.NET Core Identity checks the candidate password and generates errors if it doesn\u2019t match the password, as shown in figure 38.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre415\" src=\"\/images\/proaspnetcore7\/000421.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.5 Password validation errors<\/p>\n<\/div>\n<p class=\"body\">The password validation rules are configured using the options pattern, as shown in listing 38.13.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.13 Configuring validation in the Program.cs file in the Advanced project<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\n\nusing Microsoft.AspNetCore.Identity;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddServerSideBlazor();\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddSingleton&lt;Advanced.Services.ToggleService&gt;();\n\nbuilder.Services.AddDbContext&lt;IdentityContext&gt;(opts =&gt;\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:IdentityConnection\"]));\nbuilder.Services.AddIdentity&lt;IdentityUser, IdentityRole&gt;()\n    .AddEntityFrameworkStores&lt;IdentityContext&gt;();\n        \n<b class=\"fm-bold\">builder.Services.Configure&lt;IdentityOptions&gt;(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.Password.RequiredLength = 6;<\/b>\n    <b class=\"fm-bold\">opts.Password.RequireNonAlphanumeric = false;<\/b>\n    <b class=\"fm-bold\">opts.Password.RequireLowercase = false;<\/b>\n    <b class=\"fm-bold\">opts.Password.RequireUppercase = false;<\/b>\n    <b class=\"fm-bold\">opts.Password.RequireDigit = false;<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\napp.MapControllers();\napp.MapControllerRoute(\"controllers\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\napp.MapBlazorHub();\napp.MapFallbackToPage(\"\/_Host\");\n\napp.UseBlazorFrameworkFiles(\"\/webassembly\");\napp.MapFallbackToFile(\"\/webassembly\/{*path:nonfile}\",\n     \"\/webassembly\/index.xhtml\");\n         \nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">ASP.NET Core Identity is configured using the <code class=\"fm-code-in-text\">IdentityOptions<\/code> class, whose <code class=\"fm-code-in-text\">Password<\/code> property returns a <code class=\"fm-code-in-text\">PasswordOptions<\/code> class that configures password validation using the properties described in table 38.8.<a id=\"calibre_link-2911\"><\/a><a id=\"calibre_link-2912\"><\/a><a id=\"calibre_link-2913\"><\/a><a id=\"calibre_link-766\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 38.8 The PasswordOptions properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2914\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RequiredLength<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">int<\/code> property is used to specify the minimum length for passwords.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RequireNonAlphanumeric<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Setting this <code class=\"fm-code-in-text1\">bool<\/code> property to <code class=\"fm-code-in-text1\">true<\/code> requires passwords to contain at least one character that is not a letter or a digit.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RequireLowercase<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Setting this <code class=\"fm-code-in-text1\">bool<\/code> property to <code class=\"fm-code-in-text1\">true<\/code> requires passwords to contain at least one lowercase character.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RequireUppercase<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Setting this <code class=\"fm-code-in-text1\">bool<\/code> property to <code class=\"fm-code-in-text1\">true<\/code> requires passwords to contain at least one uppercase character.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RequireDigit<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Setting this <code class=\"fm-code-in-text1\">bool<\/code> property to <code class=\"fm-code-in-text1\">true<\/code> requires passwords to contain at least one numeric character.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">In the listing, I specified that passwords must have a minimum length of six characters and disabled the other constraints. This isn\u2019t something that you should do without careful consideration in a real project, but it allows for an effective demonstration. Restart ASP.NET Core, request http:\/\/localhost:5000\/users\/create, and fill out the form using the details from table 38.7. When you click the Submit button, the password will be accepted by the new validation rules, and a new user will be created, as shown in figure 38.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre416\" src=\"\/images\/proaspnetcore7\/000422.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.6 Changing the password validation rules<\/p>\n<\/div>\n<p class=\"fm-head2\">Validating user details<\/p>\n<p class=\"body\">Validation is also performed on usernames and e-mail addresses when accounts are created. To see how validation is applied, request http:\/\/localhost:5000\/users\/create and fill out the form using the values shown in table 38.9.<a id=\"calibre_link-2915\"><\/a><a id=\"calibre_link-777\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 38.9 The values for creating an example user<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2916\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Field<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Name<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Bob!<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Email<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">alice@example.com<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Password<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">secret<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Click the Submit button, and you will see the error message shown in figure 38.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre417\" src=\"\/images\/proaspnetcore7\/000423.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.7 A user details validation error<\/p>\n<\/div>\n<p class=\"body\">Validation can be configured with the options pattern, using the <code class=\"fm-code-in-text\">User<\/code> property defined by the <code class=\"fm-code-in-text\">IdentityOptions<\/code> class. This class returns a <code class=\"fm-code-in-text\">UserOptions<\/code> class, whose properties are described in table 38.10.<a id=\"calibre_link-2917\"><\/a><a id=\"calibre_link-776\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 38.10 The UserOptions properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2918\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"70%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AllowedUserNameCharacters<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This <code class=\"fm-code-in-text1\">string<\/code> property contains all the legal characters that can be used in a username. The default value specifies a&ndash;z, A&ndash;Z, and 0&ndash;9 and the hyphen, period, underscore, and <code class=\"fm-code-in-text1\">@<\/code> characters. This property is not a regular expression, and every legal character must be specified explicitly in the string.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RequireUniqueEmail<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Setting this <code class=\"fm-code-in-text1\">bool<\/code> property to <code class=\"fm-code-in-text1\">true<\/code> requires new accounts to specify e-mail addresses that have not been used previously.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">In listing 38.14, I have changed the configuration of the application so that unique e-mail addresses are required and so that only lowercase alphabetic characters are allowed in usernames.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.14 Changing validation in the Program.cs file in the Advanced project<\/p>\n<pre class=\"programlisting\">...\nbuilder.Services.Configure&lt;IdentityOptions&gt;(opts =&gt; {\n    opts.Password.RequiredLength = 6;\n    opts.Password.RequireNonAlphanumeric = false;\n    opts.Password.RequireLowercase = false;\n    opts.Password.RequireUppercase = false;\n    opts.Password.RequireDigit = false;\n    <b class=\"fm-bold\">opts.User.RequireUniqueEmail = true;<\/b>\n    <b class=\"fm-bold\">opts.User.AllowedUserNameCharacters = \"abcdefghijklmnopqrstuvwxyz\";<\/b>\n});\n...<\/pre>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000\/users\/create, and fill out the form with the values in table 38.9. Click the Submit button, and you will see that the e-mail address now causes an error. The username still contains illegal characters and is also flagged as an error, as shown in figure 38.8.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre418\" src=\"\/images\/proaspnetcore7\/000424.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.8 Validating user detail<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-700\">38.3.4 Editing users<\/h3>\n<p class=\"body\">To add support for editing users, add a Razor Page named <code class=\"fm-code-in-text\">Editor.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Users<\/code> folder of the Advanced project with the content shown in listing 38.15.<a id=\"calibre_link-2919\"><\/a><a id=\"calibre_link-764\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.15 The Editor.cshtml file in the Pages\/Users folder of the Advanced project<\/p>\n<pre class=\"programlisting\">@page \"{id}\"\n@model EditorModel\n\n&lt;h5 class=\"bg-warning text-white text-center p-2\"&gt;Edit User&lt;\/h5&gt;\n&lt;form method=\"post\"&gt;\n    &lt;div asp-validation-summary=\"All\" class=\"text-danger\"&gt;&lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;ID&lt;\/label&gt;\n        &lt;input name=\"Id\" class=\"form-control\" value=\"@Model.Id\" \n            disabled \/&gt;\n        &lt;input name=\"Id\" type=\"hidden\" value=\"@Model.Id\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;User Name&lt;\/label&gt;\n        &lt;input name=\"UserName\" class=\"form-control\" \n            value=\"@Model.UserName\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Email&lt;\/label&gt;\n        &lt;input name=\"Email\" class=\"form-control\" value=\"@Model.Email\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;New Password&lt;\/label&gt;\n        &lt;input name=\"Password\" class=\"form-control\" \n            value=\"@Model.Password\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"py-2\"&gt;\n        &lt;button type=\"submit\" class=\"btn btn-warning\"&gt;Submit&lt;\/button&gt;\n        &lt;a class=\"btn btn-secondary\" asp-page=\"list\"&gt;Back&lt;\/a&gt;\n    &lt;\/div&gt;\n&lt;\/form&gt;\n\n@functions {\n\n    public class EditorModel : AdminPageModel {\n        public UserManager&lt;IdentityUser&gt; UserManager;\n                \n        public EditorModel(UserManager&lt;IdentityUser&gt; usrManager) {\n            UserManager = usrManager;\n        }\n\n        [BindProperty]\n        public string Id { get; set; } = string.Empty;\n\n        [BindProperty]\n        public string UserName { get; set; } = string.Empty;\n\n        [BindProperty]\n        [EmailAddress]\n        public string Email { get; set; } = string.Empty;\n\n        [BindProperty]\n        public string? Password { get; set; }\n                \n        public async Task OnGetAsync(string id) {\n            IdentityUser? user = await UserManager.FindByIdAsync(id);\n            if (user != null) {\n                Id = user.Id; \n                UserName = user.UserName ?? string.Empty; \n                Email = user.Email ?? string.Empty;\n            }\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync() {\n            if (ModelState.IsValid) {\n                IdentityUser? user = await UserManager.FindByIdAsync(Id);\n                if (user != null) {\n                    user.UserName = UserName;\n                    user.Email = Email;\n                \n                    IdentityResult result = \n                        await UserManager.UpdateAsync(user);\n                    if (result.Succeeded \n                            &amp;&amp; !String.IsNullOrEmpty(Password)) {\n                        await UserManager.RemovePasswordAsync(user);\n                        result = await UserManager.AddPasswordAsync(user, \n                            Password);\n                    }\n                    if (result.Succeeded) {\n                        return RedirectToPage(\"List\");\n                    }\n                    foreach (IdentityError err in result.Errors) {\n                        ModelState.AddModelError(\"\", err.Description);\n                    }\n                }\n            }\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Editor<\/code> page uses the <code class=\"fm-code-in-text\">UserManager&lt;T&gt;.FindByIdAsync<\/code> method to locate the user, querying the database with the <code class=\"fm-code-in-text\">id<\/code> value received through the routing system and received as an argument to the <code class=\"fm-code-in-text\">OnGetAsync<\/code> method. The values from the <code class=\"fm-code-in-text\">IdentityUser<\/code> object returned by the query are used to populate the properties that are displayed by the view part of the page, ensuring that the values are not lost if the page is redisplayed due to validation errors.<\/p>\n<p class=\"body\"><a id=\"calibre_link-755\"><\/a>When the user submits the form, the <code class=\"fm-code-in-text\">FindByIdAsync<\/code> method is used to query the database for the <code class=\"fm-code-in-text\">IdentityUser<\/code> object, which is updated with the <code class=\"fm-code-in-text\">UserName<\/code> and <code class=\"fm-code-in-text\">Email<\/code> values provided in the form. Passwords require a different approach and must be removed from the <code class=\"fm-code-in-text\">user<\/code> object before a new password is assigned, like this:<a id=\"calibre_link-2920\"><\/a><\/p>\n<pre class=\"programlisting\">...\nawait UserManager.<b class=\"fm-bold\">RemovePasswordAsync<\/b>(user);\nresult = await UserManager.<b class=\"fm-bold\">AddPasswordAsync<\/b>(user, Password);\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Editor<\/code> page changes the password only if the form contains a <code class=\"fm-code-in-text\">Password<\/code> value and if the updates for the UserName and Email fields have been successful. Errors from ASP.NET Core Identity are presented as validation messages, and the browser is redirected to the <code class=\"fm-code-in-text\">List<\/code> page after a successful update. Request http:\/\/localhost:5000\/Users\/List, click the Edit button for Joe, and change the UserName field to bob, with all lowercase characters. Click the Submit button, and you will see the change reflected in the list of users, as shown in figure 38.9.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> You will see an error if you click the Edit button for the Alice account and click Submit without making changes. This is because the account was created before the validation policy was changed. ASP.NET Core Identity applies validation checks for updates, leading to the odd situation where the data in the database can be read&mdash;and used&mdash;but cannot be updated.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre419\" src=\"\/images\/proaspnetcore7\/000425.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.9 Editing a user<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-701\">38.3.5 Deleting users<\/h3>\n<p class=\"body\">The last feature I need for my basic user management application is the ability to delete users, as shown in listing 38.16.<a id=\"calibre_link-2921\"><\/a><a id=\"calibre_link-761\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.16 Deleting users in the List.cshtml file in the Pages\/Users folder in the Advanced project<\/p>\n<pre class=\"programlisting\">...\n@functions {\n\n    public class ListModel : AdminPageModel {\n        public UserManager&lt;IdentityUser&gt; UserManager;\n                \n        public ListModel(UserManager&lt;IdentityUser&gt; userManager) {\n            UserManager = userManager;\n        }\n \n        public IEnumerable&lt;IdentityUser&gt; Users { get; set; }\n            = Enumerable.Empty&lt;IdentityUser&gt;();\n                        \n        public void OnGet() {\n            Users = UserManager.Users;\n        }\n \n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; OnPostAsync(string id) {<\/b>\n            <b class=\"fm-bold\">IdentityUser? user = await UserManager.FindByIdAsync(id);<\/b>\n            <b class=\"fm-bold\">if (user != null) {<\/b>\n                <b class=\"fm-bold\">await UserManager.DeleteAsync(user);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return RedirectToPage();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">List<\/code> page already displays a Delete button for each user in the data table, which submits a POST request containing the <code class=\"fm-code-in-text\">Id<\/code> value for the <code class=\"fm-code-in-text\">IdentityUser<\/code> object to be removed. The <code class=\"fm-code-in-text\">OnPostAsync<\/code> method receives the <code class=\"fm-code-in-text\">Id<\/code> value and uses it to query Identity using the <code class=\"fm-code-in-text\">FindByIdAsync<\/code> method, passing the object that is returned to the <code class=\"fm-code-in-text\">DeleteAsync<\/code> method, which deletes it from the database. To check the delete functionality, request http:\/\/localhost:5000\/Users\/List and click Delete for the <code class=\"fm-code-in-text\">Alice<\/code> account. The user object will be removed, as shown in figure 38.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre420\" src=\"\/images\/proaspnetcore7\/000426.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.10 Deleting a user<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-702\">38.4 Creating role management tools<\/h2>\n<p class=\"body\"><a id=\"calibre_link-760\"><\/a>Some applications enforce only two levels of authorization: authenticated users are allowed access to all the application\u2019s features, while unauthenticated users have less&mdash;or no&mdash;access. The SportsStore application in part 1 followed this approach: there was one user, and once authenticated, they had access to all the application\u2019s features, including administration tools, while unauthenticated users were restricted to the public store features.<\/p>\n<p class=\"body\">ASP.NET Core Identity supports <i class=\"fm-italics\">roles<\/i> for applications that require more granular authorization. Users are assigned to one or more roles, and their membership of those roles determines which features are accessible. In the sections that follow, I show you how to build tools to create and manage roles.<\/p>\n<p class=\"body\">Roles are managed through the <code class=\"fm-code-in-text\">RoleManager&lt;T&gt;<\/code> class, where <code class=\"fm-code-in-text\">T<\/code> is the representation of roles in the database. When I configured ASP.NET Core Identity at the start of the chapter, I selected <code class=\"fm-code-in-text\">IdentityRole<\/code>, which is the built-in class that Identity provides to describe a role, which means that I will be using the <code class=\"fm-code-in-text\">RoleManager&lt;IdentityRole&gt;<\/code> class in these examples. The <code class=\"fm-code-in-text\">RoleManager&lt;T&gt;<\/code> class defines the methods and properties shown in table 38.11 that allow roles to be created and managed.<a id=\"calibre_link-2922\"><\/a><a id=\"calibre_link-2923\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 38.11 The members defined by the RoleManager&lt;T&gt; class<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2924\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">CreateAsync(role)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Creates a new role<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">DeleteAsync(role)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Deletes the specified role<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">FindByIdAsync(id)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Finds a role by its ID<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">FindByNameAsync(name)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Finds a role by its name<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RoleExistsAsync(name)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Returns <code class=\"fm-code-in-text1\">true<\/code> if a role with the specified name exists<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">UpdateAsync(role)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Stores changes to the specified role<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Roles<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Returns an enumeration of the roles that have been defined<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Table 38.12 describes the key properties defined by the <code class=\"fm-code-in-text\">IdentityRole<\/code> class.<\/p>\n<p class=\"fm-table-caption\">Table 38.12 Useful IdentityRole properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2925\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Id<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property contains the unique ID for the role.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">Name<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the role name.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Although roles are managed through the <code class=\"fm-code-in-text\">RoleManager&lt;T&gt;<\/code> class, membership of roles is managed through the methods provided by <code class=\"fm-code-in-text\">UserManager&lt;T&gt;<\/code> described in table 38.13.<a id=\"calibre_link-2926\"><\/a><a id=\"calibre_link-768\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 38.13 The UserManager&lt;T&gt; methods for managing role membership<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2927\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">AddToRoleAsync(user, role)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method adds a user to a role.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">RemoveFromRoleAsync(user, role)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method removes a user from a role.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetRolesAsync(user)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns the roles for which the user is a member.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">GetUsersInRoleAsync(role)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns users who are members of the specified role.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">IsInRoleAsync(user, role)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method returns <code class=\"fm-code-in-text1\">true<\/code> if the user is a member of the specified role.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"fm-head1\" id=\"calibre_link-703\">38.4.1 Preparing for role management tools<\/h3>\n<p class=\"body\">To prepare for the role management tools, create the <code class=\"fm-code-in-text\">Pages\/Roles<\/code> folder in the Advanced project and add to it a Razor Layout named <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> with the content shown in listing 38.17.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.17 The _Layout.cshtml file in the Pages\/Roles folder in the Advanced project<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;Identity&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"m-2\"&gt;\n        &lt;h5 class=\"bg-secondary text-white text-center p-2\"&gt;\n            Role Administration\n        &lt;\/h5&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">This layout will ensure there is an obvious difference between the user and role management tools.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-704\">38.4.2 Enumerating and deleting roles<\/h3>\n<p class=\"body\">Add a Razor Page named <code class=\"fm-code-in-text\">List.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Roles<\/code> folder in the Advanced project with the content shown in listing 38.18.<a id=\"calibre_link-2928\"><\/a><a id=\"calibre_link-2929\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.18 The List.cshtml file in the Pages\/Roles folder in the Advanced project<\/p>\n<pre class=\"programlisting\">@page\n@model ListModel\n\n&lt;table class=\"table table-sm table-bordered\"&gt;\n    &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;Members&lt;\/th&gt;&lt;th&gt;&lt;\/th&gt;&lt;\/tr&gt;\n    @if (Model.Roles.Count() == 0) {\n        &lt;tr&gt;&lt;td colspan=\"4\" class=\"text-center\"&gt;No Roles&lt;\/td&gt;&lt;\/tr&gt;\n    } else {\n        foreach (IdentityRole role in Model.Roles) {\n            &lt;tr&gt;\n                &lt;td&gt;@role.Id&lt;\/td&gt;\n                &lt;td&gt;@role.Name&lt;\/td&gt;\n                &lt;td&gt;@(await Model.GetMembersString(role.Name))&lt;\/td&gt;\n                &lt;td class=\"text-center\"&gt;\n                    &lt;form asp-page=\"List\" method=\"post\"&gt;\n                        &lt;input type=\"hidden\" name=\"Id\" value=\"@role.Id\" \/&gt;\n                        &lt;a class=\"btn btn-sm btn-warning\" \n                            asp-page=\"Editor\"\n                            asp-route-id=\"@role.Id\" \n                            asp-route-mode=\"edit\"&gt;Edit&lt;\/a&gt;\n                        &lt;button type=\"submit\" \n                                class=\"btn btn-sm btn-danger\"&gt;\n                            Delete\n                        &lt;\/button&gt;\n                    &lt;\/form&gt;\n                &lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    }\n&lt;\/table&gt;\n&lt;a class=\"btn btn-primary\" asp-page=\"create\"&gt;Create&lt;\/a&gt;\n\n@functions {\n\n    public class ListModel : AdminPageModel {\n        public UserManager&lt;IdentityUser&gt; UserManager;\n        public RoleManager&lt;IdentityRole&gt; RoleManager;\n                \n        public ListModel(UserManager&lt;IdentityUser&gt; userManager,\n                RoleManager&lt;IdentityRole&gt; roleManager) {\n            UserManager = userManager;\n            RoleManager = roleManager;\n        }\n \n        public IEnumerable&lt;IdentityRole&gt; Roles { get; set; }\n            = Enumerable.Empty&lt;IdentityRole&gt;();\n                        \n        public void OnGet() {\n            Roles = RoleManager.Roles;\n        }\n \n        public async Task&lt;string&gt; GetMembersString(string? role) {\n        IEnumerable&lt;IdentityUser&gt; users\n                = (await UserManager.GetUsersInRoleAsync(role!));\n            string result = users.Count() == 0\n                ? \"No members\"\n                : string.Join(\", \", \n                    users.Take(3).Select(u =&gt; u.UserName).ToArray());\n            return users.Count() &gt; 3 \n                ? $\"{result}, (plus others)\" : result;\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync(string id) {\n            IdentityRole? role = await RoleManager.FindByIdAsync(id);\n            if (role != null) {\n                await RoleManager.DeleteAsync(role);\n            }\n            return RedirectToPage();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The roles are enumerated, along with the names of up to three of the role members or a placeholder message if there are no members. There is also a Create button, and each role is presented with Edit and Delete buttons, following the same pattern I used for the user management tools.<\/p>\n<p class=\"body\">The Delete button sends a POST request back to the Razor Page. The <code class=\"fm-code-in-text\">OnPostAsync<\/code> method uses the <code class=\"fm-code-in-text\">FindByIdAsync<\/code> method to retrieve the role object, which is passed to the <code class=\"fm-code-in-text\">DeleteAsync<\/code> method to remove it from the database.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-705\">38.4.3 Creating roles<\/h3>\n<p class=\"body\">Add a Razor Page named <code class=\"fm-code-in-text\">Create.cshtml<\/code> in the <code class=\"fm-code-in-text\">Pages\/Roles<\/code> folder in the Advanced project with the contents shown in listing 38.19.<a id=\"calibre_link-2930\"><\/a><a id=\"calibre_link-757\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.19 The Create.cshtml file in the Pages\/Roles folder in the Advanced project<\/p>\n<pre class=\"programlisting\">@page\n@model CreateModel\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;Create Role&lt;\/h5&gt;\n&lt;form method=\"post\"&gt;\n    &lt;div asp-validation-summary=\"All\" class=\"text-danger\"&gt;&lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Role Name&lt;\/label&gt;\n        &lt;input name=\"Name\" class=\"form-control\" value=\"@Model.Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"py-2\"&gt;\n        &lt;button type=\"submit\" class=\"btn btn-primary\"&gt;Submit&lt;\/button&gt;\n        &lt;a class=\"btn btn-secondary\" asp-page=\"list\"&gt;Back&lt;\/a&gt;\n    &lt;\/div&gt;\n&lt;\/form&gt;\n\n@functions {\n\n    public class CreateModel : AdminPageModel {\n        public RoleManager&lt;IdentityRole&gt; RoleManager;\n                \n        public CreateModel(UserManager&lt;IdentityUser&gt; userManager,\n                RoleManager&lt;IdentityRole&gt; roleManager) {\n            RoleManager = roleManager;\n        }\n\n        [BindProperty]\n        public string Name { get; set; } = string.Empty;\n                \n        public async Task&lt;IActionResult&gt; OnPostAsync() {\n            if (ModelState.IsValid) {\n                IdentityRole role = \n                    new IdentityRole { Name = Name };\n                IdentityResult result = \n                    await RoleManager.CreateAsync(role);\n                if (result.Succeeded) {\n                    return RedirectToPage(\"List\");\n                }\n                foreach (IdentityError err in result.Errors) {\n                    ModelState.AddModelError(\"\", err.Description);\n                }\n            }\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The user is presented with a form containing an <code class=\"fm-code-in-text\">input<\/code> element to specify the name of the new role. When the form is submitted, the <code class=\"fm-code-in-text\">OnPostAsync<\/code> method creates a new <code class=\"fm-code-in-text\">IdentityRole<\/code> object and passes it to the <code class=\"fm-code-in-text\">CreateAsync<\/code> method.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-706\">38.4.4 Assigning role membership<\/h3>\n<p class=\"body\">To add support for managing role memberships, add a Razor Page named <code class=\"fm-code-in-text\">Editor.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Roles<\/code> folder in the Advanced project, with the content shown in listing 38.20.<a id=\"calibre_link-2931\"><\/a><a id=\"calibre_link-2932\"><\/a><a id=\"calibre_link-763\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 38.20 The Editor.cshtml file in the Pages\/Roles folder in the Advanced project<\/p>\n<pre class=\"programlisting\">@page \"{id}\"\n@model EditorModel\n\n&lt;h5 class=\"bg-primary text-white text-center p-2\"&gt;\n    Edit Role: @Model.Role?.Name\n&lt;\/h5&gt;\n\n&lt;form method=\"post\"&gt;\n    &lt;input type=\"hidden\" name=\"rolename\" value=\"@Model.Role?.Name\" \/&gt;\n    &lt;div asp-validation-summary=\"All\" class=\"text-danger\"&gt;&lt;\/div&gt;\n    &lt;h5 class=\"bg-secondary text-white p-2\"&gt;Members&lt;\/h5&gt;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;thead&gt;&lt;tr&gt;&lt;th&gt;User&lt;\/th&gt;&lt;th&gt;Email&lt;\/th&gt;&lt;th&gt;&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n        &lt;tbody&gt;\n            @if ((await Model.Members()).Count() == 0) {\n                &lt;tr&gt;\n                    &lt;td colspan=\"3\" class=\"text-center\"&gt;No members&lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n            @foreach (IdentityUser user in await Model.Members()) {\n                &lt;tr&gt;\n                    &lt;td&gt;@user.UserName&lt;\/td&gt;\n                    &lt;td&gt;@user.Email&lt;\/td&gt;\n                    &lt;td&gt;\n                        &lt;button asp-route-userid=\"@user.Id\"\n                            class=\"btn btn-primary btn-sm\" type=\"submit\"&gt;\n                            Change\n                        &lt;\/button&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n        \n    &lt;h5 class=\"bg-secondary text-white p-2\"&gt;Non-Members&lt;\/h5&gt;\n    &lt;table class=\"table table-sm table-striped table-bordered\"&gt;\n        &lt;thead&gt;&lt;tr&gt;&lt;th&gt;User&lt;\/th&gt;&lt;th&gt;Email&lt;\/th&gt;&lt;th&gt;&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\n        &lt;tbody&gt;\n            @if ((await Model.NonMembers()).Count() == 0) {\n                &lt;tr&gt;\n                    &lt;td colspan=\"3\" class=\"text-center\"&gt;\n                        No non-members\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n            @foreach (IdentityUser user in await Model.NonMembers()) {\n                &lt;tr&gt;\n                    &lt;td&gt;@user.UserName&lt;\/td&gt;\n                    &lt;td&gt;@user.Email&lt;\/td&gt;\n                    &lt;td&gt;\n                        &lt;button asp-route-userid=\"@user.Id\"\n                            class=\"btn btn-primary btn-sm\" type=\"submit\"&gt;\n                            Change\n                        &lt;\/button&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            }\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n&lt;\/form&gt;\n\n&lt;a class=\"btn btn-secondary\" asp-page=\"list\"&gt;Back&lt;\/a&gt;\n\n@functions {\n\n    public class EditorModel : AdminPageModel {\n        public UserManager&lt;IdentityUser&gt; UserManager;\n        public RoleManager&lt;IdentityRole&gt; RoleManager;\n                \n        public EditorModel(UserManager&lt;IdentityUser&gt; userManager,\n                RoleManager&lt;IdentityRole&gt; roleManager) {\n            UserManager = userManager;\n            RoleManager = roleManager;\n        }\n \n        public IdentityRole? Role { get; set; } = new();\n                \n        public Task&lt;IList&lt;IdentityUser&gt;&gt; Members() {\n            if (Role?.Name != null) {\n                return UserManager.GetUsersInRoleAsync(Role.Name);\n            }\n            \n            return Task.FromResult(new List&lt;IdentityUser&gt;() \n                as IList&lt;IdentityUser&gt;);\n        }\n \n        public async Task&lt;IEnumerable&lt;IdentityUser&gt;&gt; NonMembers() =&gt;\n                UserManager.Users.ToList().Except(await Members());\n                                \n        public async Task OnGetAsync(string id) {\n            Role = await RoleManager.FindByIdAsync(id);\n        }\n \n        public async Task&lt;IActionResult&gt; OnPostAsync(string userid,\n                string rolename) {\n            Role = await RoleManager.FindByNameAsync(rolename);\n            IdentityUser? user = await UserManager.FindByIdAsync(userid);\n            if (user != null) {\n                IdentityResult result;\n                if (await UserManager.IsInRoleAsync(user, rolename)) {\n                    result = await UserManager.RemoveFromRoleAsync(user, \n                        rolename);\n                } else {\n                    result = \n                        await UserManager.AddToRoleAsync(user, rolename);\n                }\n                if (result.Succeeded) {\n                    return RedirectToPage();\n                } else {\n                    foreach (IdentityError err in result.Errors) {\n                        ModelState.AddModelError(\"\", err.Description);\n                    }\n                    return Page();\n                }\n            }\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">The user is presented with a table showing the users who are members of the role and with a table showing nonmembers. Each row contains a Change button that submits the form. The <code class=\"fm-code-in-text\">OnPostAsync<\/code> method uses the <code class=\"fm-code-in-text\">UserManager.FindByIdAsync<\/code> method to retrieve the user object from the database. The <code class=\"fm-code-in-text\">IsInRoleAsync<\/code> method is used to determine whether the user is a member of the role, and the <code class=\"fm-code-in-text\">AddToRoleAsync<\/code> and <code class=\"fm-code-in-text\">RemoveFromRoleAsync<\/code> methods are used to add and remove the user, respectively.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/roles\/list. The list will be empty because there are no roles in the database. Click the Create button, enter Admins into the text field, and click the Submit button to create a new role. Once the role has been created, click the Edit button, and you will see the list of users who can be added to the role. Clicking the Change button will move the user in and out of the role. Click back, and the list will be updated to show the users who are members of the role, as shown in figure 38.11.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> ASP.NET Core Identity revalidates user details when changing role assignments, which will result in an error if you try to modify a user whose details do not match the current restrictions, which happens when restrictions are introduced after the application has been deployed and the database is already populated with users created under the old roles. It is for this reason that the Razor Page in listing 38.20 checks the result from the operations to add or remove users from a role and displays any errors as validation messages.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre421\" src=\"\/images\/proaspnetcore7\/000427.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 38.11 Managing roles<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2933\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">The ASP.NET Core Identity framework is used to authenticate users.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">User data can be stored in an relational database, but there are other authentication mechanisms available, although some of them can be complex to set up.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">User identities are managed and authenticated using the <code class=\"fm-code-in-text\">UserManager&lt;T&gt;<\/code> class, where <code class=\"fm-code-in-text\">T<\/code> is the class used to represent users in the application.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Roles are managed and applied using the <code class=\"fm-code-in-text\">RoleManager&lt;T&gt;<\/code> class, where <code class=\"fm-code-in-text\">T<\/code> is the class used to represent roles in the application.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Account administration tools can be created using the standard ASP.NET Core features.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-707\">\n<div class=\"calibre1\" id=\"calibre_link-2934\">\n<h1 class=\"tochead\" id=\"calibre_link-2935\">39 Applying ASP.NET Core Identity<\/h1>\n<p class=\"co-summary-head\">This chapter covers<a id=\"calibre_link-2936\"><\/a><a id=\"calibre_link-2937\"><\/a><\/p>\n<ul class=\"calibre6\">\n<li class=\"co-summary-bullet\">Authenticating users with ASP.NET Core Identity<\/li>\n<li class=\"co-summary-bullet\">Implementing and enforcing an authorization policy<\/li>\n<li class=\"co-summary-bullet\">Using bearer tokens instead of authentication cookies for web services or JavaScript clients<\/li>\n<\/ul>\n<p class=\"body\">In this chapter, I explain how ASP.NET Core Identity is applied to authenticate users and authorize access to application features. I create the features required for users to establish their identity, explain how access to endpoints can be controlled, and demonstrate the security features that Blazor provides. I also show two different ways to authenticate web service clients. Table 39.1 provides a guide to the chapter.<\/p>\n<p class=\"fm-table-caption\">Table 39.1 Chapter guide<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2938\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"30%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"50%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"20%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Problem<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Solution<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Listing<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Authenticating users<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">SignInManager&lt;T&gt;<\/code> class to validate the credentials users provide and use the built-in middleware to trigger authentication.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">3&ndash;8<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Restricting access to endpoints<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">Authorize<\/code> attribute and the built-in middleware to control access.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">9&ndash;13<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Restricting access to Blazor components<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use the <code class=\"fm-code-in-text1\">Authorize<\/code> attribute and the built-in Razor Components to control access.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">14&ndash;17<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Restricting access to web services<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Use cookie authentication or bearer tokens.<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">18&ndash;30<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 class=\"fm-head\" id=\"calibre_link-708\">39.1 Preparing for this chapter<\/h2>\n<p class=\"body\">This chapter uses the projects from chapter 38. To prepare for this chapter, I am going to reset both the application data and ASP.NET Core Identity databases and create new users and roles. Open a new command prompt and run the commands shown in listing 39.1 in the <code class=\"fm-code-in-text\">Advanced<\/code> project folder, which contains the <code class=\"fm-code-in-text\">Advanced.csproj<\/code> file. These commands remove the existing databases and re-create them.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can download the example project for this chapter&mdash;and for all the other chapters in this book&mdash;from <a class=\"url\" href=\"https:\/\/github.com\/manningbooks\/pro-asp.net-core-7\">https:\/\/github.com\/manningbooks\/pro-asp.net-core-7<\/a>. See chapter 1 for how to get help if you have problems running the examples.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.1 Re-creating the project databases<\/p>\n<pre class=\"programlisting\">dotnet ef database drop --force --context DataContext\ndotnet ef database drop --force --context IdentityContext\ndotnet ef database update --context DataContext\ndotnet ef database update --context IdentityContext<\/pre>\n<p class=\"body\">Now that the application contains multiple database context classes, the Entity Framework Core commands require the <code class=\"fm-code-in-text\">--context<\/code> argument to select the context that a command applies to. Use the PowerShell command prompt to run the command shown in listing 39.2.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.2 Running the example application<\/p>\n<pre class=\"programlisting\">dotnet run<\/pre>\n<p class=\"body\">Use a browser to request http:\/\/localhost:5000\/controllers, which will produce the response shown in figure 39.1.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre422\" src=\"\/images\/proaspnetcore7\/000428.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.1 Running the example application<\/p>\n<\/div>\n<p class=\"body\">The main application database is automatically reseeded when the application starts. There is no seed data for the ASP.NET Core Identity database. Request http:\/\/localhost:5000\/users\/list and http:\/\/localhost:5000\/roles\/list, and you will see the responses in figure 39.2, which show the database is empty.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre423\" src=\"\/images\/proaspnetcore7\/000429.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.2 The empty ASP.NET Core Identity database<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-709\">39.2 Authenticating users<\/h2>\n<p class=\"body\">In the sections that follow, I show you how to add authentication features to the example project so that users can present their credentials and establish their identity to the application.<a id=\"calibre_link-2939\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Authentication vs. authorization<\/p>\n<p class=\"fm-sidebar-text\"><a id=\"calibre_link-746\"><\/a>It is important to understand the difference between authentication and authorization when working with ASP.NET Core Identity. <i class=\"fm-italics\">Authentication<\/i>, often referred to as <i class=\"fm-italics\">AuthN<\/i>, is the process of establishing the identity of a user, which the user does by presenting their credentials to the application. In the case of the example application, those credentials are a username and a password. The username is public information, but the password is known only by the user, and when the correct password is presented, the application is able to authenticate the user.<a id=\"calibre_link-2940\"><\/a><a id=\"calibre_link-2941\"><\/a><a id=\"calibre_link-2942\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><i class=\"fm-italics\">Authorization<\/i>, often referred to as <i class=\"fm-italics\">AuthZ<\/i>, is the process of granting access to application features based on a user\u2019s identity. Authorization can be performed only when a user has been authenticated because an application has to know the identity of a user before deciding whether they are entitled to use a specific feature.<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-710\">39.2.1 Creating the login feature<\/h3>\n<p class=\"body\">To enforce a security policy, the application must allow users to authenticate themselves, which is done using the ASP.NET Core Identity API. Create the <code class=\"fm-code-in-text\">Pages\/Account<\/code> folder and add to it a Razor layout named <code class=\"fm-code-in-text\">_Layout.cshtml<\/code> with the content shown in listing 39.3. This layout will provide common content for authentication features.<a id=\"calibre_link-2943\"><\/a><a id=\"calibre_link-2944\"><\/a><a id=\"calibre_link-749\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.3 The _Layout.cshtml file in the Pages\/Account folder in the Advanced project<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;Identity&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        @RenderBody()\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Add a Razor Page named <code class=\"fm-code-in-text\">Login.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Account<\/code> folder in the Advanced project with the content shown in listing 39.4.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.4 The Login.cshtml file in the Pages\/Account folder of the Advanced project<\/p>\n<pre class=\"programlisting\">@page\n@model LoginModel\n\n&lt;div class=\"bg-primary text-center text-white p-2\"&gt;&lt;h4&gt;Log In&lt;\/h4&gt;&lt;\/div&gt;\n\n&lt;div class=\"m-1 text-danger\" asp-validation-summary=\"All\"&gt;&lt;\/div&gt;\n\n&lt;form method=\"post\"&gt;\n    &lt;input type=\"hidden\" name=\"returnUrl\" value=\"@Model.ReturnUrl\" \/&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;UserName&lt;\/label&gt;\n        &lt;input class=\"form-control\" asp-for=\"UserName\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;Password&lt;\/label&gt;\n        &lt;input asp-for=\"Password\" type=\"password\" class=\"form-control\" \/&gt;\n    &lt;\/div&gt;\n    &lt;button class=\"btn btn-primary mt-2\" type=\"submit\"&gt;Log In&lt;\/button&gt;\n&lt;\/form&gt;\n\n@functions {\n\n    public class LoginModel : PageModel {\n        private SignInManager&lt;IdentityUser&gt; signInManager;\n                \n        public LoginModel(SignInManager&lt;IdentityUser&gt; signinMgr) {\n            signInManager = signinMgr;\n        }\n\n        [BindProperty]\n        public string UserName { get; set; } = string.Empty;\n\n        [BindProperty]\n        public string Password { get; set; } = string.Empty;\n                \n        [BindProperty(SupportsGet = true)]\n        public string? ReturnUrl { get; set; }\n                \n        public async Task&lt;IActionResult&gt; OnPostAsync() {\n            if (ModelState.IsValid) {\n                Microsoft.AspNetCore.Identity.SignInResult result =\n                    await signInManager.PasswordSignInAsync(UserName, \n                        Password, false, false);\n                if (result.Succeeded) {\n                    return Redirect(ReturnUrl ?? \"\/\");\n                }\n                ModelState.AddModelError(\"\", \n                    \"Invalid username or password\");\n            }\n            return Page();\n        }\n    }\n}<\/pre>\n<p class=\"body\">ASP.NET Core Identity provides the <code class=\"fm-code-in-text\">SigninManager&lt;T&gt;<\/code> class to manage logins, where the generic type argument <code class=\"fm-code-in-text\">T<\/code> is the class that represents users in the application, which is <code class=\"fm-code-in-text\">IdentityUser<\/code> for the example application. Table 39.2 describes the <code class=\"fm-code-in-text\">SigninManager&lt;T&gt;<\/code> members I use in this chapter.<a id=\"calibre_link-2945\"><\/a><a id=\"calibre_link-774\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 39.2 Useful SigninManager&lt;T&gt; members<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2946\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">PasswordSignInAsync(name, password, persist, lockout)<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method attempts authentication using the specified username and password. The <code class=\"fm-code-in-text1\">persist<\/code> argument determines whether a successful authentication produces a cookie that persists after the browser is closed. The <code class=\"fm-code-in-text1\">lockout<\/code> argument determines whether the account should be locked if authentication fails.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">SignOutAsync()<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This method signs out the user.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The Razor Page presents the user with a form that collects a username and a password, which are used to perform authentication with the <code class=\"fm-code-in-text\">PasswordSignInAsync<\/code> method, like this:<\/p>\n<pre class=\"programlisting\">...\nMicrosoft.AspNetCore.Identity.SignInResult result =\n    await signInManager.<b class=\"fm-bold\">PasswordSignInAsync<\/b>(UserName,\n        Password, false, false);\n...<\/pre>\n<p class=\"body\">The result from the <code class=\"fm-code-in-text\">PasswordSignInAsync<\/code> methods is a <code class=\"fm-code-in-text\">SignInResult<\/code> object, which defines a <code class=\"fm-code-in-text\">Suceeded<\/code> property that is <code class=\"fm-code-in-text\">true<\/code> if the authentication is successful. (There is also a <code class=\"fm-code-in-text\">SignInResult<\/code> class defined in the <code class=\"fm-code-in-text\">Microsoft.AspNetCore.Mvc<\/code> namespace, which is why I used a fully qualified class name in the listing.)<\/p>\n<p class=\"body\">Authentication in an ASP.NET Core application is usually triggered when the user tries to access an endpoint that requires authorization, and it is convention to return the user to that endpoint if authentication is successful, which is why the <code class=\"fm-code-in-text\">Login<\/code> page defines a <code class=\"fm-code-in-text\">ReturnUrl<\/code> property that is used in a redirection if the user has provided valid credentials.<\/p>\n<pre class=\"programlisting\">...\nif (result.Succeeded) {\n    return Redirect(<b class=\"fm-bold\">ReturnUrl<\/b> ?? \"\/\");\n}\n...<\/pre>\n<p class=\"body\">If the user hasn\u2019t provided valid credentials, then a validation message is shown, and the page is redisplayed.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Protecting the authentication cookie<\/p>\n<p class=\"fm-sidebar-text\">The authentication cookie contains the user\u2019s identity, and ASP.NET Core trusts that requests containing the cookie originate from the authenticated user. This means you should use HTTPS for production applications that use ASP.NET Core Identity to prevent the cookie from being intercepted by an intermediary. See part 2 for details of enabling HTTPS in ASP.NET Core.<a id=\"calibre_link-2947\"><\/a><a id=\"calibre_link-747\"><\/a><\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-711\">39.2.2 Inspecting the ASP.NET Core Identity cookie<\/h3>\n<p class=\"body\">When a user is authenticated, a cookie is added to the response so that subsequent requests can be identified as being already authenticated. Add a Razor Page named <code class=\"fm-code-in-text\">Details.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Account<\/code> folder of the Advanced project with the content shown in listing 39.5, which displays the cookie when it is present.<a id=\"calibre_link-2948\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.5 The Details.cshtml file in the Pages\/Account folder of the Advanced folder<\/p>\n<pre class=\"programlisting\">@page\n@model DetailsModel\n\n&lt;table class=\"table table-sm table-bordered\"&gt;\n    &lt;tbody&gt;\n        @if (Model.Cookie == null) {\n            &lt;tr&gt;&lt;th class=\"text-center\"&gt;No Identity Cookie&lt;\/th&gt;&lt;\/tr&gt;\n        } else {\n            &lt;tr&gt;\n                &lt;th&gt;Cookie&lt;\/th&gt;\n                &lt;td class=\"text-break\"&gt;@Model.Cookie&lt;\/td&gt;\n            &lt;\/tr&gt;\n        }\n    &lt;\/tbody&gt;    \n&lt;\/table&gt;\n\n@functions {\n\n    public class DetailsModel : PageModel {\n        \n        public string? Cookie { get; set; }\n                \n        public void OnGet() {\n            Cookie = Request.Cookies[\".AspNetCore.Identity.Application\"];\n        }\n    }\n}<\/pre>\n<p class=\"body\">The name used for the ASP.NET Core Identity cookie is <code class=\"fm-code-in-text\">.AspNetCore.Identity.Application<\/code>, and this page retrieves the cookie from the request and displays its value or a placeholder message if there is no cookie.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-712\">39.2.3 Creating a Sign-Out page<\/h3>\n<p class=\"body\">It is important to give users the ability to sign out so they can explicitly delete the cookie, especially if public machines may be used to access the application. Add a Razor Page named <code class=\"fm-code-in-text\">Logout.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Account<\/code> folder of the <code class=\"fm-code-in-text\">Advanced<\/code> folder with the content shown in listing 39.6.<a id=\"calibre_link-2949\"><\/a><a id=\"calibre_link-770\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.6 The Logout.cshtml file in the Pages\/Account folder in the Advanced project<\/p>\n<pre class=\"programlisting\">@page\n@model LogoutModel\n\n&lt;div class=\"bg-primary text-center text-white p-2\"&gt;&lt;h4&gt;Log Out&lt;\/h4&gt;&lt;\/div&gt;\n&lt;div class=\"m-2\"&gt;\n    &lt;h6&gt;You are logged out&lt;\/h6&gt;\n    &lt;a asp-page=\"Login\" class=\"btn btn-secondary\"&gt;OK&lt;\/a&gt;\n&lt;\/div&gt;\n\n@functions {\n\n    public class LogoutModel : PageModel {\n        private SignInManager&lt;IdentityUser&gt; signInManager;\n                \n        public LogoutModel(SignInManager&lt;IdentityUser&gt; signInMgr) {\n            signInManager = signInMgr;\n        }\n \n        public async Task OnGetAsync() {\n            await signInManager.SignOutAsync();\n        }\n    }\n}<\/pre>\n<p class=\"body\">This page calls the <code class=\"fm-code-in-text\">SignOutAsync<\/code> method described in table 39.2 to sign the application out of the application. The ASP.NET Core Identity cookie will be deleted so that the browser will not include it in future requests (and invalidated the cookie so that requests will not be treated as authenticated even if the cookie is used again anyway).<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-713\">39.2.4 Testing the authentication feature<\/h3>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/users\/list. Click the Create button and fill out the form using the data shown in table 39.3. Click the Submit button to submit the form and create the user account.<\/p>\n<p class=\"fm-table-caption\">Table 39.3 The data values to create a user<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2950\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Field<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">UserName<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">bob<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Email<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">bob@example.com<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">Password<\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">secret<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">Navigate to http:\/\/localhost:5000\/account\/login and authenticate using the username and password from table 39.3. No return URL has been specified, and you will be redirected to the root URL once you have been authenticated. Request http:\/\/localhost:5000\/account\/details, and you will see the ASP.NET Core Identity cookie. Request http:\/\/localhost:5000\/account\/logout to log out of the application and return to http:\/\/localhost:5000\/account\/details to confirm that the cookie has been deleted, as shown in figure 39.3.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre424\" src=\"\/images\/proaspnetcore7\/000430.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.3 Authenticating a user<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-714\">39.2.5 Enabling the Identity authentication middleware<\/h3>\n<p class=\"body\">ASP.NET Core Identity provides a middleware component that detects the cookie created by the <code class=\"fm-code-in-text\">SignInManager&lt;T&gt;<\/code> class and populates the <code class=\"fm-code-in-text\">HttpContext<\/code> object with details of the authenticated user. This provides endpoints with details about the user without needing to be aware of the authentication process or having to deal directly with the cookie created by the authentication process. Listing 39.7 adds the authentication middleware to the example application\u2019s request pipeline.<a id=\"calibre_link-2951\"><\/a><a id=\"calibre_link-748\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.7 Enabling middleware in the Program.cs file in the Advanced folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Identity;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddServerSideBlazor();\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddSingleton&lt;Advanced.Services.ToggleService&gt;();\n\nbuilder.Services.AddDbContext&lt;IdentityContext&gt;(opts =&gt;\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:IdentityConnection\"]));\nbuilder.Services.AddIdentity&lt;IdentityUser, IdentityRole&gt;()\n    .AddEntityFrameworkStores&lt;IdentityContext&gt;();\n        \nbuilder.Services.Configure&lt;IdentityOptions&gt;(opts =&gt; {\n    opts.Password.RequiredLength = 6;\n    opts.Password.RequireNonAlphanumeric = false;\n    opts.Password.RequireLowercase = false;\n    opts.Password.RequireUppercase = false;\n    opts.Password.RequireDigit = false;\n    opts.User.RequireUniqueEmail = true;\n    opts.User.AllowedUserNameCharacters = \"abcdefghijklmnopqrstuvwxyz\";\n});\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\n<b class=\"fm-bold\">app.UseAuthentication();<\/b>\n\napp.MapControllers();\napp.MapControllerRoute(\"controllers\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\napp.MapBlazorHub();\napp.MapFallbackToPage(\"\/_Host\");\n\napp.UseBlazorFrameworkFiles(\"\/webassembly\");\napp.MapFallbackToFile(\"\/webassembly\/{*path:nonfile}\", \n    \"\/webassembly\/index.xhtml\");\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\napp.Run();<\/pre>\n<p class=\"body\">The middleware sets the value of the <code class=\"fm-code-in-text\">HttpContext.User<\/code> property to a <code class=\"fm-code-in-text\">ClaimsPrincipal<\/code> object. <i class=\"fm-italics\">Claims<\/i> are pieces of information about a user and details of the source of that information, providing a general-purpose approach to describing the information known about a user.<\/p>\n<p class=\"body\">The <code class=\"fm-code-in-text\">ClaimsPrincipal<\/code> class is part of .NET Core and isn\u2019t directly useful in most ASP.NET Core applications, but there are two nested properties that are useful in most applications, as described in table 39.4.<a id=\"calibre_link-2952\"><\/a><a id=\"calibre_link-756\"><\/a><\/p>\n<p class=\"fm-table-caption\">Table 39.4 Useful Nested ClaimsPrincipal Properties<\/p>\n<table border=\"1\" class=\"contenttable-1-table\" id=\"calibre_link-2953\" width=\"100%\">\n<colgroup class=\"contenttable-0-colgroup\">\n<col class=\"contenttable-0-col\" span=\"1\" width=\"40%\"><\/col>\n<col class=\"contenttable-0-col\" span=\"1\" width=\"60%\"><\/col>\n<\/colgroup>\n<thead class=\"contenttable-1-thead\">\n<tr class=\"contenttable-0-tr\">\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Name<\/p>\n<\/th>\n<th class=\"contenttable-1-th\">\n<p class=\"fm-table-head\">Description<\/p>\n<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"contenttable-1-tbody\">\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ClaimsPrincipal.Identity.Name<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns the username, which will be <code class=\"fm-code-in-text1\">null<\/code> if there is no user associated with the request.<\/p>\n<\/td>\n<\/tr>\n<tr class=\"contenttable-0-tr\">\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\"><code class=\"fm-code-in-text1\">ClaimsPrincipal.Identity.IsAuthenticated<\/code><\/p>\n<\/td>\n<td class=\"contenttable-1-td\">\n<p class=\"fm-table-body\">This property returns <code class=\"fm-code-in-text1\">true<\/code> if the user associated with the request has been authenticated.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"body\">The username provided through the <code class=\"fm-code-in-text\">ClaimsPrincipal<\/code> object can be used to obtain the ASP.NET Core Identity user object, as shown in listing 39.8.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.8 User details in the Details.cshtml file in the Pages\/Account folder of the Advanced project<\/p>\n<pre class=\"programlisting\">@page\n@model DetailsModel\n\n<b class=\"fm-bold\">&lt;table class=\"table table-sm table-bordered\"&gt;<\/b>\n    <b class=\"fm-bold\">&lt;tbody&gt;<\/b>\n        <b class=\"fm-bold\">@if (Model.IdentityUser == null) {<\/b>\n            <b class=\"fm-bold\">&lt;tr&gt;&lt;th class=\"text-center\"&gt;No User&lt;\/th&gt;&lt;\/tr&gt;<\/b>\n        <b class=\"fm-bold\">} else {<\/b>\n            <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;td&gt;@Model.IdentityUser.UserName&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n            <b class=\"fm-bold\">&lt;tr&gt;&lt;th&gt;Email&lt;\/th&gt;&lt;td&gt;@Model.IdentityUser.Email&lt;\/td&gt;&lt;\/tr&gt;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">&lt;\/tbody&gt;<\/b>    \n<b class=\"fm-bold\">&lt;\/table&gt;<\/b>\n\n@functions {\n\n    public class DetailsModel : PageModel {\n        <b class=\"fm-bold\">private UserManager&lt;IdentityUser&gt; userManager;<\/b>\n                \n        <b class=\"fm-bold\">public DetailsModel(UserManager&lt;IdentityUser&gt; manager) {<\/b>\n            <b class=\"fm-bold\">userManager = manager;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">public IdentityUser? IdentityUser { get; set; }<\/b>\n                \n        <b class=\"fm-bold\">public async Task OnGetAsync() {<\/b>\n            <b class=\"fm-bold\">if (User.Identity != null<\/b> \n                    <b class=\"fm-bold\">&amp;&amp; User.Identity.Name != null<\/b>\n                    <b class=\"fm-bold\">&amp;&amp; User.Identity.IsAuthenticated) {<\/b>\n                <b class=\"fm-bold\">IdentityUser = await<\/b> \n                    <b class=\"fm-bold\">userManager.FindByNameAsync(User.Identity.Name);<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">HttpContext.User<\/code> property can be accessed through the <code class=\"fm-code-in-text\">User<\/code> convenience property defined by the <code class=\"fm-code-in-text\">PageModel<\/code> and <code class=\"fm-code-in-text\">ControllerBase<\/code> classes. This Razor Page confirms that there is an authenticated user associated with the request and gets the <code class=\"fm-code-in-text\">IdentityUser<\/code> object that describes the user.<\/p>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000\/account\/login, and authenticate using the details in table 39.3. Request http:\/\/localhost:5000\/account\/details, and you will see how the ASP.NET Core Identity middleware enabled in listing 39.7 has processed the cookie to associate user details with the request, as shown in figure 39.4.<\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Considering two-factor authentication<\/p>\n<p class=\"fm-sidebar-text\">I have performed single-factor authentication in this chapter, which is where the user authenticates using a single piece of information known to them in advance: the password.<a id=\"calibre_link-2954\"><\/a><a id=\"calibre_link-775\"><\/a><\/p>\n<p class=\"fm-sidebar-text\">ASP.NET Core Identity also supports two-factor authentication, where the user needs something extra, usually something that is given to the user at the moment they want to authenticate. The most common examples are a value from a hardware token or smartphone app or an authentication code that is sent as an email or text message. (Strictly speaking, the two factors can be anything, including fingerprints, iris scans, and voice recognition, although these are options that are rarely required for most web applications.)<\/p>\n<p class=\"fm-sidebar-text\">Security is increased because an attacker needs to know the user\u2019s password <i class=\"fm-italics\">and<\/i> have access to whatever provides the second factor, such as an e-mail account or cell phone.<\/p>\n<p class=\"fm-sidebar-text\">I don\u2019t show two-factor authentication in the book for two reasons. The first is that it requires a lot of preparatory work, such as setting up the infrastructure that distributes the second-factor e-mails and texts and implementing the validation logic, all of which is beyond the scope of this book.<\/p>\n<p class=\"fm-sidebar-text\">The second reason is that two-factor authentication forces the user to remember to jump through an additional hoop to authenticate, such as remembering their phone or keeping a security token nearby, something that isn\u2019t always appropriate for web applications. I carried a hardware token of one sort or another for more than a decade in various jobs, and I lost count of the number of times that I couldn\u2019t log in to an employer\u2019s system because I left the token at home. If you are considering two-factor authentication, then I recommend using one of the many hosted providers that will take care of distributing and managing the second factors for you.<\/p>\n<\/div>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre425\" src=\"\/images\/proaspnetcore7\/000431.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.4 Getting details of an authenticated user<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-715\">39.3 Authorizing access to endpoints<\/h2>\n<p class=\"body\">Once an application has an authentication feature, user identities can be used to restrict access to endpoints. In the sections that follow, I explain the process for enabling authorization and demonstrate how an authorization policy can be defined.<a id=\"calibre_link-2955\"><\/a><a id=\"calibre_link-2956\"><\/a><a id=\"calibre_link-750\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-716\">39.3.1 Applying the authorization attribute<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Authorize<\/code> attribute is used to restrict access to an endpoint and can be applied to individual action or page handler methods or to controller or page model classes, in which case the policy applies to all the methods defined by the class. I want to restrict access to the user and role administration tools created in chapter 38. When there are multiple Razor Pages or controllers for which the same authorization policy is required, it is a good idea to define a common base class to which the <code class=\"fm-code-in-text\">Authorize<\/code> attribute can be applied because it ensures that you won\u2019t accidentally omit the attribute and allow unauthorized access. It is for this reason that I defined the <code class=\"fm-code-in-text\">AdminPageModel<\/code> class and used it as the base for all the administration tool page models in chapter 38. Listing 39.9 applies the <code class=\"fm-code-in-text\">Authorize<\/code> attribute to the <code class=\"fm-code-in-text\">AdminPageModel<\/code> class to create the authorization policy.<a id=\"calibre_link-2957\"><\/a><a id=\"calibre_link-2958\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.9 Applying an attribute in the AdminPageModel.cs file in the Pages folder in the Advanced project<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Mvc.RazorPages;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Authorization;<\/b>\n\nnamespace Advanced.Pages {\n\n    <b class=\"fm-bold\">[Authorize(Roles=\"Admins\")]<\/b>\n    public class AdminPageModel : PageModel {\n        \n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">Authorize<\/code> attribute can be applied without arguments, which restricts access to any authenticated user. The <code class=\"fm-code-in-text\">Roles<\/code> argument is used to further restrict access to users who are members of specific roles, which are expressed as a comma-separated list. The attribute in this listing restricts access to users assigned to the <code class=\"fm-code-in-text\">Admins<\/code> role. The authorization restrictions are inherited, which means that applying the attribute to the base class restricts access to all the Razor Pages created to manage users and roles in chapter 38.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Note<\/span> If you want to restrict access to most, but not all, of the action methods in a controller, then you can apply the <code class=\"fm-code-in-text1\">Authorize<\/code> attribute to the controller class and the <code class=\"fm-code-in-text1\">AllowAnonymous<\/code> attribute to just the action methods for which unauthenticated access is required.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-717\">39.3.2 Enabling the authorization middleware<\/h3>\n<p class=\"body\">The authorization policy is enforced by a middleware component, which must be added to the application\u2019s request pipeline, as shown in listing 39.10.<a id=\"calibre_link-2959\"><\/a><a id=\"calibre_link-744\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.10 Adding middleware in the Program.cs file in the Advanced project<\/p>\n<pre class=\"programlisting\">...\napp.UseStaticFiles();\n\napp.UseAuthentication();\n<b class=\"fm-bold\">app.UseAuthorization();<\/b>\n\napp.MapControllers();\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UseAuthorization<\/code> method must be called between the <code class=\"fm-code-in-text\">UseRouting<\/code> and <code class=\"fm-code-in-text\">UseEndpoints<\/code> methods and after the <code class=\"fm-code-in-text\">UseAuthentication<\/code> method has been called. This ensures that the authorization component can access the user data and inspect the authorization policy after the endpoint has been selected but before the request is handled.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-718\">39.3.3 Creating the access denied endpoint<\/h3>\n<p class=\"body\">The application must deal with two different types of authorization failure. If no user has been authenticated when a restricted endpoint is requested, then the authorization middleware will return a challenge response, which will trigger a redirection to the login page so the user can present their credentials and prove they should be able to access the endpoint.<\/p>\n<p class=\"body\">But if an authenticated user requests a restricted endpoint and doesn\u2019t pass the authorization checks, then an access denied response is generated so the application can display a suitable warning to the user. Add a Razor Page named <code class=\"fm-code-in-text\">AccessDenied.cshtml<\/code> to the <code class=\"fm-code-in-text\">Pages\/Account<\/code> folder of the <code class=\"fm-code-in-text\">Advanced<\/code> folder with the content shown in listing 39.11.<a id=\"calibre_link-2960\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.11 The AccessDenied.cshtml file in the Pages\/Account folder of the Advanced project<\/p>\n<pre class=\"programlisting\">@page\n\n&lt;h4 class=\"bg-danger text-white text-center p-2\"&gt;Access Denied&lt;\/h4&gt;\n\n&lt;div class=\"m-2\"&gt;\n    &lt;h6&gt;You are not authorized for this URL&lt;\/h6&gt;\n    &lt;a class=\"btn btn-outline-danger\" href=\"\/\"&gt;OK&lt;\/a&gt;\n    &lt;a class=\"btn btn-outline-secondary\" asp-page=\"Logout\"&gt;Logout&lt;\/a&gt;\n&lt;\/div&gt;<\/pre>\n<p class=\"body\">This page displays a warning message to the user, with a button that navigates to the root URL. There is typically little the user can do to resolve authorization failures without administrative intervention, and my preference is to keep the access denied response as simple as possible.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-719\">39.3.4 Creating the seed data<\/h3>\n<p class=\"body\">In listing 39.9, I restricted access to the user and role administration tools so they can be accessed only by users in the <code class=\"fm-code-in-text\">Admin<\/code> role. There is no such role in the database, which creates a problem: I am locked out of the administration tools because there is no authorized account that will let me create the role.<\/p>\n<p class=\"body\">I could have created an administration user and role before applying the <code class=\"fm-code-in-text\">Authorize<\/code> attribute, but that complicates deploying the application, when making code changes should be avoided. Instead, I am going to create seed data for ASP.NET Core Identity to ensure there will always be at least one account that can be used to access the user and role management tools. Add a class file named <code class=\"fm-code-in-text\">IdentitySeedData.cs<\/code> to the <code class=\"fm-code-in-text\">Models<\/code> folder in the Advanced project and use it to define the class shown in listing 39.12.<a id=\"calibre_link-2961\"><\/a><a id=\"calibre_link-773\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.12 The IdentitySeedData.cs file in the Models folder of the Advanced project<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Identity;\n\nnamespace Advanced.Models {\n    public class IdentitySeedData {\n        \n        public static void CreateAdminAccount(\n                IServiceProvider serviceProvider,\n                IConfiguration configuration) {\n            CreateAdminAccountAsync(serviceProvider, configuration)\n                .Wait();\n        }\n \n        public static async Task CreateAdminAccountAsync(IServiceProvider\n                serviceProvider, IConfiguration configuration) {\n                                \n            serviceProvider = \n                serviceProvider.CreateScope().ServiceProvider;\n            UserManager&lt;IdentityUser&gt; userManager = serviceProvider\n                .GetRequiredService&lt;UserManager&lt;IdentityUser&gt;&gt;();\n            RoleManager&lt;IdentityRole&gt; roleManager = serviceProvider\n                .GetRequiredService&lt;RoleManager&lt;IdentityRole&gt;&gt;();\n                                \n            string username = configuration[\"Data:AdminUser:Name\"] \n                ?? \"admin\";\n            string email = configuration[\"Data:AdminUser:Email\"] \n                ?? \"admin@example.com\";\n            string password = configuration[\"Data:AdminUser:Password\"] \n                ?? \"secret\";\n            string role = configuration[\"Data:AdminUser:Role\"] \n                ?? \"Admins\";\n                                \n            if (await userManager.FindByNameAsync(username) == null) {\n                if (await roleManager.FindByNameAsync(role) == null) {\n                    await roleManager.CreateAsync(new IdentityRole(role));\n                }\n                                \n                IdentityUser user = new IdentityUser {\n                    UserName = username,\n                    Email = email\n                };\n                                \n                IdentityResult result = await userManager\n                    .CreateAsync(user, password);\n                if (result.Succeeded) {\n                    await userManager.AddToRoleAsync(user, role);\n                }\n            }\n        }\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">UserManager&lt;T&gt;<\/code> and <code class=\"fm-code-in-text\">RoleManager&lt;T&gt;<\/code> services are scoped, which means I need to create a new scope before requesting the services since the seeding will be done when the application starts. The seeding code creates a user account that is assigned to a role. The values for the seed data are read from the application\u2019s configuration with fallback values, making it easy to configure the seeded account without needing a code change. Listing 39.13 adds a statement to the <code class=\"fm-code-in-text\">Program.cs<\/code> file so that the database is seeded when the application starts.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> Putting passwords in code files or plain-text configuration files means you must make it part of your deployment process to change the default account\u2019s password when you deploy the application and initialize a new database for the first time. You can also use the user secrets feature to keep sensitive data outside of the project.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.13 Seeding identity in the Program.cs file in the Advanced project<\/p>\n<pre class=\"programlisting\">...\nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n<b class=\"fm-bold\">IdentitySeedData.CreateAdminAccount(app.Services, app.Configuration);<\/b>\n\napp.Run();\n...<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-720\">39.3.5 Testing the authentication sequence<\/h3>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/account\/logout to ensure that no user is logged in to the application. Without logging in, request http:\/\/localhost:5000\/users\/list. The endpoint that will be selected to handle the request requires authentication, and the login prompt will be shown since there is no authenticated user associated with the request. Authenticate with the username <code class=\"fm-code-in-text\">bob<\/code> and the password <code class=\"fm-code-in-text\">secret<\/code>. This user doesn\u2019t have access to the restricted endpoint, and the access denied response will be shown, as illustrated by figure 39.5.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre426\" src=\"\/images\/proaspnetcore7\/000432.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.5 A user without authorization<\/p>\n<\/div>\n<p class=\"body\">Click the Logout button and request http:\/\/localhost:5000\/users\/list again, which will lead to the login prompt being displayed. Authenticate with the username <code class=\"fm-code-in-text\">admin<\/code> and the password <code class=\"fm-code-in-text\">secret<\/code>. This is the user account created by the seed data and that is a member of the role specified by the <code class=\"fm-code-in-text\">Authorize<\/code> attribute. The user passes the authorization check, and the requested Razor Page is displayed, as shown in figure 39.6.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre427\" src=\"\/images\/proaspnetcore7\/000433.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.6 A user with authorization<\/p>\n<\/div>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Changing the authorization URLs<\/p>\n<p class=\"fm-sidebar-text\">The <code class=\"fm-code-in-text1\">\/Account\/Login<\/code> and <code class=\"fm-code-in-text1\">\/Account\/AccessDenied<\/code> URLs are the defaults used by ASP.NET Core authorization files. These can be changed in the <code class=\"fm-code-in-text1\">Program.cs<\/code> file using the options pattern, like this:<a id=\"calibre_link-2962\"><\/a><\/p>\n<pre class=\"programlisting\">...\nbuilder.Services.Configure&lt;CookieAuthenticationOptions&gt;(\n       IdentityConstants.ApplicationScheme,\n    opts =&gt; {\n        opts.LoginPath = \"\/Authenticate\";\n        opts.AccessDeniedPath = \"\/NotAllowed\";\n    });\n...<\/pre>\n<p class=\"fm-sidebar-text\">Configuration is performed using the <code class=\"fm-code-in-text1\">CookieAuthenticationOptions<\/code> class, defined in the <code class=\"fm-code-in-text1\">Microsoft.AspNetCore.Authentication.Cookies<\/code> namespace. The <code class=\"fm-code-in-text1\">LoginPath<\/code> property is used to specify the path to which browsers will be redirected when an unauthenticated user attempts to access a restricted endpoint. The <code class=\"fm-code-in-text1\">AccessDeniedPath<\/code> property is used to specify the path when an authenticated user attempts to access a restricted endpoint and does not have authorization.<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-721\">39.4 Authorizing access to Blazor applications<\/h2>\n<p class=\"body\">The simplest way to protect Blazor applications is to restrict access to the action method or Razor Page that acts as the entry point. In listing 39.14, I added the <code class=\"fm-code-in-text\">Authorize<\/code> attribute to the page model class for the <code class=\"fm-code-in-text\">_Host<\/code> page, which is the entry point for the Blazor application in the example project.<a id=\"calibre_link-2963\"><\/a><a id=\"calibre_link-2964\"><\/a><a id=\"calibre_link-754\"><\/a><\/p>\n<div class=\"fm-sidebar-block\">\n<p class=\"fm-sidebar-title\">Understanding OAuth and IDENTITYSERVER<\/p>\n<p class=\"fm-sidebar-text\">If you read the Microsoft documentation, you will be left with the impression that you need to use a third-party server called <code class=\"fm-code-in-text1\">IdentityServer<\/code> (<a class=\"url\" href=\"https:\/\/duendesoftware.com\">https:\/\/duendesoftware.com<\/a>) to authenticate web services.<a id=\"calibre_link-2965\"><\/a><a id=\"calibre_link-2966\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><code class=\"fm-code-in-text1\">IdentityServer<\/code> is a high-quality open-source package that provides authentication and authorization services, with paid-for options for add-ons and support. <code class=\"fm-code-in-text1\">IdentityServer<\/code> provides support for <code class=\"fm-code-in-text1\">OAuth<\/code>, which is a standard for managing authentication and authorization and provides packages for a range of client-side frameworks.<\/p>\n<p class=\"fm-sidebar-text\">What the Microsoft documentation is saying&mdash;albeit awkwardly&mdash;is that Microsoft has used <code class=\"fm-code-in-text1\">IdentityServer<\/code> in the project templates that include authentication for web services. If you create an Angular or React project using an ASP.NET Core template provided by Microsoft, you will find that the authentication has been implemented using <code class=\"fm-code-in-text1\">IdentityServer<\/code>.<\/p>\n<p class=\"fm-sidebar-text\">Authentication is complex, and <code class=\"fm-code-in-text1\">IdentityServer<\/code> can be difficult to set up correctly. I like <code class=\"fm-code-in-text1\">IdentityServer<\/code>, but it is not essential and is not required by most projects. <code class=\"fm-code-in-text1\">IdentityServer<\/code> may be useful if your project needs to support complex authentication scenarios, but my advice is not to rush into using third-party authentication servers until they are essential.<\/p>\n<\/div>\n<p class=\"fm-code-listing-caption\">Listing 39.14 Applying an attribute in the _Host.cshtml file in the Pages folder of the Advanced project<\/p>\n<pre class=\"programlisting\">@page \"\/\"\n@{ Layout = null; }\n<b class=\"fm-bold\">@model HostModel<\/b>\n<b class=\"fm-bold\">@using Microsoft.AspNetCore.Authorization<\/b>\n\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;@ViewBag.Title&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    &lt;base href=\"~\/\" \/&gt;  \n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div class=\"m-2\"&gt;\n        &lt;component type=\"typeof(Advanced.Blazor.Routed)\" \n            render-mode=\"Server\" \/&gt;\n    &lt;\/div&gt;\n    &lt;script src=\"_framework\/blazor.server.js\"&gt;&lt;\/script&gt;\n    &lt;script src=\"~\/interop.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n\n<b class=\"fm-bold\">@functions {<\/b>\n\n    <b class=\"fm-bold\">[Authorize]<\/b>\n    <b class=\"fm-bold\">public class HostModel : PageModel { }<\/b>\n<b class=\"fm-bold\">}<\/b><\/pre>\n<p class=\"body\">This has the effect of preventing unauthenticated users from accessing the Blazor application. Request http:\/\/localhost:5000\/account\/logout to ensure the browser doesn\u2019t have an authentication cookie and then request http:\/\/localhost:5000. This request will be handled by the <code class=\"fm-code-in-text\">_Host<\/code> page, but the authorization middleware will trigger the redirection to the login prompt. Authenticate with the username <code class=\"fm-code-in-text\">bob<\/code> and the password <code class=\"fm-code-in-text\">secret<\/code>, and you will be granted access to the Blazor application, as shown in figure 39.7.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre428\" src=\"\/images\/proaspnetcore7\/000434.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.7 Restricting access to the Blazor endpoint<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-722\">39.4.1 Performing authorization in Blazor components<\/h3>\n<p class=\"body\">Restricting access to the endpoint is an effective technique, but it applies the same level of authorization to all the Blazor functionality. For applications that require more granular restrictions, Blazor provides the <code class=\"fm-code-in-text\">AuthorizeRouteView<\/code> component, which allows different content to be displayed for authorized and unauthorized when components are managed using URL routing. Listing 39.15 adds the <code class=\"fm-code-in-text\">AuthorizeRouteView<\/code> to the routing component in the example application.<a id=\"calibre_link-2967\"><\/a><a id=\"calibre_link-786\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.15 Adding a component in the Routed.razor file in the Blazor folder of the Advanced project<\/p>\n<pre class=\"programlisting\"><b class=\"fm-bold\">@using Microsoft.AspNetCore.Components.Authorization<\/b>\n\n&lt;Router AppAssembly=\"typeof(Program).Assembly\"&gt;\n    &lt;Found&gt;\n        <b class=\"fm-bold\">&lt;AuthorizeRouteView RouteData=\"@context\"<\/b> \n                <b class=\"fm-bold\">DefaultLayout=\"typeof(NavLayout)\"&gt;<\/b>\n           <b class=\"fm-bold\">&lt;NotAuthorized Context=\"authContext\"&gt;<\/b>\n                <b class=\"fm-bold\">&lt;h4 class=\"bg-danger text-white text-center p-2\"&gt;<\/b>\n                    <b class=\"fm-bold\">Not Authorized<\/b>\n                <b class=\"fm-bold\">&lt;\/h4&gt;<\/b>\n                <b class=\"fm-bold\">&lt;div class=\"text-center\"&gt;<\/b>\n                    <b class=\"fm-bold\">You may need to log in as a different user<\/b>\n                <b class=\"fm-bold\">&lt;\/div&gt;<\/b>\n            <b class=\"fm-bold\">&lt;\/NotAuthorized&gt;<\/b>\n        <b class=\"fm-bold\">&lt;\/AuthorizeRouteView&gt;<\/b>\n    &lt;\/Found&gt;\n    &lt;NotFound&gt;\n        &lt;h4 class=\"bg-danger text-white text-center p-2\"&gt;\n            No Matching Route Found\n        &lt;\/h4&gt;\n    &lt;\/NotFound&gt;\n&lt;\/Router&gt;<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">NotAuthorized<\/code> section is used to define the content that will be presented to users when they attempt to access a restricted resource. To demonstrate this feature, I am going to restrict access to the <code class=\"fm-code-in-text\">DepartmentList<\/code> component to users assigned to the <code class=\"fm-code-in-text\">Admins<\/code> role, as shown in listing 39.16.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.16 Restricting access in the DepartmentList.cshtml file in the Blazor folder in the Advanced project<\/p>\n<pre class=\"programlisting\">@page \"\/departments\"\n@page \"\/depts\"\n<b class=\"fm-bold\">@using Microsoft.AspNetCore.Authorization<\/b>\n<b class=\"fm-bold\">@attribute [Authorize(Roles = \"Admins\")]<\/b>\n\n&lt;CascadingValue Name=\"BgTheme\" Value=\"Theme\" IsFixed=\"false\"&gt;\n    &lt;TableTemplate RowType=\"Department\" RowData=\"Departments\"\n                   Highlight=\"@(d =&gt; d.Name)\"\n                   SortDirection=\"@(d =&gt; d.Name)\"&gt;\n        &lt;Header&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;People&lt;\/th&gt;&lt;th&gt;Locations&lt;\/th&gt;\n        &lt;\/tr&gt;\n        &lt;\/Header&gt;\n        &lt;RowTemplate Context=\"d\"&gt;\n            &lt;td&gt;@d.Departmentid&lt;\/td&gt;\n            &lt;td&gt;@d.Name&lt;\/td&gt;\n            &lt;td&gt;\n                @(String.Join(\", \", d.People!.Select(p =&gt; p.Surname)))\n            &lt;\/td&gt;\n            &lt;td&gt;\n                @(String.Join(\", \", d.People!.Select(p =&gt;\n                    p.Location!.City).Distinct()))\n            &lt;\/td&gt;\n        &lt;\/RowTemplate&gt;\n    &lt;\/TableTemplate&gt;\n&lt;\/CascadingValue&gt;\n\n&lt;SelectFilter Title=\"@(\"Theme\")\" Values=\"Themes\"\n              @bind-SelectedValue=\"Theme\" \/&gt;\n                          \n&lt;button class=\"btn btn-primary\" @onclick=\"HandleClick\"&gt;People&lt;\/button&gt;\n\n@code {\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Department&gt;? Departments =&gt; Context?.Departments?\n        .Include(d =&gt; d.People!).ThenInclude(p =&gt; p.Location!);\n                \n    public string Theme { get; set; } = \"info\";\n    public string[] Themes = \n        new string[] { \"primary\", \"info\", \"success\" };\n                \n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n \n    public void HandleClick() =&gt; NavManager?.NavigateTo(\"\/people\");\n}<\/pre>\n<p class=\"body\">I have used the <code class=\"fm-code-in-text\">@attribute<\/code> directive to apply the <code class=\"fm-code-in-text\">Authorize<\/code> attribute to the component. Restart ASP.NET Core and request http:\/\/localhost:5000\/account\/logout to remove the authentication cookie and then request http:\/\/localhost:5000. When prompted, authenticate with the username <code class=\"fm-code-in-text\">bob<\/code> and the password <code class=\"fm-code-in-text\">secret<\/code>. You will see the Blazor application, but when you click the Departments button, you will see the authorization content defined in listing 39.15, as shown in figure 39.8. Log out again and log in as <code class=\"fm-code-in-text\">admin<\/code> with the password <code class=\"fm-code-in-text\">secret<\/code>, and you will be able to use the restricted component.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre429\" src=\"\/images\/proaspnetcore7\/000435.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.8 Using authorization in a Blazor application<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-723\">39.4.2 Displaying content to authorized users<\/h3>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AuthorizeView<\/code> component is used to restrict access to sections of content rendered by a component. In listing 39.17, I have changed the authorization for the <code class=\"fm-code-in-text\">DepartmentList<\/code> component so that any authenticated user can access the page and use the <code class=\"fm-code-in-text\">AuthorizeView<\/code> component so that the contents of the <code class=\"fm-code-in-text\">Locations<\/code> column in the table is shown only to users assigned to the <code class=\"fm-code-in-text\">Admins<\/code> group.<a id=\"calibre_link-2968\"><\/a><a id=\"calibre_link-787\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.17 Selective content in the DepartmentList.razor file in the Blazor folder in the Advanced project<\/p>\n<pre class=\"programlisting\">@page \"\/departments\"\n@page \"\/depts\"\n@using Microsoft.AspNetCore.Authorization\n<b class=\"fm-bold\">@using Microsoft.AspNetCore.Components.Authorization<\/b>\n<b class=\"fm-bold\">@attribute [Authorize]<\/b>\n\n&lt;CascadingValue Name=\"BgTheme\" Value=\"Theme\" IsFixed=\"false\"&gt;\n    &lt;TableTemplate RowType=\"Department\" RowData=\"Departments\"\n                   Highlight=\"@(d =&gt; d.Name)\"\n                   SortDirection=\"@(d =&gt; d.Name)\"&gt;\n        &lt;Header&gt;\n        &lt;tr&gt;\n            &lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;Name&lt;\/th&gt;&lt;th&gt;People&lt;\/th&gt;&lt;th&gt;Locations&lt;\/th&gt;\n        &lt;\/tr&gt;\n        &lt;\/Header&gt;\n        &lt;RowTemplate Context=\"d\"&gt;\n            &lt;td&gt;@d.Departmentid&lt;\/td&gt;\n            &lt;td&gt;@d.Name&lt;\/td&gt;\n            &lt;td&gt;\n                @(String.Join(\", \", d.People!.Select(p =&gt; p.Surname)))\n            &lt;\/td&gt;\n            &lt;td&gt;\n                <b class=\"fm-bold\">&lt;AuthorizeView Roles=\"Admins\"&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;Authorized&gt;<\/b>\n                        <b class=\"fm-bold\">@(String.Join(\", \",d.People!<\/b>\n                            <b class=\"fm-bold\">.Select(p =&gt; p.Location!.City).Distinct()))<\/b>\n                    <b class=\"fm-bold\">&lt;\/Authorized&gt;<\/b>\n                    <b class=\"fm-bold\">&lt;NotAuthorized&gt;<\/b>\n                        <b class=\"fm-bold\">(Not authorized)<\/b>\n                    <b class=\"fm-bold\">&lt;\/NotAuthorized&gt;<\/b>\n                <b class=\"fm-bold\">&lt;\/AuthorizeView&gt;<\/b>\n            &lt;\/td&gt;\n        &lt;\/RowTemplate&gt;\n    &lt;\/TableTemplate&gt;\n&lt;\/CascadingValue&gt;\n\n&lt;SelectFilter Title=\"@(\"Theme\")\" Values=\"Themes\"\n              @bind-SelectedValue=\"Theme\" \/&gt;\n                          \n&lt;button class=\"btn btn-primary\" @onclick=\"HandleClick\"&gt;People&lt;\/button&gt;\n\n@code {\n    [Inject]\n    public DataContext? Context { get; set; }\n \n    public IEnumerable&lt;Department&gt;? Departments =&gt; Context?.Departments?\n        .Include(d =&gt; d.People!).ThenInclude(p =&gt; p.Location!);\n                \n    public string Theme { get; set; } = \"info\";\n    public string[] Themes = \n        new string[] { \"primary\", \"info\", \"success\" };\n                \n    [Inject]\n    public NavigationManager? NavManager { get; set; }\n \n    public void HandleClick() =&gt; NavManager?.NavigateTo(\"\/people\");\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AuthorizeView<\/code> component is configured with the <code class=\"fm-code-in-text\">Roles<\/code> property, which accepts a comma-separated list of authorized roles. The <code class=\"fm-code-in-text\">Authorized<\/code> section contains the content that will be shown to authorized users. The <code class=\"fm-code-in-text\">NotAuthorized<\/code> section contains the content that will be shown to unauthorized users.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You can omit the <code class=\"fm-code-in-text1\">NotAuthorized<\/code> section if you don\u2019t need to show content to unauthorized users.<\/p>\n<p class=\"body\">Restart ASP.NET Core and authenticate as <code class=\"fm-code-in-text\">bob<\/code>, with password <code class=\"fm-code-in-text\">secret<\/code>, before requesting http:\/\/localhost:5000\/depts. This user is not authorized to see the contents of the <code class=\"fm-code-in-text\">Locations<\/code> column, as shown in figure 39.9. Authenticate as <code class=\"fm-code-in-text\">admin<\/code>, with password <code class=\"fm-code-in-text\">secret<\/code>, and request http:\/\/localhost:5000\/depts again. This time the user is a member of the <code class=\"fm-code-in-text\">Admins<\/code> role and passes the authorization checks, also shown in figure 39.9.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre430\" src=\"\/images\/proaspnetcore7\/000436.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.9 Selectively displaying content based on authorization<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-724\">39.5 Authenticating and authorizing web services<\/h2>\n<p class=\"body\">The authorization process in the previous section relies on being able to redirect the client to a URL that allows the user to enter their credentials. A different approach is required when adding authentication and authorization to a web service because there is no option to present the user with an HTML form to collect their credentials. The first step in adding support for web services authentication is to disable the redirections so that the client will receive HTTP error responses when attempting to request an endpoint that requires authentication. Add a class file named <code class=\"fm-code-in-text\">CookieAuthenticationExtensions.cs<\/code> to the <code class=\"fm-code-in-text\">Advanced<\/code> folder and use it to define the extension method shown in listing 39.18.<a id=\"calibre_link-2969\"><\/a><a id=\"calibre_link-2970\"><\/a><a id=\"calibre_link-2971\"><\/a><a id=\"calibre_link-2972\"><\/a><a id=\"calibre_link-751\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.18 The CookieAuthenticationExtensions.cs file in the Advanced folder<\/p>\n<pre class=\"programlisting\">using System.Linq.Expressions;\n\nnamespace Microsoft.AspNetCore.Authentication.Cookies {\n    public static class CookieAuthenticationExtensions {\n        \n        public static void DisableRedirectForPath(\n            this CookieAuthenticationEvents events,\n            Expression&lt;Func&lt;CookieAuthenticationEvents,\n                Func&lt;RedirectContext&lt;CookieAuthenticationOptions&gt;, \n                Task&gt;&gt;&gt; expr,\n            string path, int statuscode) {\n                        \n            string propertyName \n                = ((MemberExpression)expr.Body).Member.Name;\n            var oldHandler = expr.Compile().Invoke(events);\n                        \n            Func&lt;RedirectContext&lt;CookieAuthenticationOptions&gt;, Task&gt; \n                newHandler = context =&gt; {\n                    if (context.Request.Path.StartsWithSegments(path)) {\n                        context.Response.StatusCode = statuscode;\n                    } else {\n                        oldHandler(context);\n                    }\n                    return Task.CompletedTask;\n                };\n                                \n            typeof(CookieAuthenticationEvents).GetProperty(propertyName)?\n                .SetValue(events, newHandler);\n        }\n    }\n}<\/pre>\n<p class=\"body\">This code is hard to follow. ASP.NET Core provides the <code class=\"fm-code-in-text\">CookieAuthenticationOptions<\/code> class, which is used to configure cookie-based authentication. The <code class=\"fm-code-in-text\">CookieAuthenticationOptions.Events<\/code> property returns a <code class=\"fm-code-in-text\">CookieAuthenticationEvents<\/code> object, which is used to set the handlers for the events triggered by the authentication system, including the redirections that occur when the user requests unauthorized content. The extension methods in listing 39.18 replaces the default handler for an event with one that performs redirection only if the request doesn\u2019t start with a specified path string. Listing 39.19 uses the extension method to replace the <code class=\"fm-code-in-text\">OnRedirectToLogin<\/code> and <code class=\"fm-code-in-text\">OnRedirectToAccessDenied<\/code> handlers so that redirections are not performed when the request path starts with <code class=\"fm-code-in-text\">\/api<\/code>.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.19 Preventing redirection in the Program.cs file in the Advanced folder<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\nusing Microsoft.AspNetCore.Identity;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Authentication.Cookies;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddServerSideBlazor();\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddSingleton&lt;Advanced.Services.ToggleService&gt;();\n\nbuilder.Services.AddDbContext&lt;IdentityContext&gt;(opts =&gt;\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:IdentityConnection\"]));\nbuilder.Services.AddIdentity&lt;IdentityUser, IdentityRole&gt;()\n    .AddEntityFrameworkStores&lt;IdentityContext&gt;();\n        \nbuilder.Services.Configure&lt;IdentityOptions&gt;(opts =&gt; {\n    opts.Password.RequiredLength = 6;\n    opts.Password.RequireNonAlphanumeric = false;\n    opts.Password.RequireLowercase = false;\n    opts.Password.RequireUppercase = false;\n    opts.Password.RequireDigit = false;\n    opts.User.RequireUniqueEmail = true;\n    opts.User.AllowedUserNameCharacters = \"abcdefghijklmnopqrstuvwxyz\";\n});\n\n<b class=\"fm-bold\">builder.Services.AddAuthentication(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.DefaultScheme =<\/b>\n        <b class=\"fm-bold\">CookieAuthenticationDefaults.AuthenticationScheme;<\/b>\n    <b class=\"fm-bold\">opts.DefaultChallengeScheme =<\/b>\n         <b class=\"fm-bold\">CookieAuthenticationDefaults.AuthenticationScheme;<\/b>\n<b class=\"fm-bold\">}).AddCookie(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.Events.DisableRedirectForPath(e =&gt; e.OnRedirectToLogin,<\/b>\n         <b class=\"fm-bold\">\"\/api\", StatusCodes.Status401Unauthorized);<\/b>\n    <b class=\"fm-bold\">opts.Events.DisableRedirectForPath(e =&gt; e.OnRedirectToAccessDenied,<\/b>\n        <b class=\"fm-bold\">\"\/api\", StatusCodes.Status403Forbidden);<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\napp.UseAuthentication();\napp.UseAuthorization();\n\napp.MapControllers();\napp.MapControllerRoute(\"controllers\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\napp.MapBlazorHub();\napp.MapFallbackToPage(\"\/_Host\");\n\napp.UseBlazorFrameworkFiles(\"\/webassembly\");\napp.MapFallbackToFile(\"\/webassembly\/{*path:nonfile}\",\n    \"\/webassembly\/index.xhtml\");\n        \nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\nIdentitySeedData.CreateAdminAccount(app.Services, app.Configuration);\n\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddAuthentication<\/code> method is used to select cookie-based authentication and is chained with the <code class=\"fm-code-in-text\">AddCookie<\/code> method to replace the event handlers that would otherwise trigger redirections.<a id=\"calibre_link-2973\"><\/a><a id=\"calibre_link-762\"><\/a><\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-725\">39.5.1 Building a simple JavaScript client<\/h3>\n<p class=\"body\">To demonstrate how to perform authentication with web services, I am going to create a simple JavaScript client that will consume data from the <code class=\"fm-code-in-text\">Data<\/code> controller in the example project.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Tip<\/span> You don\u2019t have to be familiar with JavaScript to follow the examples in this part of the chapter. It is the server-side code that is important and the way it supports authentication by the client so that it can access the web service.<\/p>\n<p class=\"body\">Add an HTML Page called <code class=\"fm-code-in-text\">webclient.xhtml<\/code> to the <code class=\"fm-code-in-text\">wwwroot<\/code> folder of the <code class=\"fm-code-in-text\">Advanced<\/code> project with the elements shown in listing 39.20.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.20 The contents of the webclient.xhtml file in the wwwroot folder of the Advanced project<\/p>\n<pre class=\"programlisting\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;Web Service Authentication&lt;\/title&gt;\n    &lt;link href=\"\/lib\/bootstrap\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt;\n    &lt;script type=\"text\/javascript\" src=\"webclient.js\"&gt;&lt;\/script&gt;\n&lt;\/head&gt;\n&lt;body&gt;    \n    &lt;div id=\"controls\" class=\"m-2\"&gt;&lt;\/div&gt;\n    &lt;div id=\"data\" class=\"m-2 p-2\"&gt;\n        No data\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p class=\"body\">Add a JavaScript file named <code class=\"fm-code-in-text\">webclient.js<\/code> to the <code class=\"fm-code-in-text\">wwwroot<\/code> of the Advanced project with the content shown in listing 39.21.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.21 The webclient.js file in the wwwroot folder of the Advanced project<\/p>\n<pre class=\"programlisting\">const username = \"bob\";\nconst password = \"secret\";\n\nwindow.addEventListener(\"DOMContentLoaded\", () =&gt; {\n    const controlDiv = document.getElementById(\"controls\");\n    <span lang=\"it-IT\">createButton(controlDiv, \"Get Data\", getData);<\/span>\n<span lang=\"it-IT\">    createButton(controlDiv, \"Log In\", login);<\/span>\n    createButton(controlDiv, \"Log Out\", logout);\n});\n\nfunction login() {\n    \/\/ do nothing\n}\n\nfunction logout() {\n    \/\/ do nothing\n}\n\nasync function getData() {\n    let response = await fetch(\"\/api\/people\");\n    if (response.ok) {\n        let jsonData = await response.json();\n        displayData(...jsonData.map(item =&gt; `${item.surname}, \n            ${item.firstname}`));\n    } else {\n        displayData(`Error: ${response.status}: ${response.statusText}`);\n    }\n}\n\nfunction displayData(...items) {\n    const dataDiv = document.getElementById(\"data\");\n    dataDiv.innerHTML = \"\";\n    items.forEach(item =&gt; {\n        const itemDiv = document.createElement(\"div\");\n        itemDiv.innerText = item;\n        itemDiv.style.wordWrap = \"break-word\";\n        dataDiv.appendChild(itemDiv);\n    })\n}\n\nfunction createButton(parent, label, handler) {\n    const button = document.createElement(\"button\");\n    button.classList.add(\"btn\", \"btn-primary\",  \"m-2\");\n    button.innerText = label;\n    button.onclick = handler;\n    parent.appendChild(button);\n}<\/pre>\n<p class=\"body\">This code presents the user with Get Data and Log In and Log Out buttons. Clicking the Get Data button sends an HTTP request using the Fetch API, processes the JSON result, and displays a list of names. The other buttons do nothing, but I\u2019ll use them in later examples to authenticate with the ASP.NET Core application using the hardwired credentials in the JavaScript code.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> This is just a simple client to demonstrate server-side authentication features. If you need to write a JavaScript client, then consider a framework such as Angular or React. Regardless of how you build your clients, do not include hardwired credentials in the JavaScript files.<\/p>\n<p class=\"body\">Request http:\/\/localhost:5000\/webclient.xhtml and click the Get Data button. The JavaScript client will send an HTTP request to the <code class=\"fm-code-in-text\">Data<\/code> controller and display the results, as shown in figure 39.10.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre431\" src=\"\/images\/proaspnetcore7\/000437.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.10 A simple web client<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-726\">39.5.2 Restricting access to the web service<\/h3>\n<p class=\"body\">The standard authorization features are used to restrict access to web service endpoints, and in listing 39.22, I have applied the <code class=\"fm-code-in-text\">Authorize<\/code> attribute to the <code class=\"fm-code-in-text\">DataController<\/code> class.<a id=\"calibre_link-2974\"><\/a><a id=\"calibre_link-752\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.22 Applying an attribute in the DataController.cs file in the Controllers folder of the Advanced project<\/p>\n<pre class=\"programlisting\">using Advanced.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Authorization;<\/b>\n\nnamespace Advanced.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/people\")]\n    <b class=\"fm-bold\">[Authorize]<\/b>\n    public class DataController : ControllerBase {\n        private DataContext context;\n                \n        \/\/ ...methods omitted for brevity...\n    }\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/account\/logout to ensure that the JavaScript client doesn\u2019t use an authentication cookie from a previous example. Request http:\/\/localhost:5000\/webclient.xhtml to load the JavaScript client and click the Get Data button to send the HTTP request. The server will respond with a <code class=\"fm-code-in-text\">401 Unauthorized<\/code> response, as shown in figure 39.11.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre432\" src=\"\/images\/proaspnetcore7\/000438.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.11 An unauthorized request<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-727\">39.5.3 Using cookie authentication<\/h3>\n<p class=\"body\">The simplest way to implement authentication is to rely on the standard ASP.NET Core cookies demonstrated in previous sections. Add a class file named <code class=\"fm-code-in-text\">ApiAccountController.cs<\/code> to the <code class=\"fm-code-in-text\">Controllers<\/code> folder of the <code class=\"fm-code-in-text\">Advanced<\/code> project and use it to define the controller shown in listing 39.23.<a id=\"calibre_link-2975\"><\/a><a id=\"calibre_link-779\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.23 The ApiAccountController.cs file in the Controllers folder of the Advanced project<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Identity;\nusing Microsoft.AspNetCore.Mvc;\n\nnamespace Advanced.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/account\")]\n    public class ApiAccountController : ControllerBase {\n        private SignInManager&lt;IdentityUser&gt; signinManager;\n                \n        public ApiAccountController(SignInManager&lt;IdentityUser&gt; mgr) {\n            signinManager = mgr;\n        }\n                \n        [HttpPost(\"login\")]\n        public async Task&lt;IActionResult&gt; Login([FromBody] \n                Credentials creds) {\n            Microsoft.AspNetCore.Identity.SignInResult result\n                = await signinManager.PasswordSignInAsync(creds.Username,\n                     creds.Password, false, false);\n            if (result.Succeeded) {\n                return Ok();\n            }\n            return Unauthorized();\n        }\n                \n        [HttpPost(\"logout\")]\n        public async Task&lt;IActionResult&gt; Logout() {\n            await signinManager.SignOutAsync();\n            return Ok();\n        }\n \n        public class Credentials {\n                \n            public string Username { get; set; } = string.Empty;\n                        \n            public string Password { get; set; } = string.Empty;\n        }\n    }\n}<\/pre>\n<p class=\"body\">This web service controller defines actions that allow clients to log in and log out. The response for a successful authentication request will contain a cookie that the browser will automatically include in requests made by the JavaScript client.<\/p>\n<p class=\"body\">Listing 39.24 adds support to the simple JavaScript client for authenticating using the action methods defined in listing 39.23.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.24 Adding authentication in the webclient.js file in the wwwroot folder of the Advanced project<\/p>\n<pre class=\"programlisting\">const username = \"bob\";\nconst password = \"secret\";\n\nwindow.addEventListener(\"DOMContentLoaded\", () =&gt; {\n    const controlDiv = document.getElementById(\"controls\");\n    <span lang=\"it-IT\">createButton(controlDiv, \"Get Data\", getData);<\/span>\n<span lang=\"it-IT\">    createButton(controlDiv, \"Log In\", login);<\/span>\n    createButton(controlDiv, \"Log Out\", logout);\n});\n\n<b class=\"fm-bold\">async function login() {<\/b>\n    <b class=\"fm-bold\">let response = await fetch(\"\/api\/account\/login\", {<\/b>\n        <b class=\"fm-bold\">method: \"POST\",<\/b>\n        <b class=\"fm-bold\">headers: { \"Content-Type\": \"application\/json\" },<\/b>\n        <b class=\"fm-bold\">body: JSON.stringify({ username: username, password: password })<\/b>\n    <b class=\"fm-bold\">});<\/b>\n    <b class=\"fm-bold\">if (response.ok) {<\/b>\n        <b class=\"fm-bold\">displayData(\"Logged in\");<\/b>\n    <b class=\"fm-bold\">} else {<\/b>\n        <b class=\"fm-bold\">displayData('Error: ${response.status}: ${response.statusText}');<\/b>\n    <b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">}<\/b>\n\n<b class=\"fm-bold\">async function logout() {<\/b>\n    <b class=\"fm-bold\">let response = await fetch(\"\/api\/account\/logout\", {<\/b>\n        <b class=\"fm-bold\">method: \"POST\"<\/b>\n    <b class=\"fm-bold\">});<\/b>\n    <b class=\"fm-bold\">if (response.ok) {<\/b>\n        <b class=\"fm-bold\">displayData(\"Logged out\");<\/b>\n    <b class=\"fm-bold\">} else {<\/b>\n        <b class=\"fm-bold\">displayData('Error: ${response.status}: ${response.statusText}');<\/b>\n    <b class=\"fm-bold\">}<\/b>\n<b class=\"fm-bold\">}<\/b>\n\nasync function getData() {\n    let response = await fetch(\"\/api\/people\");\n    if (response.ok) {\n        let jsonData = await response.json();\n        displayData(...jsonData.map(item =&gt; `${item.surname}, \n            ${item.firstname}`));\n    } else {\n        displayData(`Error: ${response.status}: ${response.statusText}`);\n    }\n}\n\nfunction displayData(...items) {\n    const dataDiv = document.getElementById(\"data\");\n    dataDiv.innerHTML = \"\";\n    items.forEach(item =&gt; {\n        const itemDiv = document.createElement(\"div\");\n        itemDiv.innerText = item;\n        itemDiv.style.wordWrap = \"break-word\";\n        dataDiv.appendChild(itemDiv);\n    })\n}\n\nfunction createButton(parent, label, handler) {\n    const button = document.createElement(\"button\");\n    button.classList.add(\"btn\", \"btn-primary\",  \"m-2\");\n    button.innerText = label;\n    button.onclick = handler;\n    parent.appendChild(button);\n}<\/pre>\n<p class=\"body\">Restart ASP.NET Core, request http:\/\/localhost:5000\/webclient.xhtml, and click the Login In button. Wait for the message confirming authentication and then click the Get Data button. The browser includes the authentication cookie, and the request passes the authorization checks. Click the Log Out button and then click Get Data again. No cookie is used, and the request fails. Figure 39.12 shows both requests.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre433\" src=\"\/images\/proaspnetcore7\/000439.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.12 Using cookie authentication<\/p>\n<\/div>\n<h3 class=\"fm-head1\" id=\"calibre_link-728\">39.5.4 Using bearer token authentication<\/h3>\n<p class=\"body\">Not all web services will be able to rely on cookies because not all clients can use then. An alternative is to use a bearer token, which is a string that clients are given and is included in the requests they send to the web service. Clients don\u2019t understand the meaning of the token&mdash;which is said to be <i class=\"fm-italics\">opaque<\/i>&mdash;and just use whatever token the server provides.<\/p>\n<p class=\"body\">I am going to demonstrate authentication using a JSON Web Token (JWT), which provides the client with an encrypted token that contains the authenticated username. The client is unable to decrypt or modify the token, but when it is included in a request, the ASP.NET Core server decrypts the token and uses the name it contains as the identity of the user. The JWT format is described in detail at <a class=\"url\" href=\"https:\/\/tools.ietf.org\/html\/rfc7519\">https:\/\/tools.ietf.org\/html\/rfc7519<\/a>.<a id=\"calibre_link-2976\"><\/a><a id=\"calibre_link-2977\"><\/a><a id=\"calibre_link-778\"><\/a><\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> ASP.NET Core will trust that any request that includes the token originates from the authenticated user. Just as when using cookies, production applications should use HTTPS to prevent tokens from being intercepted and reused.<\/p>\n<p class=\"body\">Open a new PowerShell command prompt, navigate to the <code class=\"fm-code-in-text\">Advanced<\/code> project folder, and run the commands shown in listing 39.25 to add the packages for JWT to the project.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.25 Installing the NuGet package<\/p>\n<pre class=\"programlisting\">dotnet add package System.IdentityModel.Tokens.Jwt --version 6.25.0\ndotnet add package Microsoft.AspNetCore.Authentication.JwtBearer\n    --version 7.0.0 <\/pre>\n<p class=\"body\">JWT requires a key that is used to encrypt and decrypt tokens. Add the configuration setting shown in listing 39.26 to the <code class=\"fm-code-in-text\">appsettings.json<\/code> file. If you use JWT in a real application, ensure you change the key or use a secret to store the key outside of the project.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.26 Adding a setting in the appsettings.json file in the Advanced project<\/p>\n<pre class=\"programlisting\">{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\",\n      \"Microsoft.EntityFrameworkCore\": \"Information\"\n    }\n  },\n  \"AllowedHosts\": \"*\",\n  \"ConnectionStrings\": {\n    \"PeopleConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;...\nDatabase=People;MultipleActiveResultSets=True\",\n    \"IdentityConnection\": \"Server=(localdb)\\\\MSSQLLocalDB;Database=Identity\n<span class=\"fm-code-continuation-arrow\">\u27a5<\/span>;MultipleActiveResultSets=True\"\n  },\n  <b class=\"fm-bold\">\"jwtSecret\": \"jwt_secret\"<\/b>\n}<\/pre>\n<h3 class=\"fm-head1\" id=\"calibre_link-729\">39.5.5 Creating tokens<\/h3>\n<p class=\"body\">The client will send an HTTP request that contains user credentials and will receive a JWT in response. Listing 39.27 adds an action method to the <code class=\"fm-code-in-text\">ApiAccount<\/code> controller that receives the credentials, validates them, and generates tokens.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.27 Generating tokens in the ApiAccountController.cs file in the Controllers folder of the Advanced project<\/p>\n<pre class=\"programlisting\">using Microsoft.AspNetCore.Identity;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.IdentityModel.Tokens;\n<b class=\"fm-bold\">using System.IdentityModel.Tokens.Jwt;<\/b>\n<b class=\"fm-bold\">using System.Text;<\/b>\n<b class=\"fm-bold\">using System.Security.Claims;<\/b>\n\nnamespace Advanced.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/account\")]\n    public class ApiAccountController : ControllerBase {\n        private SignInManager&lt;IdentityUser&gt; signinManager;\n        <b class=\"fm-bold\">private UserManager&lt;IdentityUser&gt; userManager;<\/b>\n        <b class=\"fm-bold\">private IConfiguration configuration;<\/b>\n                \n        <b class=\"fm-bold\">public ApiAccountController(SignInManager&lt;IdentityUser&gt; mgr,<\/b>\n                 <b class=\"fm-bold\">UserManager&lt;IdentityUser&gt; usermgr,<\/b> \n                 <b class=\"fm-bold\">IConfiguration config) {<\/b>\n            <b class=\"fm-bold\">signinManager = mgr;<\/b>\n            <b class=\"fm-bold\">userManager = usermgr;<\/b>\n            <b class=\"fm-bold\">configuration = config;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        [HttpPost(\"login\")]\n        public async Task&lt;IActionResult&gt; Login(\n                [FromBody] Credentials creds) {\n            Microsoft.AspNetCore.Identity.SignInResult result\n                = await signinManager.PasswordSignInAsync(creds.Username,\n                    creds.Password, false, false);\n            if (result.Succeeded) {\n                return Ok();\n            }\n            return Unauthorized();\n        }\n                \n        [HttpPost(\"logout\")]\n        public async Task&lt;IActionResult&gt; Logout() {\n            await signinManager.SignOutAsync();\n            return Ok();\n        }\n                \n        <b class=\"fm-bold\">[HttpPost(\"token\")]<\/b>\n        <b class=\"fm-bold\">public async Task&lt;IActionResult&gt; Token(<\/b>\n                <b class=\"fm-bold\">[FromBody] Credentials creds) {<\/b>\n            <b class=\"fm-bold\">if (await CheckPassword(creds)) {<\/b>\n                <b class=\"fm-bold\">JwtSecurityTokenHandler handler =<\/b>\n                    <b class=\"fm-bold\">new JwtSecurityTokenHandler();<\/b>\n                <b class=\"fm-bold\">byte[] secret =<\/b> \n                    <b class=\"fm-bold\">Encoding.ASCII.GetBytes(configuration[\"jwtSecret\"]!);<\/b>\n                <b class=\"fm-bold\">SecurityTokenDescriptor descriptor =<\/b> \n                    <b class=\"fm-bold\">new SecurityTokenDescriptor {<\/b>\n                        <b class=\"fm-bold\">Subject = new ClaimsIdentity(new Claim[] {<\/b>\n                            <b class=\"fm-bold\">new Claim(ClaimTypes.Name, creds.Username)<\/b>\n                        <b class=\"fm-bold\">}),<\/b>\n                        <b class=\"fm-bold\">Expires = DateTime.UtcNow.AddHours(24),<\/b>\n                        <b class=\"fm-bold\">SigningCredentials = new SigningCredentials(<\/b>\n                            <b class=\"fm-bold\">new SymmetricSecurityKey(secret),<\/b>\n                                <b class=\"fm-bold\">SecurityAlgorithms.HmacSha256Signature)<\/b>\n                    <b class=\"fm-bold\">};<\/b>\n                <b class=\"fm-bold\">SecurityToken token = handler.CreateToken(descriptor);<\/b>\n                <b class=\"fm-bold\">return Ok(new {<\/b>\n                    <b class=\"fm-bold\">success = true,<\/b>\n                    <b class=\"fm-bold\">token = handler.WriteToken(token)<\/b>\n                <b class=\"fm-bold\">});<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return Unauthorized();<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        <b class=\"fm-bold\">private async Task&lt;bool&gt; CheckPassword(Credentials creds) {<\/b>\n            <b class=\"fm-bold\">IdentityUser? user<\/b> \n                <b class=\"fm-bold\">= await userManager.FindByNameAsync(creds.Username);<\/b>\n            <b class=\"fm-bold\">if (user != null) {<\/b>\n                <b class=\"fm-bold\">return (await signinManager.CheckPasswordSignInAsync(user,<\/b>\n                    <b class=\"fm-bold\">creds.Password, true)).Succeeded;<\/b>\n            <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">return false;<\/b>\n        <b class=\"fm-bold\">}<\/b>\n                \n        public class Credentials {\n                \n            public string Username { get; set; } = string.Empty;\n                        \n            public string Password { get; set; } = string.Empty;\n        }\n    }\n}<\/pre>\n<p class=\"body\">When the <code class=\"fm-code-in-text\">Token<\/code> action method is invoked, it passes the credentials to the <code class=\"fm-code-in-text\">CheckPassword<\/code> method, which enumerates the <code class=\"fm-code-in-text\">IPasswordValidator&lt;T&gt;<\/code> objects to invoke the <code class=\"fm-code-in-text\">ValidateAsync<\/code> method on each of them. If the password is validated by any of the validators, then the Token method creates a token.<\/p>\n<p class=\"body\">The JWT specification defines a general-purpose token that can be used more broadly than identifying users in HTTP requests, and many of the options that are available are not required for this example. The token that is created in listing 39.27 contains a payload like this:<\/p>\n<pre class=\"programlisting\">...\n{\n  \"unique_name\": \"bob\",\n  \"nbf\": 1579765454,\n  \"exp\": 1579851854,\n  \"iat\": 1579765454\n}\n...<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">unique_name<\/code> property contains the name of the user and is used to authenticate requests that contain the token. The other payload properties are timestamps, which I do not use.<\/p>\n<p class=\"body\">The payload is encrypted using the key defined in listing 39.27 and returned to the client as a JSON-encoded response that looks like this:<\/p>\n<pre class=\"programlisting\">...\n{\n    \"success\":true,\n    \"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"\n}\n...<\/pre>\n<p class=\"body\">I have shown just the first part of the token because they are long strings and it is the structure of the response that is important. The client receives the token and includes it in future requests using the <code class=\"fm-code-in-text\">Authorization<\/code> header, like this:<\/p>\n<pre class=\"programlisting\">...\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\n...<\/pre>\n<p class=\"body\">The server receives the token, decrypts it using the key, and authenticates the request using the value of the <code class=\"fm-code-in-text\">unique_name<\/code> property from the token payload. No further validation is performed, and requests with a valid token will be authenticated using whatever username is contained in the payload.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-730\">39.5.6 Authenticating with tokens<\/h3>\n<p class=\"body\">The next step is to configure the application to receive and validate the tokens, as shown in listing 39.28.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.28 Authenticating tokens in the Program.cs file in the Advanced project<\/p>\n<pre class=\"programlisting\">using Microsoft.EntityFrameworkCore;\nusing Advanced.Models;\nusing Microsoft.AspNetCore.Identity;\nusing Microsoft.AspNetCore.Authentication.Cookies;\n<b class=\"fm-bold\">using Microsoft.IdentityModel.Tokens;<\/b>\n<b class=\"fm-bold\">using System.Text;<\/b>\n<b class=\"fm-bold\">using System.Security.Claims;<\/b>\n<b class=\"fm-bold\">using Microsoft.AspNetCore.Authentication.JwtBearer;<\/b>\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddServerSideBlazor();\n\nbuilder.Services.AddDbContext&lt;DataContext&gt;(opts =&gt; {\n    opts.UseSqlServer(\n        builder.Configuration[\"ConnectionStrings:PeopleConnection\"]);\n    opts.EnableSensitiveDataLogging(true);\n});\n\nbuilder.Services.AddSingleton&lt;Advanced.Services.ToggleService&gt;();\n\nbuilder.Services.AddDbContext&lt;IdentityContext&gt;(opts =&gt;\n    opts.UseSqlServer(builder.Configuration[\n        \"ConnectionStrings:IdentityConnection\"]));\nbuilder.Services.AddIdentity&lt;IdentityUser, IdentityRole&gt;()\n    .AddEntityFrameworkStores&lt;IdentityContext&gt;();\n        \nbuilder.Services.Configure&lt;IdentityOptions&gt;(opts =&gt; {\n    opts.Password.RequiredLength = 6;\n    opts.Password.RequireNonAlphanumeric = false;\n    opts.Password.RequireLowercase = false;\n    opts.Password.RequireUppercase = false;\n    opts.Password.RequireDigit = false;\n    opts.User.RequireUniqueEmail = true;\n    opts.User.AllowedUserNameCharacters = \"abcdefghijklmnopqrstuvwxyz\";\n});\n\nbuilder.Services.AddAuthentication(opts =&gt; {\n    opts.DefaultScheme =\n        CookieAuthenticationDefaults.AuthenticationScheme;\n    opts.DefaultChallengeScheme =\n         CookieAuthenticationDefaults.AuthenticationScheme;\n}).AddCookie(opts =&gt; {\n    opts.Events.DisableRedirectForPath(e =&gt; e.OnRedirectToLogin,\n         \"\/api\", StatusCodes.Status401Unauthorized);\n    opts.Events.DisableRedirectForPath(e =&gt; e.OnRedirectToAccessDenied,\n        \"\/api\", StatusCodes.Status403Forbidden);\n<b class=\"fm-bold\">}).AddJwtBearer(opts =&gt; {<\/b>\n    <b class=\"fm-bold\">opts.RequireHttpsMetadata = false;<\/b>\n    <b class=\"fm-bold\">opts.SaveToken = true;<\/b>\n    <b class=\"fm-bold\">opts.TokenValidationParameters = new TokenValidationParameters {<\/b>\n        <b class=\"fm-bold\">ValidateIssuerSigningKey = true,<\/b>\n        <b class=\"fm-bold\">IssuerSigningKey = new SymmetricSecurityKey(<\/b>\n            <b class=\"fm-bold\">Encoding.ASCII.GetBytes(builder.Configuration[\"jwtSecret\"]!)),<\/b>\n        <b class=\"fm-bold\">ValidateAudience = false,<\/b>\n        <b class=\"fm-bold\">ValidateIssuer = false<\/b>\n    <b class=\"fm-bold\">};<\/b>\n    <b class=\"fm-bold\">opts.Events = new JwtBearerEvents {<\/b>\n        <b class=\"fm-bold\">OnTokenValidated = async ctx =&gt; {<\/b>\n            <b class=\"fm-bold\">var usrmgr = ctx.HttpContext.RequestServices<\/b>\n                <b class=\"fm-bold\">.GetRequiredService&lt;UserManager&lt;IdentityUser&gt;&gt;();<\/b>\n            <b class=\"fm-bold\">var signinmgr = ctx.HttpContext.RequestServices<\/b>\n                <b class=\"fm-bold\">.GetRequiredService&lt;SignInManager&lt;IdentityUser&gt;&gt;();<\/b>\n            <b class=\"fm-bold\">string? username =<\/b>\n                <b class=\"fm-bold\">ctx.Principal?.FindFirst(ClaimTypes.Name)?.Value;<\/b>\n            <b class=\"fm-bold\">if (username != null) {<\/b>\n                <b class=\"fm-bold\">IdentityUser? idUser =<\/b> \n                    <b class=\"fm-bold\">await usrmgr.FindByNameAsync(username);<\/b>\n                <b class=\"fm-bold\">if (idUser != null) {<\/b>\n                    <b class=\"fm-bold\">ctx.Principal =<\/b>\n                        <b class=\"fm-bold\">await signinmgr.CreateUserPrincipalAsync(idUser);<\/b>\n                <b class=\"fm-bold\">}<\/b>\n            <b class=\"fm-bold\">}<\/b>\n        <b class=\"fm-bold\">}<\/b>\n    <b class=\"fm-bold\">};<\/b>\n<b class=\"fm-bold\">});<\/b>\n\nvar app = builder.Build();\n\napp.UseStaticFiles();\n\napp.UseAuthentication();\napp.UseAuthorization();\n\napp.MapControllers();\napp.MapControllerRoute(\"controllers\",\n    \"controllers\/{controller=Home}\/{action=Index}\/{id?}\");\napp.MapRazorPages();\napp.MapBlazorHub();\napp.MapFallbackToPage(\"\/_Host\");\n\napp.UseBlazorFrameworkFiles(\"\/webassembly\");\napp.MapFallbackToFile(\"\/webassembly\/{*path:nonfile}\",\n    \"\/webassembly\/index.xhtml\");\n        \nvar context = app.Services.CreateScope().ServiceProvider\n    .GetRequiredService&lt;DataContext&gt;();\nSeedData.SeedDatabase(context);\n\nIdentitySeedData.CreateAdminAccount(app.Services, app.Configuration);\napp.Run();<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AddJwtBearer<\/code> adds support for JWT to the authentication system and provides the settings required to decrypt tokens. I have added a handler for the <code class=\"fm-code-in-text\">OnTokenValidated<\/code> event, which is triggered when a token is validated so that I can query the user database and associate the <code class=\"fm-code-in-text\">IdentityUser<\/code> object with the request. This acts as a bridge between the JWT tokens and the ASP.NET Core Identity data, ensuring that features like role-based authorization work seamlessly.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-731\">39.5.7 Restricting access with tokens<\/h3>\n<p class=\"body\">To allow a restricted endpoint to be accessed with tokens, I have modified the <code class=\"fm-code-in-text\">Authorize<\/code> attribute applied to the <code class=\"fm-code-in-text\">Data<\/code> controller, as shown in listing 39.29.<a id=\"calibre_link-2978\"><\/a><a id=\"calibre_link-753\"><\/a><\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.29 Enabling tokens in the DataController.cs file in the Controllers folder of the Advanced project<\/p>\n<pre class=\"programlisting\">using Advanced.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Authorization;\n\nnamespace Advanced.Controllers {\n\n    [ApiController]\n    [Route(\"\/api\/people\")]\n    <b class=\"fm-bold\">[Authorize(AuthenticationSchemes = \"Identity.Application, Bearer\")]<\/b>\n    public class DataController : ControllerBase {\n        private DataContext context;\n        \/\/ ...methods omitted for brevity...\n    }\n}<\/pre>\n<p class=\"body\">The <code class=\"fm-code-in-text\">AuthenticationSchemes<\/code> argument is used to specify the types of authentication that can be used to authorize access to the controller. In this case, I have specified that the default cookie authentication and the new bearer tokens can be used.<\/p>\n<h3 class=\"fm-head1\" id=\"calibre_link-732\">39.5.8 Using tokens to request data<\/h3>\n<p class=\"body\">The final step is to update the JavaScript client so that it obtains a token and includes it in requests for data, as shown in listing 39.30.<\/p>\n<p class=\"fm-code-listing-caption\">Listing 39.30 Using tokens in the webclient.js file in the wwwroot folder of the Advanced project<\/p>\n<pre class=\"programlisting\">const username = \"bob\";\nconst password = \"secret\";\n<b class=\"fm-bold\">let token;<\/b>\n\nwindow.addEventListener(\"DOMContentLoaded\", () =&gt; {\n    const controlDiv = document.getElementById(\"controls\");\n    createButton(controlDiv, \"Get Data\", getData);\n    createButton(controlDiv, \"Log In\", login);\n    createButton(controlDiv, \"Log Out\", logout);\n});\n\nasync function login() {\n    <b class=\"fm-bold\">let response = await fetch(\"\/api\/account\/token\", {<\/b>\n        <b class=\"fm-bold\">method: \"POST\",<\/b>\n        <b class=\"fm-bold\">headers: { \"Content-Type\": \"application\/json\" },<\/b>\n        <b class=\"fm-bold\">body: JSON.stringify({ username: username, password: password })<\/b>\n    <b class=\"fm-bold\">});<\/b>\n    if (response.ok) {\n        <b class=\"fm-bold\">token = (await response.json()).token;<\/b>\n        <b class=\"fm-bold\">displayData(\"Logged in\", token);<\/b>\n    } else {\n        displayData(`Error: ${response.status}: ${response.statusText}`);\n    }\n}\n\n<b class=\"fm-bold\">async function logout() {<\/b>\n    <b class=\"fm-bold\">token = \"\";<\/b>\n    <b class=\"fm-bold\">displayData(\"Logged out\");<\/b>\n<b class=\"fm-bold\">}<\/b>\n\nasync function getData() {\n    <b class=\"fm-bold\">let response = await fetch(\"\/api\/people\", {<\/b>\n        <b class=\"fm-bold\">headers: { \"Authorization\": 'Bearer ${token}' }<\/b>\n    <b class=\"fm-bold\">});<\/b>\n    if (response.ok) {\n        let jsonData = await response.json();\n        displayData(...jsonData.map(item =&gt; \n            `${item.surname}, ${item.firstname}`));\n    } else {\n        displayData(`Error: ${response.status}: ${response.statusText}`);\n    }\n}\n\nfunction displayData(...items) {\n    <span lang=\"it-IT\">const dataDiv = document.getElementById(\"data\");<\/span>\n    dataDiv.innerHTML = \"\";\n    items.forEach(item =&gt; {\n        const itemDiv = document.createElement(\"div\");\n        itemDiv.innerText = item;\n        itemDiv.style.wordWrap = \"break-word\";\n        dataDiv.appendChild(itemDiv);\n    })\n}\n\nfunction createButton(parent, label, handler) {\n    const button = document.createElement(\"button\");\n    button.classList.add(\"btn\", \"btn-primary\",  \"m-2\");\n    button.innerText = label;\n    button.onclick = handler;\n    parent.appendChild(button);\n}<\/pre>\n<p class=\"body\">The client receives the authentication response and assigns the token so it can be used by the <code class=\"fm-code-in-text\">GetData<\/code> method, which sets the <code class=\"fm-code-in-text\">Authorization<\/code> header. Notice that no logout request is required, and the variable used to store the token is simply reset when the user clicks the Log Out button.<\/p>\n<p class=\"fm-sidebar-text\"><span class=\"fm-callout-head\">Caution<\/span> It is easy to end up authenticating with a cookie when attempting to test tokens. Make sure you clear your browser cookies before testing this feature to ensure that cookies from previous tests are not used.<\/p>\n<p class=\"body\">Restart ASP.NET Core and request http:\/\/localhost:5000\/webclient.xhtml. Click the Log In button, and a token will be generated and displayed. Click the Get Data button, and the token will be sent to the server and used to authenticate the user, producing the results shown in figure 39.13.<\/p>\n<div class=\"figure\">\n<p class=\"figure1\"><img decoding=\"async\" alt=\"\" class=\"calibre434\" src=\"\/images\/proaspnetcore7\/000440.png\" \/><\/p>\n<p class=\"figurecaption\">Figure 39.13 Using a token for authentication<\/p>\n<\/div>\n<h2 class=\"fm-head\" id=\"calibre_link-2979\">Summary<\/h2>\n<ul class=\"calibre6\">\n<li class=\"fm-list-bullet\">\n<p class=\"list\">User credentials are validated using the <code class=\"fm-code-in-text\">SignInManager<\/code> service and successful authentications usually generate a cookie that will be included in subsequent requests.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Identity supports alternatives to cookies, including bearer tokens for use with web services and JavaScript clients.<\/p>\n<\/li>\n<li class=\"fm-list-bullet\">\n<p class=\"list\">Access controls are applied to ensure that only authorized users can request restricted actions, pages, or components. Authorization policies are specified using attributes.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"calibre\" id=\"calibre_link-733\">\n<div class=\"calibre1\" id=\"calibre_link-2980\">\n<h1 class=\"tochead\" id=\"calibre_link-2981\">index<\/h1>\n<p class=\"grouptitlesix\">A<\/p>\n<p class=\"level1ix\">Action methods <a class=\"url1\" href=\"\/#calibre_link-734\">500<\/a><\/p>\n<p class=\"level1ix\">Action Results <a class=\"url1\" href=\"\/#calibre_link-735\">514<\/a><\/p>\n<p class=\"level2ix\">redirection <a class=\"url1\" href=\"\/#calibre_link-735\">514<\/a><\/p>\n<p class=\"level2ix\">sending files <a class=\"url1\" href=\"\/#calibre_link-735\">514<\/a><\/p>\n<p class=\"level2ix\">status codes <a class=\"url1\" href=\"\/#calibre_link-735\">514<\/a><\/p>\n<p class=\"level1ix\">ActivatorUtilities class<\/p>\n<p class=\"level2ix\">resolving services <a class=\"url1\" href=\"\/#calibre_link-736\">362<\/a><\/p>\n<p class=\"level1ix\">Angular <a class=\"url1\" href=\"\/#calibre_link-737\">3<\/a><\/p>\n<p class=\"level1ix\">appsettings.Development.json File <a class=\"url1\" href=\"\/#calibre_link-738\">388<\/a><\/p>\n<p class=\"level1ix\">appsettings.json File <a class=\"url1\" href=\"\/#calibre_link-739\">387<\/a><\/p>\n<p class=\"level1ix\">Arrays<\/p>\n<p class=\"level2ix\">model binding <a class=\"url1\" href=\"\/#calibre_link-740\">816<\/a><\/p>\n<p class=\"level1ix\">ASP.NET Core<\/p>\n<p class=\"level2ix\">application frameworks <a class=\"url1\" href=\"\/#calibre_link-737\">3<\/a><\/p>\n<p class=\"level3ix\">Blazor <a class=\"url1\" href=\"\/#calibre_link-741\">4<\/a><\/p>\n<p class=\"level3ix\">MVC framework <a class=\"url1\" href=\"\/#calibre_link-737\">3<\/a><\/p>\n<p class=\"level3ix\">Razor Pages <a class=\"url1\" href=\"\/#calibre_link-741\">4<\/a><\/p>\n<p class=\"level2ix\">architecture <a class=\"url1\" href=\"\/#calibre_link-742\">1<\/a><\/p>\n<p class=\"level2ix\">gRPC <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a><\/p>\n<p class=\"level2ix\">platform <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a><\/p>\n<p class=\"level2ix\">SignalR <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a><\/p>\n<p class=\"level2ix\">utility frameworks<\/p>\n<p class=\"level3ix\">ASP.NET Core Identity <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a><\/p>\n<p class=\"level3ix\">Entity Framework Core <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a><\/p>\n<p class=\"level1ix\">ASP.NET Core Identity <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a><\/p>\n<p class=\"level2ix\">access denied endpoint <a class=\"url1\" href=\"\/#calibre_link-744\">1184<\/a><\/p>\n<p class=\"level2ix\">application configuration <a class=\"url1\" href=\"\/#calibre_link-745\">1147<\/a><\/p>\n<p class=\"level2ix\">authentication <a class=\"url1\" href=\"\/#calibre_link-746\">1174<\/a><\/p>\n<p class=\"level2ix\">authentication cookie <a class=\"url1\" href=\"\/#calibre_link-747\">1177<\/a><\/p>\n<p class=\"level2ix\">authentication middleware <a class=\"url1\" href=\"\/#calibre_link-748\">1180<\/a><\/p>\n<p class=\"level2ix\">authentication vs authorization <a class=\"url1\" href=\"\/#calibre_link-749\">1175<\/a><\/p>\n<p class=\"level2ix\">authN <a class=\"url1\" href=\"\/#calibre_link-749\">1175<\/a><\/p>\n<p class=\"level2ix\">authorization <a class=\"url1\" href=\"\/#calibre_link-750\">1183<\/a><\/p>\n<p class=\"level3ix\">Authorize attribute <a class=\"url1\" href=\"\/#calibre_link-750\">1183<\/a><\/p>\n<p class=\"level3ix\">endpoints <a class=\"url1\" href=\"\/#calibre_link-750\">1183<\/a><\/p>\n<p class=\"level3ix\">web services <a class=\"url1\" href=\"\/#calibre_link-751\">1194<\/a><\/p>\n<p class=\"level2ix\">authorization middleware <a class=\"url1\" href=\"\/#calibre_link-744\">1184<\/a><\/p>\n<p class=\"level2ix\">Authorize attribute <a class=\"url1\" href=\"\/#calibre_link-752\">1198<\/a><\/p>\n<p class=\"level3ix\">AuthenticationSchemes argument <a class=\"url1\" href=\"\/#calibre_link-753\">1208<\/a><\/p>\n<p class=\"level3ix\">Roles argument <a class=\"url1\" href=\"\/#calibre_link-750\">1183<\/a><\/p>\n<p class=\"level2ix\">authZ <a class=\"url1\" href=\"\/#calibre_link-749\">1175<\/a><\/p>\n<p class=\"level2ix\">Blazor applications <a class=\"url1\" href=\"\/#calibre_link-754\">1188<\/a><\/p>\n<p class=\"level2ix\">changing passwords <a class=\"url1\" href=\"\/#calibre_link-755\">1161<\/a><\/p>\n<p class=\"level2ix\">ClaimsPrincipal class <a class=\"url1\" href=\"\/#calibre_link-756\">1181<\/a><\/p>\n<p class=\"level2ix\">creating<\/p>\n<p class=\"level3ix\">roles <a class=\"url1\" href=\"\/#calibre_link-757\">1167<\/a><\/p>\n<p class=\"level3ix\">users <a class=\"url1\" href=\"\/#calibre_link-758\">1152<\/a><\/p>\n<p class=\"level2ix\">database<\/p>\n<p class=\"level3ix\">configuration<\/p>\n<p class=\"level3ix\">migration <a class=\"url1\" href=\"\/#calibre_link-759\">1148<\/a><\/p>\n<p class=\"level3ix\">reset <a class=\"url1\" href=\"\/#calibre_link-759\">1148<\/a><\/p>\n<p class=\"level2ix\">deleting<\/p>\n<p class=\"level3ix\">roles <a class=\"url1\" href=\"\/#calibre_link-760\">1165<\/a><\/p>\n<p class=\"level3ix\">users <a class=\"url1\" href=\"\/#calibre_link-761\">1162<\/a><\/p>\n<p class=\"level2ix\">disabling redirection <a class=\"url1\" href=\"\/#calibre_link-762\">1196<\/a><\/p>\n<p class=\"level2ix\">editing<\/p>\n<p class=\"level3ix\">roles <a class=\"url1\" href=\"\/#calibre_link-763\">1168<\/a><\/p>\n<p class=\"level3ix\">users <a class=\"url1\" href=\"\/#calibre_link-764\">1160<\/a><\/p>\n<p class=\"level2ix\">Entity Framework Core <a class=\"url1\" href=\"\/#calibre_link-759\">1148<\/a><\/p>\n<p class=\"level2ix\">enumerating<\/p>\n<p class=\"level3ix\">roles <a class=\"url1\" href=\"\/#calibre_link-760\">1165<\/a><\/p>\n<p class=\"level3ix\">users <a class=\"url1\" href=\"\/#calibre_link-765\">1151<\/a>, <a class=\"url1\" href=\"\/#calibre_link-766\">1157<\/a><\/p>\n<p class=\"level2ix\">IdentityResult class <a class=\"url1\" href=\"\/#calibre_link-767\">1154<\/a><\/p>\n<p class=\"level2ix\">IdentityRole class <a class=\"url1\" href=\"\/#calibre_link-768\">1164<\/a><\/p>\n<p class=\"level2ix\">Identity Server <a class=\"url1\" href=\"\/#calibre_link-754\">1188<\/a><\/p>\n<p class=\"level2ix\">IdentityUser class <a class=\"url1\" href=\"\/#calibre_link-769\">1149<\/a><\/p>\n<p class=\"level2ix\">importance of HTTPS <a class=\"url1\" href=\"\/#calibre_link-747\">1177<\/a><\/p>\n<p class=\"level2ix\">logging in <a class=\"url1\" href=\"\/#calibre_link-749\">1175<\/a><\/p>\n<p class=\"level2ix\">logging out <a class=\"url1\" href=\"\/#calibre_link-749\">1175<\/a>, <a class=\"url1\" href=\"\/#calibre_link-770\">1178<\/a><\/p>\n<p class=\"level2ix\">management tools <a class=\"url1\" href=\"\/#calibre_link-769\">1149<\/a><\/p>\n<p class=\"level2ix\">OAuth <a class=\"url1\" href=\"\/#calibre_link-754\">1188<\/a><\/p>\n<p class=\"level2ix\">options pattern <a class=\"url1\" href=\"\/#calibre_link-766\">1157<\/a><\/p>\n<p class=\"level2ix\">package, adding <a class=\"url1\" href=\"\/#calibre_link-771\">1145<\/a><\/p>\n<p class=\"level2ix\">PasswordOptions class <a class=\"url1\" href=\"\/#calibre_link-766\">1157<\/a><\/p>\n<p class=\"level2ix\">password validation <a class=\"url1\" href=\"\/#calibre_link-772\">1155<\/a><\/p>\n<p class=\"level2ix\">redirection URLs <a class=\"url1\" href=\"\/#calibre_link-754\">1188<\/a><\/p>\n<p class=\"level2ix\">RoleManager&lt;T&gt; class <a class=\"url1\" href=\"\/#calibre_link-768\">1164<\/a><\/p>\n<p class=\"level2ix\">role membership <a class=\"url1\" href=\"\/#calibre_link-763\">1168<\/a><\/p>\n<p class=\"level2ix\">scaffolding <a class=\"url1\" href=\"\/#calibre_link-769\">1149<\/a><\/p>\n<p class=\"level2ix\">seed data <a class=\"url1\" href=\"\/#calibre_link-773\">1185<\/a><\/p>\n<p class=\"level2ix\">service configuration <a class=\"url1\" href=\"\/#calibre_link-745\">1147<\/a><\/p>\n<p class=\"level2ix\">SignInManager&lt;T&gt; class <a class=\"url1\" href=\"\/#calibre_link-774\">1176<\/a><\/p>\n<p class=\"level2ix\">two-factor authentication <a class=\"url1\" href=\"\/#calibre_link-775\">1182<\/a><\/p>\n<p class=\"level2ix\">UserManager&lt;T&gt; class <a class=\"url1\" href=\"\/#calibre_link-769\">1149<\/a>, <a class=\"url1\" href=\"\/#calibre_link-768\">1164<\/a><\/p>\n<p class=\"level2ix\">UserOptions class <a class=\"url1\" href=\"\/#calibre_link-776\">1159<\/a><\/p>\n<p class=\"level2ix\">validating user details <a class=\"url1\" href=\"\/#calibre_link-777\">1158<\/a><\/p>\n<p class=\"level2ix\">web services <a class=\"url1\" href=\"\/#calibre_link-751\">1194<\/a><\/p>\n<p class=\"level3ix\">bearer tokens <a class=\"url1\" href=\"\/#calibre_link-778\">1202<\/a><\/p>\n<p class=\"level3ix\">cookie authentication <a class=\"url1\" href=\"\/#calibre_link-779\">1199<\/a><\/p>\n<p class=\"level3ix\">JSON Web Token <a class=\"url1\" href=\"\/#calibre_link-778\">1202<\/a><\/p>\n<p class=\"grouptitlesix\">B<\/p>\n<p class=\"level1ix\">Blazor <a class=\"url1\" href=\"\/#calibre_link-741\">4<\/a>, <a class=\"url1\" href=\"\/#calibre_link-780\">977<\/a><\/p>\n<p class=\"level2ix\">advantages <a class=\"url1\" href=\"\/#calibre_link-781\">978<\/a><\/p>\n<p class=\"level2ix\">application errors<\/p>\n<p class=\"level3ix\">element ID <a class=\"url1\" href=\"\/#calibre_link-782\">1035<\/a><\/p>\n<p class=\"level3ix\">error boundaries <a class=\"url1\" href=\"\/#calibre_link-783\">1036<\/a><\/p>\n<p class=\"level2ix\">architecture <a class=\"url1\" href=\"\/#calibre_link-780\">977<\/a><\/p>\n<p class=\"level2ix\">attributes<\/p>\n<p class=\"level3ix\">parameters <a class=\"url1\" href=\"\/#calibre_link-784\">992<\/a><\/p>\n<p class=\"level2ix\">attribute splatting <a class=\"url1\" href=\"\/#calibre_link-785\">1010<\/a><\/p>\n<p class=\"level2ix\">authorization <a class=\"url1\" href=\"\/#calibre_link-754\">1188<\/a><\/p>\n<p class=\"level2ix\">AuthorizeRouteView component <a class=\"url1\" href=\"\/#calibre_link-786\">1190<\/a><\/p>\n<p class=\"level2ix\">AuthorizeView component <a class=\"url1\" href=\"\/#calibre_link-787\">1192<\/a><\/p>\n<p class=\"level2ix\">bindings <a class=\"url1\" href=\"\/#calibre_link-788\">983<\/a>, <a class=\"url1\" href=\"\/#calibre_link-789\">994<\/a><\/p>\n<p class=\"level3ix\">configuring <a class=\"url1\" href=\"\/#calibre_link-790\">996<\/a><\/p>\n<p class=\"level3ix\">DateTime bindings <a class=\"url1\" href=\"\/#calibre_link-791\">997<\/a><\/p>\n<p class=\"level3ix\">defining <a class=\"url1\" href=\"\/#calibre_link-790\">996<\/a><\/p>\n<p class=\"level2ix\">Blazor Server <a class=\"url1\" href=\"\/#calibre_link-741\">4<\/a><\/p>\n<p class=\"level2ix\">Blazor WebAssembly <a class=\"url1\" href=\"\/#calibre_link-741\">4<\/a><\/p>\n<p class=\"level2ix\">cascading parameters <a class=\"url1\" href=\"\/#calibre_link-792\">1028<\/a><\/p>\n<p class=\"level2ix\">client-side code <a class=\"url1\" href=\"\/#calibre_link-793\">979<\/a><\/p>\n<p class=\"level2ix\">component<\/p>\n<p class=\"level3ix\">applying <a class=\"url1\" href=\"\/#calibre_link-788\">983<\/a><\/p>\n<p class=\"level3ix\">attributes <a class=\"url1\" href=\"\/#calibre_link-794\">1008<\/a><\/p>\n<p class=\"level3ix\">bulk attributes <a class=\"url1\" href=\"\/#calibre_link-795\">1009<\/a><\/p>\n<p class=\"level3ix\">cascading parameters <a class=\"url1\" href=\"\/#calibre_link-792\">1028<\/a><\/p>\n<p class=\"level3ix\">child content <a class=\"url1\" href=\"\/#calibre_link-796\">1017<\/a><\/p>\n<p class=\"level3ix\">@ChildContent expression <a class=\"url1\" href=\"\/#calibre_link-796\">1017<\/a><\/p>\n<p class=\"level3ix\">code section <a class=\"url1\" href=\"\/#calibre_link-797\">982<\/a><\/p>\n<p class=\"level3ix\">combining <a class=\"url1\" href=\"\/#calibre_link-798\">1006<\/a><\/p>\n<p class=\"level3ix\">configuration <a class=\"url1\" href=\"\/#calibre_link-794\">1008<\/a><\/p>\n<p class=\"level3ix\">content section <a class=\"url1\" href=\"\/#calibre_link-797\">982<\/a><\/p>\n<p class=\"level3ix\">creating <a class=\"url1\" href=\"\/#calibre_link-799\">981<\/a><\/p>\n<p class=\"level3ix\">custom bindings <a class=\"url1\" href=\"\/#calibre_link-800\">1012<\/a>, <a class=\"url1\" href=\"\/#calibre_link-801\">1015<\/a><\/p>\n<p class=\"level3ix\">custom events <a class=\"url1\" href=\"\/#calibre_link-800\">1012<\/a><\/p>\n<p class=\"level3ix\">generic templates <a class=\"url1\" href=\"\/#calibre_link-802\">1022<\/a><\/p>\n<p class=\"level3ix\">@key expression <a class=\"url1\" href=\"\/#calibre_link-803\">1018<\/a><\/p>\n<p class=\"level3ix\">restricting element reuse <a class=\"url1\" href=\"\/#calibre_link-803\">1018<\/a><\/p>\n<p class=\"level3ix\">templates <a class=\"url1\" href=\"\/#calibre_link-804\">1019<\/a><\/p>\n<p class=\"level2ix\">components<\/p>\n<p class=\"level3ix\">code-only components <a class=\"url1\" href=\"\/#calibre_link-805\">1002<\/a><\/p>\n<p class=\"level3ix\">custom form components <a class=\"url1\" href=\"\/#calibre_link-806\">1088<\/a><\/p>\n<p class=\"level3ix\">DataAnnotationsValidator component <a class=\"url1\" href=\"\/#calibre_link-807\">1092<\/a><\/p>\n<p class=\"level3ix\">element references <a class=\"url1\" href=\"\/#calibre_link-808\">1073<\/a><\/p>\n<p class=\"level3ix\">forms <a class=\"url1\" href=\"\/#calibre_link-809\">1085<\/a><\/p>\n<p class=\"level3ix\">InvokeAsync method <a class=\"url1\" href=\"\/#calibre_link-810\">1066<\/a><\/p>\n<p class=\"level3ix\">invoking from other code <a class=\"url1\" href=\"\/#calibre_link-811\">1065<\/a><\/p>\n<p class=\"level3ix\">JavaScript <a class=\"url1\" href=\"\/#calibre_link-812\">1070<\/a><\/p>\n<p class=\"level2ix\">DotNetObjectReference class <a class=\"url1\" href=\"\/#calibre_link-813\">1078<\/a><\/p>\n<p class=\"level2ix\">InvokeAsync method <a class=\"url1\" href=\"\/#calibre_link-814\">1072<\/a><\/p>\n<p class=\"level2ix\">InvokeVoidAsync method <a class=\"url1\" href=\"\/#calibre_link-814\">1072<\/a><\/p>\n<p class=\"level3ix\">LayoutView component <a class=\"url1\" href=\"\/#calibre_link-815\">1056<\/a><\/p>\n<p class=\"level3ix\">partial classes <a class=\"url1\" href=\"\/#calibre_link-816\">1000<\/a><\/p>\n<p class=\"level3ix\">StateHasChanged method <a class=\"url1\" href=\"\/#calibre_link-810\">1066<\/a><\/p>\n<p class=\"level3ix\">ValidationMessage component <a class=\"url1\" href=\"\/#calibre_link-807\">1092<\/a><\/p>\n<p class=\"level3ix\">ValidationSummary component <a class=\"url1\" href=\"\/#calibre_link-807\">1092<\/a><\/p>\n<p class=\"level2ix\">component tag helper <a class=\"url1\" href=\"\/#calibre_link-788\">983<\/a><\/p>\n<p class=\"level2ix\">connection errors <a class=\"url1\" href=\"\/#calibre_link-817\">1031<\/a><\/p>\n<p class=\"level3ix\">element classes <a class=\"url1\" href=\"\/#calibre_link-818\">1032<\/a><\/p>\n<p class=\"level3ix\">element ID <a class=\"url1\" href=\"\/#calibre_link-818\">1032<\/a><\/p>\n<p class=\"level3ix\">testing with Fiddler <a class=\"url1\" href=\"\/#calibre_link-819\">1033<\/a><\/p>\n<p class=\"level2ix\">data validation<\/p>\n<p class=\"level3ix\">components <a class=\"url1\" href=\"\/#calibre_link-807\">1092<\/a><\/p>\n<p class=\"level3ix\">element classes <a class=\"url1\" href=\"\/#calibre_link-807\">1092<\/a><\/p>\n<p class=\"level2ix\">disadvantages <a class=\"url1\" href=\"\/#calibre_link-781\">978<\/a><\/p>\n<p class=\"level2ix\">EditForm component <a class=\"url1\" href=\"\/#calibre_link-809\">1085<\/a><\/p>\n<p class=\"level2ix\">enabling <a class=\"url1\" href=\"\/#calibre_link-793\">979<\/a><\/p>\n<p class=\"level2ix\">Entity Framework Core<\/p>\n<p class=\"level3ix\">dependency injection scopes <a class=\"url1\" href=\"\/#calibre_link-820\">1098<\/a><\/p>\n<p class=\"level3ix\">repeated queries <a class=\"url1\" href=\"\/#calibre_link-821\">1103<\/a><\/p>\n<p class=\"level2ix\">errors <a class=\"url1\" href=\"\/#calibre_link-817\">1031<\/a><\/p>\n<p class=\"level2ix\">events<\/p>\n<p class=\"level3ix\">attributes <a class=\"url1\" href=\"\/#calibre_link-822\">986<\/a><\/p>\n<p class=\"level3ix\">default actions <a class=\"url1\" href=\"\/#calibre_link-784\">992<\/a><\/p>\n<p class=\"level3ix\">event types <a class=\"url1\" href=\"\/#calibre_link-822\">986<\/a><\/p>\n<p class=\"level3ix\">handler methods names <a class=\"url1\" href=\"\/#calibre_link-823\">989<\/a><\/p>\n<p class=\"level3ix\">handler parameters <a class=\"url1\" href=\"\/#calibre_link-824\">988<\/a><\/p>\n<p class=\"level3ix\">inline expressions <a class=\"url1\" href=\"\/#calibre_link-825\">991<\/a><\/p>\n<p class=\"level3ix\">parameters <a class=\"url1\" href=\"\/#calibre_link-784\">992<\/a><\/p>\n<p class=\"level3ix\">propagation <a class=\"url1\" href=\"\/#calibre_link-784\">992<\/a><\/p>\n<p class=\"level2ix\">form features <a class=\"url1\" href=\"\/#calibre_link-809\">1085<\/a><\/p>\n<p class=\"level2ix\">forms<\/p>\n<p class=\"level3ix\">EditContext features <a class=\"url1\" href=\"\/#calibre_link-826\">1115<\/a><\/p>\n<p class=\"level2ix\">forms<\/p>\n<p class=\"level3ix\">CRUD operations <a class=\"url1\" href=\"\/#calibre_link-827\">1109<\/a><\/p>\n<p class=\"level2ix\">HTTP connection <a class=\"url1\" href=\"\/#calibre_link-780\">977<\/a><\/p>\n<p class=\"level2ix\">hub <a class=\"url1\" href=\"\/#calibre_link-793\">979<\/a><\/p>\n<p class=\"level2ix\">imports file, creating <a class=\"url1\" href=\"\/#calibre_link-828\">980<\/a><\/p>\n<p class=\"level2ix\">InputCheckbox component <a class=\"url1\" href=\"\/#calibre_link-809\">1085<\/a><\/p>\n<p class=\"level2ix\">InputDate component <a class=\"url1\" href=\"\/#calibre_link-809\">1085<\/a><\/p>\n<p class=\"level2ix\">InputNumber component <a class=\"url1\" href=\"\/#calibre_link-809\">1085<\/a><\/p>\n<p class=\"level2ix\">InputTextArea component <a class=\"url1\" href=\"\/#calibre_link-809\">1085<\/a><\/p>\n<p class=\"level2ix\">InputText component <a class=\"url1\" href=\"\/#calibre_link-809\">1085<\/a><\/p>\n<p class=\"level2ix\">JavaScript file <a class=\"url1\" href=\"\/#calibre_link-793\">979<\/a><\/p>\n<p class=\"level2ix\">@layout expression <a class=\"url1\" href=\"\/#calibre_link-815\">1056<\/a><\/p>\n<p class=\"level2ix\">layouts <a class=\"url1\" href=\"\/#calibre_link-815\">1056<\/a><\/p>\n<p class=\"level2ix\">lifecycle <a class=\"url1\" href=\"\/#calibre_link-829\">1057<\/a><\/p>\n<p class=\"level3ix\">methods <a class=\"url1\" href=\"\/#calibre_link-829\">1057<\/a><\/p>\n<p class=\"level2ix\">Parameter attribute <a class=\"url1\" href=\"\/#calibre_link-794\">1008<\/a>, <a class=\"url1\" href=\"\/#calibre_link-795\">1009<\/a><\/p>\n<p class=\"level2ix\">@ref expression <a class=\"url1\" href=\"\/#calibre_link-830\">1062<\/a><\/p>\n<p class=\"level2ix\">rendering modes <a class=\"url1\" href=\"\/#calibre_link-788\">983<\/a><\/p>\n<p class=\"level2ix\">retaining references<\/p>\n<p class=\"level3ix\">@ref expression <a class=\"url1\" href=\"\/#calibre_link-830\">1062<\/a><\/p>\n<p class=\"level2ix\">routing <a class=\"url1\" href=\"\/#calibre_link-831\">1044<\/a><\/p>\n<p class=\"level3ix\">configuring fallback <a class=\"url1\" href=\"\/#calibre_link-832\">1046<\/a><\/p>\n<p class=\"level3ix\">default route <a class=\"url1\" href=\"\/#calibre_link-833\">1049<\/a><\/p>\n<p class=\"level3ix\">layouts <a class=\"url1\" href=\"\/#calibre_link-834\">1055<\/a>, <a class=\"url1\" href=\"\/#calibre_link-815\">1056<\/a><\/p>\n<p class=\"level3ix\">MapFallbackToPage method <a class=\"url1\" href=\"\/#calibre_link-835\">1047<\/a><\/p>\n<p class=\"level3ix\">navigation <a class=\"url1\" href=\"\/#calibre_link-836\">1050<\/a><\/p>\n<p class=\"level3ix\">NavigationManager service <a class=\"url1\" href=\"\/#calibre_link-837\">1051<\/a><\/p>\n<p class=\"level3ix\">NavLink component <a class=\"url1\" href=\"\/#calibre_link-836\">1050<\/a>, <a class=\"url1\" href=\"\/#calibre_link-834\">1055<\/a><\/p>\n<p class=\"level3ix\">@page directive <a class=\"url1\" href=\"\/#calibre_link-835\">1047<\/a><\/p>\n<p class=\"level3ix\">receiving route data <a class=\"url1\" href=\"\/#calibre_link-838\">1053<\/a><\/p>\n<p class=\"level3ix\">Router component <a class=\"url1\" href=\"\/#calibre_link-831\">1044<\/a><\/p>\n<p class=\"level3ix\">RouteView component <a class=\"url1\" href=\"\/#calibre_link-831\">1044<\/a><\/p>\n<p class=\"level2ix\">WebAssembly<\/p>\n<p class=\"level3ix\">base element <a class=\"url1\" href=\"\/#calibre_link-839\">1129<\/a><\/p>\n<p class=\"level3ix\">base URL <a class=\"url1\" href=\"\/#calibre_link-839\">1129<\/a><\/p>\n<p class=\"level3ix\">components <a class=\"url1\" href=\"\/#calibre_link-840\">1131<\/a><\/p>\n<p class=\"level3ix\">CSS styles <a class=\"url1\" href=\"\/#calibre_link-841\">1137<\/a><\/p>\n<p class=\"level3ix\">HttpClient service <a class=\"url1\" href=\"\/#calibre_link-842\">1134<\/a><\/p>\n<p class=\"level3ix\">layout <a class=\"url1\" href=\"\/#calibre_link-843\">1136<\/a><\/p>\n<p class=\"level3ix\">navigation URLs <a class=\"url1\" href=\"\/#calibre_link-844\">1133<\/a><\/p>\n<p class=\"level3ix\">placeholder components <a class=\"url1\" href=\"\/#calibre_link-845\">1130<\/a><\/p>\n<p class=\"level3ix\">setup <a class=\"url1\" href=\"\/#calibre_link-846\">1126<\/a>, <a class=\"url1\" href=\"\/#calibre_link-847\">1128<\/a><\/p>\n<p class=\"level3ix\">shared code <a class=\"url1\" href=\"\/#calibre_link-846\">1126<\/a><\/p>\n<p class=\"level1ix\">Blazor Server <a class=\"url1\" href=\"\/#calibre_link-780\">977<\/a><\/p>\n<p class=\"level1ix\">Blazor WebAssembly <a class=\"url1\" href=\"\/#calibre_link-846\">1126<\/a><\/p>\n<p class=\"level1ix\">Bootstrap CSS framework <a class=\"url1\" href=\"\/#calibre_link-848\">967<\/a><\/p>\n<p class=\"grouptitlesix\">C<\/p>\n<p class=\"level1ix\">C#<\/p>\n<p class=\"level2ix\">anonymous types <a class=\"url1\" href=\"\/#calibre_link-849\">107<\/a><\/p>\n<p class=\"level2ix\">asynchronous methods <a class=\"url1\" href=\"\/#calibre_link-850\">111<\/a><\/p>\n<p class=\"level3ix\">asynchronous enumerable <a class=\"url1\" href=\"\/#calibre_link-851\">114<\/a><\/p>\n<p class=\"level3ix\">async keyword <a class=\"url1\" href=\"\/#calibre_link-852\">112<\/a><\/p>\n<p class=\"level3ix\">await keyword <a class=\"url1\" href=\"\/#calibre_link-852\">112<\/a><\/p>\n<p class=\"level3ix\">tasks <a class=\"url1\" href=\"\/#calibre_link-850\">111<\/a><\/p>\n<p class=\"level2ix\">collection initializers <a class=\"url1\" href=\"\/#calibre_link-853\">90<\/a><\/p>\n<p class=\"level2ix\">extension methods <a class=\"url1\" href=\"\/#calibre_link-854\">95<\/a><\/p>\n<p class=\"level3ix\">interfaces <a class=\"url1\" href=\"\/#calibre_link-855\">97<\/a><\/p>\n<p class=\"level2ix\">global using statements <a class=\"url1\" href=\"\/#calibre_link-856\">79<\/a><\/p>\n<p class=\"level2ix\">implicit usings <a class=\"url1\" href=\"\/#calibre_link-856\">79<\/a><\/p>\n<p class=\"level2ix\">index initializers <a class=\"url1\" href=\"\/#calibre_link-857\">92<\/a><\/p>\n<p class=\"level2ix\">interfaces<\/p>\n<p class=\"level3ix\">default implementations <a class=\"url1\" href=\"\/#calibre_link-858\">109<\/a><\/p>\n<p class=\"level3ix\">extension methods <a class=\"url1\" href=\"\/#calibre_link-855\">97<\/a><\/p>\n<p class=\"level2ix\">lambda expressions <a class=\"url1\" href=\"\/#calibre_link-859\">100<\/a><\/p>\n<p class=\"level3ix\">expression forms <a class=\"url1\" href=\"\/#calibre_link-860\">105<\/a><\/p>\n<p class=\"level3ix\">functions <a class=\"url1\" href=\"\/#calibre_link-861\">102<\/a><\/p>\n<p class=\"level3ix\">methods <a class=\"url1\" href=\"\/#calibre_link-860\">105<\/a><\/p>\n<p class=\"level3ix\">properties <a class=\"url1\" href=\"\/#calibre_link-860\">105<\/a><\/p>\n<p class=\"level2ix\">nameof expressions <a class=\"url1\" href=\"\/#calibre_link-862\">117<\/a><\/p>\n<p class=\"level2ix\">null state analysis <a class=\"url1\" href=\"\/#calibre_link-863\">82<\/a><\/p>\n<p class=\"level2ix\">object initializers <a class=\"url1\" href=\"\/#calibre_link-853\">90<\/a><\/p>\n<p class=\"level2ix\">pattern matching <a class=\"url1\" href=\"\/#calibre_link-864\">94<\/a><\/p>\n<p class=\"level3ix\">switch statements <a class=\"url1\" href=\"\/#calibre_link-864\">94<\/a><\/p>\n<p class=\"level2ix\">Program.cs file <a class=\"url1\" href=\"\/#calibre_link-856\">79<\/a><\/p>\n<p class=\"level2ix\">top-level statements <a class=\"url1\" href=\"\/#calibre_link-856\">79<\/a><\/p>\n<p class=\"level2ix\">type inference<\/p>\n<p class=\"level3ix\">var keyword <a class=\"url1\" href=\"\/#calibre_link-849\">107<\/a><\/p>\n<p class=\"level2ix\">var keyword <a class=\"url1\" href=\"\/#calibre_link-849\">107<\/a><\/p>\n<p class=\"level1ix\">Caching <a class=\"url1\" href=\"\/#calibre_link-865\">452<\/a><\/p>\n<p class=\"level1ix\">Circular references <a class=\"url1\" href=\"\/#calibre_link-866\">534<\/a><\/p>\n<p class=\"level1ix\">Client-Side packages <a class=\"url1\" href=\"\/#calibre_link-867\">411<\/a><\/p>\n<p class=\"level1ix\">Collections<\/p>\n<p class=\"level2ix\">model binding <a class=\"url1\" href=\"\/#calibre_link-868\">820<\/a><\/p>\n<p class=\"level1ix\">Commands<\/p>\n<p class=\"level2ix\">dotenet list package <a class=\"url1\" href=\"\/#calibre_link-869\">68<\/a><\/p>\n<p class=\"level2ix\">dotnet add package <a class=\"url1\" href=\"\/#calibre_link-869\">68<\/a>, <a class=\"url1\" href=\"\/#calibre_link-870\">292<\/a><\/p>\n<p class=\"level2ix\">dotnet ef <a class=\"url1\" href=\"\/#calibre_link-871\">469<\/a>, <a class=\"url1\" href=\"\/#calibre_link-872\">485<\/a>, <a class=\"url1\" href=\"\/#calibre_link-873\">963<\/a><\/p>\n<p class=\"level2ix\">dotnet ef command <a class=\"url1\" href=\"\/#calibre_link-874\">69<\/a><\/p>\n<p class=\"level2ix\">dotnet ef database <a class=\"url1\" href=\"\/#calibre_link-875\">473<\/a><\/p>\n<p class=\"level2ix\">dotnet ef migrations <a class=\"url1\" href=\"\/#calibre_link-875\">473<\/a>, <a class=\"url1\" href=\"\/#calibre_link-876\">489<\/a>, <a class=\"url1\" href=\"\/#calibre_link-848\">967<\/a><\/p>\n<p class=\"level2ix\">dotnet new <a class=\"url1\" href=\"\/#calibre_link-877\">59<\/a>, <a class=\"url1\" href=\"\/#calibre_link-733\">65<\/a><\/p>\n<p class=\"level2ix\">dotnet new globaljson <a class=\"url1\" href=\"\/#calibre_link-878\">61<\/a><\/p>\n<p class=\"level2ix\">dotnet new sln <a class=\"url1\" href=\"\/#calibre_link-878\">61<\/a><\/p>\n<p class=\"level2ix\">dotnet new web <a class=\"url1\" href=\"\/#calibre_link-878\">61<\/a><\/p>\n<p class=\"level2ix\">dotnet remove package <a class=\"url1\" href=\"\/#calibre_link-874\">69<\/a><\/p>\n<p class=\"level2ix\">dotnet run <a class=\"url1\" href=\"\/#calibre_link-879\">66<\/a><\/p>\n<p class=\"level2ix\">dotnet sln <a class=\"url1\" href=\"\/#calibre_link-878\">61<\/a><\/p>\n<p class=\"level2ix\">dotnet sql cache create <a class=\"url1\" href=\"\/#calibre_link-880\">458<\/a><\/p>\n<p class=\"level2ix\">dotnet test <a class=\"url1\" href=\"\/#calibre_link-881\">128<\/a><\/p>\n<p class=\"level2ix\">Invoke-RestMethod <a class=\"url1\" href=\"\/#calibre_link-882\">498<\/a><\/p>\n<p class=\"level2ix\">libman <a class=\"url1\" href=\"\/#calibre_link-874\">69<\/a><\/p>\n<p class=\"level2ix\">libman init <a class=\"url1\" href=\"\/#calibre_link-883\">70<\/a><\/p>\n<p class=\"level2ix\">libman install <a class=\"url1\" href=\"\/#calibre_link-883\">70<\/a><\/p>\n<p class=\"level2ix\">sqlcmd <a class=\"url1\" href=\"\/#calibre_link-880\">458<\/a><\/p>\n<p class=\"level1ix\">Configuration <a class=\"url1\" href=\"\/#calibre_link-739\">387<\/a><\/p>\n<p class=\"level2ix\">appsettings.json File <a class=\"url1\" href=\"\/#calibre_link-739\">387<\/a><\/p>\n<p class=\"level2ix\">ASPNETCORE_ENVIRONMENT <a class=\"url1\" href=\"\/#calibre_link-884\">393<\/a><\/p>\n<p class=\"level2ix\">command-line arguments <a class=\"url1\" href=\"\/#calibre_link-885\">475<\/a><\/p>\n<p class=\"level2ix\">determining the environment <a class=\"url1\" href=\"\/#calibre_link-886\">397<\/a><\/p>\n<p class=\"level2ix\">environment-specific configuration <a class=\"url1\" href=\"\/#calibre_link-738\">388<\/a>, <a class=\"url1\" href=\"\/#calibre_link-887\">392<\/a><\/p>\n<p class=\"level2ix\">IConfiguration interface <a class=\"url1\" href=\"\/#calibre_link-738\">388<\/a><\/p>\n<p class=\"level2ix\">IsDevelopment method <a class=\"url1\" href=\"\/#calibre_link-886\">397<\/a><\/p>\n<p class=\"level2ix\">IsProduction method <a class=\"url1\" href=\"\/#calibre_link-886\">397<\/a><\/p>\n<p class=\"level2ix\">IsStaging method <a class=\"url1\" href=\"\/#calibre_link-886\">397<\/a><\/p>\n<p class=\"level2ix\">launch.json File <a class=\"url1\" href=\"\/#calibre_link-884\">393<\/a><\/p>\n<p class=\"level2ix\">launchSettings.json File <a class=\"url1\" href=\"\/#calibre_link-887\">392<\/a><\/p>\n<p class=\"level2ix\">listing secrets <a class=\"url1\" href=\"\/#calibre_link-888\">399<\/a><\/p>\n<p class=\"level2ix\">User Secrets <a class=\"url1\" href=\"\/#calibre_link-889\">398<\/a><\/p>\n<p class=\"level3ix\">reading secrets <a class=\"url1\" href=\"\/#calibre_link-888\">399<\/a><\/p>\n<p class=\"level3ix\">storing secrets <a class=\"url1\" href=\"\/#calibre_link-889\">398<\/a><\/p>\n<p class=\"level2ix\">using in middleware components <a class=\"url1\" href=\"\/#calibre_link-738\">388<\/a><\/p>\n<p class=\"level1ix\">Containers, creating <a class=\"url1\" href=\"\/#calibre_link-890\">274<\/a><\/p>\n<p class=\"level1ix\">Content Delivery Networks <a class=\"url1\" href=\"\/#calibre_link-891\">741<\/a><\/p>\n<p class=\"level1ix\">Content Negotiation <a class=\"url1\" href=\"\/#calibre_link-892\">542<\/a><\/p>\n<p class=\"level1ix\">Controllers <a class=\"url1\" href=\"\/#calibre_link-882\">498<\/a><\/p>\n<p class=\"level1ix\">Cookies <a class=\"url1\" href=\"\/#calibre_link-893\">420<\/a><\/p>\n<p class=\"level2ix\">CookieOptions class <a class=\"url1\" href=\"\/#calibre_link-894\">421<\/a><\/p>\n<p class=\"level2ix\">IsEssential property <a class=\"url1\" href=\"\/#calibre_link-895\">424<\/a><\/p>\n<p class=\"level2ix\">ITrackingConsentFeature interface <a class=\"url1\" href=\"\/#calibre_link-895\">424<\/a><\/p>\n<p class=\"level2ix\">sessions <a class=\"url1\" href=\"\/#calibre_link-896\">426<\/a><\/p>\n<p class=\"level2ix\">tracking cookies <a class=\"url1\" href=\"\/#calibre_link-897\">422<\/a><\/p>\n<p class=\"level3ix\">requiring consent <a class=\"url1\" href=\"\/#calibre_link-898\">423<\/a><\/p>\n<p class=\"level2ix\">UseCookiePolicy method <a class=\"url1\" href=\"\/#calibre_link-895\">424<\/a><\/p>\n<p class=\"level1ix\">Cross-origin requests<\/p>\n<p class=\"level2ix\">enabling <a class=\"url1\" href=\"\/#calibre_link-899\">510<\/a><\/p>\n<p class=\"grouptitlesix\">D<\/p>\n<p class=\"level1ix\">Data Validation<\/p>\n<p class=\"level2ix\">attributes <a class=\"url1\" href=\"\/#calibre_link-900\">519<\/a><\/p>\n<p class=\"level3ix\">Range <a class=\"url1\" href=\"\/#calibre_link-901\">520<\/a><\/p>\n<p class=\"level3ix\">Required <a class=\"url1\" href=\"\/#calibre_link-901\">520<\/a><\/p>\n<p class=\"level2ix\">ModelState property <a class=\"url1\" href=\"\/#calibre_link-901\">520<\/a><\/p>\n<p class=\"level1ix\">Debugging <a class=\"url1\" href=\"\/#calibre_link-902\">71<\/a><\/p>\n<p class=\"level1ix\">Dependency Injection <a class=\"url1\" href=\"\/#calibre_link-903\">288<\/a>, <a class=\"url1\" href=\"\/#calibre_link-904\">357<\/a><\/p>\n<p class=\"level2ix\">ActivatorUtilities class <a class=\"url1\" href=\"\/#calibre_link-736\">362<\/a><\/p>\n<p class=\"level3ix\">CreateInstance method <a class=\"url1\" href=\"\/#calibre_link-905\">363<\/a><\/p>\n<p class=\"level3ix\">GetServiceOrCreateInstance method <a class=\"url1\" href=\"\/#calibre_link-905\">363<\/a><\/p>\n<p class=\"level2ix\">AddScoped method <a class=\"url1\" href=\"\/#calibre_link-906\">365<\/a>, <a class=\"url1\" href=\"\/#calibre_link-907\">370<\/a><\/p>\n<p class=\"level2ix\">AddSingleton method <a class=\"url1\" href=\"\/#calibre_link-908\">358<\/a>, <a class=\"url1\" href=\"\/#calibre_link-906\">365<\/a><\/p>\n<p class=\"level2ix\">AddTransient method <a class=\"url1\" href=\"\/#calibre_link-906\">365<\/a><\/p>\n<p class=\"level2ix\">controllers <a class=\"url1\" href=\"\/#calibre_link-909\">504<\/a><\/p>\n<p class=\"level2ix\">CreateScope method <a class=\"url1\" href=\"\/#calibre_link-907\">370<\/a><\/p>\n<p class=\"level2ix\">dependency chains <a class=\"url1\" href=\"\/#calibre_link-910\">374<\/a><\/p>\n<p class=\"level2ix\">Entity Framework Core scopes <a class=\"url1\" href=\"\/#calibre_link-820\">1098<\/a><\/p>\n<p class=\"level2ix\">exceptions <a class=\"url1\" href=\"\/#calibre_link-911\">371<\/a><\/p>\n<p class=\"level2ix\">factory functions <a class=\"url1\" href=\"\/#calibre_link-912\">377<\/a><\/p>\n<p class=\"level2ix\">filters <a class=\"url1\" href=\"\/#calibre_link-913\">891<\/a>, <a class=\"url1\" href=\"\/#calibre_link-914\">915<\/a><\/p>\n<p class=\"level2ix\">FromServices attribute <a class=\"url1\" href=\"\/#calibre_link-915\">505<\/a><\/p>\n<p class=\"level2ix\">GetRequiredService method <a class=\"url1\" href=\"\/#calibre_link-916\">361<\/a><\/p>\n<p class=\"level2ix\">GetService method <a class=\"url1\" href=\"\/#calibre_link-916\">361<\/a><\/p>\n<p class=\"level2ix\">HttpContext object <a class=\"url1\" href=\"\/#calibre_link-917\">369<\/a><\/p>\n<p class=\"level3ix\">RequestServices property <a class=\"url1\" href=\"\/#calibre_link-911\">371<\/a><\/p>\n<p class=\"level2ix\">IServiceProvider interface<\/p>\n<p class=\"level3ix\">extension methods <a class=\"url1\" href=\"\/#calibre_link-916\">361<\/a><\/p>\n<p class=\"level2ix\">middleware<\/p>\n<p class=\"level3ix\">constructor parameter <a class=\"url1\" href=\"\/#calibre_link-918\">359<\/a><\/p>\n<p class=\"level2ix\">middleware<\/p>\n<p class=\"level3ix\">HttpContext object <a class=\"url1\" href=\"\/#calibre_link-919\">360<\/a><\/p>\n<p class=\"level2ix\">options pattern <a class=\"url1\" href=\"\/#calibre_link-920\">306<\/a>, <a class=\"url1\" href=\"\/#calibre_link-921\">390<\/a><\/p>\n<p class=\"level2ix\">root scope exceptions <a class=\"url1\" href=\"\/#calibre_link-911\">371<\/a><\/p>\n<p class=\"level3ix\">singleton services <a class=\"url1\" href=\"\/#calibre_link-922\">364<\/a><\/p>\n<p class=\"level3ix\">transient services <a class=\"url1\" href=\"\/#calibre_link-906\">365<\/a><\/p>\n<p class=\"level2ix\">service lifecycles <a class=\"url1\" href=\"\/#calibre_link-922\">364<\/a>, <a class=\"url1\" href=\"\/#calibre_link-906\">365<\/a><\/p>\n<p class=\"level3ix\">AddTransient method <a class=\"url1\" href=\"\/#calibre_link-906\">365<\/a><\/p>\n<p class=\"level3ix\">methods <a class=\"url1\" href=\"\/#calibre_link-906\">365<\/a><\/p>\n<p class=\"level3ix\">scoped services <a class=\"url1\" href=\"\/#calibre_link-917\">369<\/a><\/p>\n<p class=\"level2ix\">service location <a class=\"url1\" href=\"\/#calibre_link-923\">352<\/a>, <a class=\"url1\" href=\"\/#calibre_link-924\">353<\/a><\/p>\n<p class=\"level2ix\">services in Program.cs file <a class=\"url1\" href=\"\/#calibre_link-925\">376<\/a><\/p>\n<p class=\"level2ix\">services with multiple implementations <a class=\"url1\" href=\"\/#calibre_link-926\">379<\/a><\/p>\n<p class=\"level2ix\">singleton pattern <a class=\"url1\" href=\"\/#calibre_link-927\">354<\/a><\/p>\n<p class=\"level2ix\">tight coupling <a class=\"url1\" href=\"\/#calibre_link-923\">352<\/a><\/p>\n<p class=\"level2ix\">type brokers <a class=\"url1\" href=\"\/#calibre_link-928\">355<\/a><\/p>\n<p class=\"level2ix\">unbound types <a class=\"url1\" href=\"\/#calibre_link-929\">381<\/a><\/p>\n<p class=\"level2ix\">whether to use <a class=\"url1\" href=\"\/#calibre_link-923\">352<\/a><\/p>\n<p class=\"level1ix\">Design patterns <a class=\"url1\" href=\"\/#calibre_link-741\">4<\/a><\/p>\n<p class=\"level1ix\">Dictionaries<\/p>\n<p class=\"level2ix\">model binding <a class=\"url1\" href=\"\/#calibre_link-868\">820<\/a><\/p>\n<p class=\"level1ix\">Docker <a class=\"url1\" href=\"\/#calibre_link-890\">274<\/a><\/p>\n<p class=\"grouptitlesix\">E<\/p>\n<p class=\"level1ix\">Endpoints<\/p>\n<p class=\"level2ix\">controllers <a class=\"url1\" href=\"\/#calibre_link-930\">23<\/a><\/p>\n<p class=\"level1ix\">Entity Framework Core <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a><\/p>\n<p class=\"level2ix\">AddDbContext&lt;T&gt; method <a class=\"url1\" href=\"\/#calibre_link-931\">471<\/a>, <a class=\"url1\" href=\"\/#calibre_link-932\">488<\/a>, <a class=\"url1\" href=\"\/#calibre_link-933\">966<\/a><\/p>\n<p class=\"level2ix\">applying programmatically <a class=\"url1\" href=\"\/#calibre_link-934\">474<\/a><\/p>\n<p class=\"level3ix\">SaveChangesAsync method <a class=\"url1\" href=\"\/#calibre_link-935\">476<\/a><\/p>\n<p class=\"level3ix\">SaveChanges method <a class=\"url1\" href=\"\/#calibre_link-934\">474<\/a><\/p>\n<p class=\"level2ix\">ASP.NET Core Identity <a class=\"url1\" href=\"\/#calibre_link-759\">1148<\/a><\/p>\n<p class=\"level2ix\">changing primary keys <a class=\"url1\" href=\"\/#calibre_link-936\">938<\/a><\/p>\n<p class=\"level2ix\">Column attribute <a class=\"url1\" href=\"\/#calibre_link-937\">487<\/a><\/p>\n<p class=\"level2ix\">connection strings <a class=\"url1\" href=\"\/#calibre_link-938\">472<\/a>, <a class=\"url1\" href=\"\/#calibre_link-876\">489<\/a>, <a class=\"url1\" href=\"\/#calibre_link-848\">967<\/a><\/p>\n<p class=\"level3ix\">multiple active result sets <a class=\"url1\" href=\"\/#calibre_link-938\">472<\/a><\/p>\n<p class=\"level2ix\">context class <a class=\"url1\" href=\"\/#calibre_link-931\">471<\/a>, <a class=\"url1\" href=\"\/#calibre_link-937\">487<\/a>, <a class=\"url1\" href=\"\/#calibre_link-939\">964<\/a><\/p>\n<p class=\"level2ix\">creating related data <a class=\"url1\" href=\"\/#calibre_link-940\">948<\/a><\/p>\n<p class=\"level2ix\">creating services <a class=\"url1\" href=\"\/#calibre_link-931\">471<\/a>, <a class=\"url1\" href=\"\/#calibre_link-932\">488<\/a>, <a class=\"url1\" href=\"\/#calibre_link-933\">966<\/a><\/p>\n<p class=\"level2ix\">data model classes <a class=\"url1\" href=\"\/#calibre_link-941\">470<\/a>, <a class=\"url1\" href=\"\/#calibre_link-872\">485<\/a><\/p>\n<p class=\"level2ix\">global tools<\/p>\n<p class=\"level3ix\">installing <a class=\"url1\" href=\"\/#calibre_link-871\">469<\/a><\/p>\n<p class=\"level2ix\">LINQ queries <a class=\"url1\" href=\"\/#calibre_link-935\">476<\/a><\/p>\n<p class=\"level3ix\">translation to SQL <a class=\"url1\" href=\"\/#calibre_link-942\">477<\/a><\/p>\n<p class=\"level2ix\">logging sensitive data <a class=\"url1\" href=\"\/#calibre_link-943\">478<\/a><\/p>\n<p class=\"level2ix\">migrations<\/p>\n<p class=\"level3ix\">applying <a class=\"url1\" href=\"\/#calibre_link-875\">473<\/a><\/p>\n<p class=\"level3ix\">creating <a class=\"url1\" href=\"\/#calibre_link-875\">473<\/a>, <a class=\"url1\" href=\"\/#calibre_link-876\">489<\/a>, <a class=\"url1\" href=\"\/#calibre_link-848\">967<\/a><\/p>\n<p class=\"level2ix\">NuGet packages, installing <a class=\"url1\" href=\"\/#calibre_link-941\">470<\/a>, <a class=\"url1\" href=\"\/#calibre_link-872\">485<\/a>, <a class=\"url1\" href=\"\/#calibre_link-873\">963<\/a><\/p>\n<p class=\"level2ix\">Related data <a class=\"url1\" href=\"\/#calibre_link-944\">533<\/a><\/p>\n<p class=\"level3ix\">circular references <a class=\"url1\" href=\"\/#calibre_link-866\">534<\/a><\/p>\n<p class=\"level3ix\">Include method <a class=\"url1\" href=\"\/#calibre_link-866\">534<\/a><\/p>\n<p class=\"level3ix\">projecting new objects <a class=\"url1\" href=\"\/#calibre_link-945\">535<\/a><\/p>\n<p class=\"level2ix\">related data properties, resetting <a class=\"url1\" href=\"\/#calibre_link-946\">934<\/a><\/p>\n<p class=\"level2ix\">seeding data <a class=\"url1\" href=\"\/#calibre_link-875\">473<\/a>, <a class=\"url1\" href=\"\/#calibre_link-937\">487<\/a>, <a class=\"url1\" href=\"\/#calibre_link-947\">965<\/a><\/p>\n<p class=\"level2ix\">SQL types <a class=\"url1\" href=\"\/#calibre_link-937\">487<\/a><\/p>\n<p class=\"level3ix\">Column attribute <a class=\"url1\" href=\"\/#calibre_link-937\">487<\/a><\/p>\n<p class=\"level2ix\">storing data<\/p>\n<p class=\"level3ix\">migrations<\/p>\n<p class=\"level1ix\">Errata, reporting <a class=\"url1\" href=\"\/#calibre_link-948\">7<\/a><\/p>\n<p class=\"level1ix\">Error boundaries <a class=\"url1\" href=\"\/#calibre_link-783\">1036<\/a><\/p>\n<p class=\"level1ix\">Exceptions<\/p>\n<p class=\"level2ix\">developer exception page <a class=\"url1\" href=\"\/#calibre_link-949\">441<\/a><\/p>\n<p class=\"level2ix\">HTML error responses <a class=\"url1\" href=\"\/#calibre_link-950\">443<\/a><\/p>\n<p class=\"level2ix\">status code responses <a class=\"url1\" href=\"\/#calibre_link-951\">444<\/a><\/p>\n<p class=\"grouptitlesix\">F<\/p>\n<p class=\"level1ix\">Files<\/p>\n<p class=\"level2ix\">sending using action results <a class=\"url1\" href=\"\/#calibre_link-735\">514<\/a><\/p>\n<p class=\"level1ix\">Filters <a class=\"url1\" href=\"\/#calibre_link-952\">880<\/a><\/p>\n<p class=\"level2ix\">action filters <a class=\"url1\" href=\"\/#calibre_link-953\">885<\/a>, <a class=\"url1\" href=\"\/#calibre_link-954\">894<\/a><\/p>\n<p class=\"level3ix\">context objects <a class=\"url1\" href=\"\/#calibre_link-955\">895<\/a><\/p>\n<p class=\"level2ix\">attributes <a class=\"url1\" href=\"\/#calibre_link-956\">886<\/a><\/p>\n<p class=\"level2ix\">authorization filters <a class=\"url1\" href=\"\/#calibre_link-953\">885<\/a>, <a class=\"url1\" href=\"\/#calibre_link-957\">887<\/a><\/p>\n<p class=\"level2ix\">dependency injection <a class=\"url1\" href=\"\/#calibre_link-913\">891<\/a>, <a class=\"url1\" href=\"\/#calibre_link-914\">915<\/a><\/p>\n<p class=\"level2ix\">exception filters <a class=\"url1\" href=\"\/#calibre_link-953\">885<\/a>, <a class=\"url1\" href=\"\/#calibre_link-958\">908<\/a><\/p>\n<p class=\"level3ix\">context objects <a class=\"url1\" href=\"\/#calibre_link-959\">909<\/a><\/p>\n<p class=\"level2ix\">execution order <a class=\"url1\" href=\"\/#calibre_link-953\">885<\/a><\/p>\n<p class=\"level2ix\">filter attributes <a class=\"url1\" href=\"\/#calibre_link-956\">886<\/a><\/p>\n<p class=\"level2ix\">FilterContext class <a class=\"url1\" href=\"\/#calibre_link-957\">887<\/a><\/p>\n<p class=\"level2ix\">filter factories <a class=\"url1\" href=\"\/#calibre_link-960\">913<\/a><\/p>\n<p class=\"level2ix\">filter interfaces <a class=\"url1\" href=\"\/#calibre_link-956\">886<\/a><\/p>\n<p class=\"level2ix\">global filters <a class=\"url1\" href=\"\/#calibre_link-961\">916<\/a><\/p>\n<p class=\"level2ix\">IActionFilter interface <a class=\"url1\" href=\"\/#calibre_link-954\">894<\/a><\/p>\n<p class=\"level2ix\">IAlwaysRunResultFilter interface <a class=\"url1\" href=\"\/#calibre_link-962\">903<\/a><\/p>\n<p class=\"level2ix\">IAsyncActionFilter interface <a class=\"url1\" href=\"\/#calibre_link-954\">894<\/a><\/p>\n<p class=\"level2ix\">IAsyncAlwaysRunResultFilter interface <a class=\"url1\" href=\"\/#calibre_link-962\">903<\/a><\/p>\n<p class=\"level2ix\">IAsyncAuthorizationFilter interface <a class=\"url1\" href=\"\/#calibre_link-957\">887<\/a><\/p>\n<p class=\"level2ix\">IAsyncExceptionFilter interface <a class=\"url1\" href=\"\/#calibre_link-958\">908<\/a><\/p>\n<p class=\"level2ix\">IAsyncPageFilter interface <a class=\"url1\" href=\"\/#calibre_link-963\">899<\/a><\/p>\n<p class=\"level2ix\">IAsyncResourceFilter interface <a class=\"url1\" href=\"\/#calibre_link-964\">890<\/a><\/p>\n<p class=\"level2ix\">IAsyncResultFilter interface <a class=\"url1\" href=\"\/#calibre_link-962\">903<\/a><\/p>\n<p class=\"level2ix\">IAuthorizationFilter interface <a class=\"url1\" href=\"\/#calibre_link-957\">887<\/a><\/p>\n<p class=\"level2ix\">IExceptionFilter interface <a class=\"url1\" href=\"\/#calibre_link-958\">908<\/a><\/p>\n<p class=\"level2ix\">IFilterFactory interface <a class=\"url1\" href=\"\/#calibre_link-960\">913<\/a><\/p>\n<p class=\"level2ix\">interfaces <a class=\"url1\" href=\"\/#calibre_link-956\">886<\/a><\/p>\n<p class=\"level2ix\">IOrderedFilter interface <a class=\"url1\" href=\"\/#calibre_link-965\">918<\/a><\/p>\n<p class=\"level2ix\">IPageFilter interface <a class=\"url1\" href=\"\/#calibre_link-963\">899<\/a><\/p>\n<p class=\"level2ix\">IResourceFilter interface <a class=\"url1\" href=\"\/#calibre_link-964\">890<\/a><\/p>\n<p class=\"level2ix\">IResultFilter interface <a class=\"url1\" href=\"\/#calibre_link-962\">903<\/a><\/p>\n<p class=\"level2ix\">lifecycle <a class=\"url1\" href=\"\/#calibre_link-966\">911<\/a><\/p>\n<p class=\"level3ix\">reusing filters <a class=\"url1\" href=\"\/#calibre_link-960\">913<\/a><\/p>\n<p class=\"level3ix\">using scopes <a class=\"url1\" href=\"\/#calibre_link-914\">915<\/a><\/p>\n<p class=\"level2ix\">ordering <a class=\"url1\" href=\"\/#calibre_link-965\">918<\/a><\/p>\n<p class=\"level2ix\">page filters <a class=\"url1\" href=\"\/#calibre_link-953\">885<\/a>, <a class=\"url1\" href=\"\/#calibre_link-963\">899<\/a><\/p>\n<p class=\"level3ix\">context objects <a class=\"url1\" href=\"\/#calibre_link-963\">899<\/a><\/p>\n<p class=\"level2ix\">RequireHttps <a class=\"url1\" href=\"\/#calibre_link-967\">882<\/a><\/p>\n<p class=\"level2ix\">resource filters <a class=\"url1\" href=\"\/#calibre_link-953\">885<\/a>, <a class=\"url1\" href=\"\/#calibre_link-964\">890<\/a><\/p>\n<p class=\"level3ix\">context classes <a class=\"url1\" href=\"\/#calibre_link-964\">890<\/a><\/p>\n<p class=\"level2ix\">result filters <a class=\"url1\" href=\"\/#calibre_link-953\">885<\/a>, <a class=\"url1\" href=\"\/#calibre_link-962\">903<\/a><\/p>\n<p class=\"level3ix\">always-run filters <a class=\"url1\" href=\"\/#calibre_link-968\">905<\/a><\/p>\n<p class=\"level3ix\">context objects <a class=\"url1\" href=\"\/#calibre_link-969\">904<\/a><\/p>\n<p class=\"level2ix\">reusing filters <a class=\"url1\" href=\"\/#calibre_link-960\">913<\/a><\/p>\n<p class=\"level2ix\">ServiceFilter attribute <a class=\"url1\" href=\"\/#calibre_link-961\">916<\/a><\/p>\n<p class=\"level2ix\">short-circuiting <a class=\"url1\" href=\"\/#calibre_link-956\">886<\/a><\/p>\n<p class=\"level2ix\">types of filter <a class=\"url1\" href=\"\/#calibre_link-953\">885<\/a><\/p>\n<p class=\"level2ix\">using <a class=\"url1\" href=\"\/#calibre_link-967\">882<\/a><\/p>\n<p class=\"level1ix\">Forms <a class=\"url1\" href=\"\/#calibre_link-970\">928<\/a><\/p>\n<p class=\"level2ix\">Blazor form features <a class=\"url1\" href=\"\/#calibre_link-809\">1085<\/a><\/p>\n<p class=\"level2ix\">button elements<\/p>\n<p class=\"level3ix\">asp-action attribute <a class=\"url1\" href=\"\/#calibre_link-971\">766<\/a><\/p>\n<p class=\"level3ix\">asp-controller attribute <a class=\"url1\" href=\"\/#calibre_link-971\">766<\/a><\/p>\n<p class=\"level3ix\">asp-page attribute <a class=\"url1\" href=\"\/#calibre_link-971\">766<\/a><\/p>\n<p class=\"level2ix\">creating data <a class=\"url1\" href=\"\/#calibre_link-972\">933<\/a>, <a class=\"url1\" href=\"\/#calibre_link-973\">945<\/a><\/p>\n<p class=\"level2ix\">creating related data <a class=\"url1\" href=\"\/#calibre_link-940\">948<\/a><\/p>\n<p class=\"level3ix\">separate request <a class=\"url1\" href=\"\/#calibre_link-974\">953<\/a><\/p>\n<p class=\"level3ix\">single request <a class=\"url1\" href=\"\/#calibre_link-940\">948<\/a><\/p>\n<p class=\"level2ix\">CSRF protection <a class=\"url1\" href=\"\/#calibre_link-975\">787<\/a><\/p>\n<p class=\"level3ix\">AutoValidateAntiForgeryToken attribute <a class=\"url1\" href=\"\/#calibre_link-976\">789<\/a><\/p>\n<p class=\"level3ix\">controllers <a class=\"url1\" href=\"\/#calibre_link-976\">789<\/a><\/p>\n<p class=\"level3ix\">IgnoreAntiForgeryToken attribute <a class=\"url1\" href=\"\/#calibre_link-977\">790<\/a><\/p>\n<p class=\"level3ix\">JavaScript clients <a class=\"url1\" href=\"\/#calibre_link-977\">790<\/a><\/p>\n<p class=\"level2ix\">configuring <a class=\"url1\" href=\"\/#calibre_link-978\">791<\/a><\/p>\n<p class=\"level3ix\">Razor Pages <a class=\"url1\" href=\"\/#calibre_link-977\">790<\/a><\/p>\n<p class=\"level3ix\">security token <a class=\"url1\" href=\"\/#calibre_link-979\">788<\/a><\/p>\n<p class=\"level3ix\">ValidateAntiForgeryToken attribute <a class=\"url1\" href=\"\/#calibre_link-977\">790<\/a><\/p>\n<p class=\"level2ix\">deleting data <a class=\"url1\" href=\"\/#calibre_link-980\">939<\/a>, <a class=\"url1\" href=\"\/#calibre_link-981\">947<\/a><\/p>\n<p class=\"level2ix\">editing data <a class=\"url1\" href=\"\/#calibre_link-982\">936<\/a>, <a class=\"url1\" href=\"\/#calibre_link-983\">946<\/a><\/p>\n<p class=\"level2ix\">form elements<\/p>\n<p class=\"level3ix\">asp-action attribute <a class=\"url1\" href=\"\/#calibre_link-984\">764<\/a><\/p>\n<p class=\"level3ix\">asp-* attributes <a class=\"url1\" href=\"\/#calibre_link-984\">764<\/a><\/p>\n<p class=\"level3ix\">asp-controller attribute <a class=\"url1\" href=\"\/#calibre_link-984\">764<\/a><\/p>\n<p class=\"level3ix\">asp-page attribute <a class=\"url1\" href=\"\/#calibre_link-985\">765<\/a><\/p>\n<p class=\"level3ix\">target <a class=\"url1\" href=\"\/#calibre_link-984\">764<\/a><\/p>\n<p class=\"level2ix\">input elements<\/p>\n<p class=\"level3ix\">asp-for attribute <a class=\"url1\" href=\"\/#calibre_link-986\">767<\/a><\/p>\n<p class=\"level3ix\">Column attribute <a class=\"url1\" href=\"\/#calibre_link-987\">772<\/a><\/p>\n<p class=\"level3ix\">DisplayFormat attribute <a class=\"url1\" href=\"\/#calibre_link-988\">773<\/a><\/p>\n<p class=\"level3ix\">formatting values <a class=\"url1\" href=\"\/#calibre_link-989\">771<\/a><\/p>\n<p class=\"level3ix\">id attribute <a class=\"url1\" href=\"\/#calibre_link-990\">768<\/a><\/p>\n<p class=\"level3ix\">name attribute <a class=\"url1\" href=\"\/#calibre_link-990\">768<\/a><\/p>\n<p class=\"level3ix\">related data <a class=\"url1\" href=\"\/#calibre_link-991\">775<\/a><\/p>\n<p class=\"level3ix\">type attribute <a class=\"url1\" href=\"\/#calibre_link-992\">769<\/a><\/p>\n<p class=\"level3ix\">value attribute <a class=\"url1\" href=\"\/#calibre_link-990\">768<\/a><\/p>\n<p class=\"level2ix\">label elements<\/p>\n<p class=\"level3ix\">asp-for attribute <a class=\"url1\" href=\"\/#calibre_link-993\">779<\/a><\/p>\n<p class=\"level3ix\">for attribute <a class=\"url1\" href=\"\/#calibre_link-993\">779<\/a><\/p>\n<p class=\"level2ix\">Post\/Redirect\/Get pattern <a class=\"url1\" href=\"\/#calibre_link-994\">759<\/a><\/p>\n<p class=\"level2ix\">#pragma warning <a class=\"url1\" href=\"\/#calibre_link-995\">777<\/a><\/p>\n<p class=\"level2ix\">primary keys, invariant <a class=\"url1\" href=\"\/#calibre_link-936\">938<\/a><\/p>\n<p class=\"level2ix\">reading data <a class=\"url1\" href=\"\/#calibre_link-996\">931<\/a>, <a class=\"url1\" href=\"\/#calibre_link-973\">945<\/a><\/p>\n<p class=\"level3ix\">resetting related data <a class=\"url1\" href=\"\/#calibre_link-946\">934<\/a><\/p>\n<p class=\"level2ix\">select elements<\/p>\n<p class=\"level3ix\">asp-for attribute <a class=\"url1\" href=\"\/#calibre_link-997\">781<\/a><\/p>\n<p class=\"level3ix\">asp-items attribute <a class=\"url1\" href=\"\/#calibre_link-997\">781<\/a>, <a class=\"url1\" href=\"\/#calibre_link-998\">783<\/a><\/p>\n<p class=\"level3ix\">SelectList class <a class=\"url1\" href=\"\/#calibre_link-998\">783<\/a><\/p>\n<p class=\"level3ix\">SelectListItem class <a class=\"url1\" href=\"\/#calibre_link-998\">783<\/a><\/p>\n<p class=\"level2ix\">tag helpers <a class=\"url1\" href=\"\/#calibre_link-994\">759<\/a><\/p>\n<p class=\"level3ix\">form elements <a class=\"url1\" href=\"\/#calibre_link-984\">764<\/a><\/p>\n<p class=\"level2ix\">textarea elements<\/p>\n<p class=\"level3ix\">asp-for attribute <a class=\"url1\" href=\"\/#calibre_link-999\">785<\/a><\/p>\n<p class=\"grouptitlesix\">G<\/p>\n<p class=\"level1ix\">General Data Protection Regulation <a class=\"url1\" href=\"\/#calibre_link-897\">422<\/a><\/p>\n<p class=\"level1ix\">Globbing selectors <a class=\"url1\" href=\"\/#calibre_link-1000\">735<\/a><\/p>\n<p class=\"level1ix\">GraphQL <a class=\"url1\" href=\"\/#calibre_link-1001\">495<\/a><\/p>\n<p class=\"level1ix\">gRPC <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1001\">495<\/a><\/p>\n<p class=\"grouptitlesix\">H<\/p>\n<p class=\"level1ix\">Host header<\/p>\n<p class=\"level2ix\">request filtering <a class=\"url1\" href=\"\/#calibre_link-1002\">446<\/a><\/p>\n<p class=\"level1ix\">HTML Forms <a class=\"url1\" href=\"\/#calibre_link-994\">759<\/a><\/p>\n<p class=\"level1ix\">HTML, Safe Encoding <a class=\"url1\" href=\"\/#calibre_link-1003\">626<\/a><\/p>\n<p class=\"level1ix\">HttpContext class<\/p>\n<p class=\"level2ix\">useful members <a class=\"url1\" href=\"\/#calibre_link-1004\">294<\/a><\/p>\n<p class=\"level1ix\">HttpRequest class<\/p>\n<p class=\"level2ix\">useful members <a class=\"url1\" href=\"\/#calibre_link-1004\">294<\/a><\/p>\n<p class=\"level1ix\">HttpResponse class<\/p>\n<p class=\"level2ix\">useful members <a class=\"url1\" href=\"\/#calibre_link-1005\">295<\/a><\/p>\n<p class=\"level1ix\">HTTPS <a class=\"url1\" href=\"\/#calibre_link-1006\">431<\/a><\/p>\n<p class=\"level2ix\">configuring <a class=\"url1\" href=\"\/#calibre_link-1006\">431<\/a><\/p>\n<p class=\"level2ix\">detecting HTTPS requests <a class=\"url1\" href=\"\/#calibre_link-1007\">433<\/a><\/p>\n<p class=\"level2ix\">development certificates <a class=\"url1\" href=\"\/#calibre_link-1008\">432<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1009\">878<\/a><\/p>\n<p class=\"level2ix\">enabling <a class=\"url1\" href=\"\/#calibre_link-1006\">431<\/a><\/p>\n<p class=\"level2ix\">HTTP Strict Transport Security <a class=\"url1\" href=\"\/#calibre_link-1010\">435<\/a><\/p>\n<p class=\"level2ix\">redirection from HTTP <a class=\"url1\" href=\"\/#calibre_link-1011\">434<\/a><\/p>\n<p class=\"level3ix\">configuration <a class=\"url1\" href=\"\/#calibre_link-1011\">434<\/a><\/p>\n<p class=\"level2ix\">SSL <a class=\"url1\" href=\"\/#calibre_link-1006\">431<\/a><\/p>\n<p class=\"level2ix\">TLS <a class=\"url1\" href=\"\/#calibre_link-1006\">431<\/a><\/p>\n<p class=\"level1ix\">HTTP status codes <a class=\"url1\" href=\"\/#calibre_link-951\">444<\/a><\/p>\n<p class=\"grouptitlesix\">J<\/p>\n<p class=\"level1ix\">jQuery<\/p>\n<p class=\"level2ix\">client-side validation packages <a class=\"url1\" href=\"\/#calibre_link-1012\">866<\/a><\/p>\n<p class=\"level2ix\">installing package <a class=\"url1\" href=\"\/#calibre_link-1013\">729<\/a><\/p>\n<p class=\"level1ix\">JSON<\/p>\n<p class=\"level2ix\">JsonIgnore attribute <a class=\"url1\" href=\"\/#calibre_link-1014\">524<\/a><\/p>\n<p class=\"level2ix\">JsonIgnoreCondition enum <a class=\"url1\" href=\"\/#calibre_link-1014\">524<\/a><\/p>\n<p class=\"level2ix\">serializer configuration <a class=\"url1\" href=\"\/#calibre_link-1014\">524<\/a><\/p>\n<p class=\"level2ix\">web services <a class=\"url1\" href=\"\/#calibre_link-1001\">495<\/a><\/p>\n<p class=\"level1ix\">JSON.NET Serializer <a class=\"url1\" href=\"\/#calibre_link-1015\">537<\/a><\/p>\n<p class=\"level1ix\">JSON Patch <a class=\"url1\" href=\"\/#calibre_link-1016\">536<\/a><\/p>\n<p class=\"grouptitlesix\">L<\/p>\n<p class=\"level1ix\">Library Manager <a class=\"url1\" href=\"\/#calibre_link-867\">411<\/a><\/p>\n<p class=\"level1ix\">Lists<\/p>\n<p class=\"level2ix\">model binding <a class=\"url1\" href=\"\/#calibre_link-868\">820<\/a><\/p>\n<p class=\"level1ix\">LocalDB <a class=\"url1\" href=\"\/#calibre_link-1017\">15<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1018\">17<\/a><\/p>\n<p class=\"level1ix\">Logging <a class=\"url1\" href=\"\/#calibre_link-1019\">401<\/a><\/p>\n<p class=\"level2ix\">ILogger&lt;T&gt; interface<\/p>\n<p class=\"level3ix\">extension methods <a class=\"url1\" href=\"\/#calibre_link-1020\">403<\/a><\/p>\n<p class=\"level2ix\">LoggerMessage attribute <a class=\"url1\" href=\"\/#calibre_link-1021\">405<\/a><\/p>\n<p class=\"level2ix\">logging providers <a class=\"url1\" href=\"\/#calibre_link-1019\">401<\/a><\/p>\n<p class=\"level3ix\">console provider <a class=\"url1\" href=\"\/#calibre_link-1019\">401<\/a><\/p>\n<p class=\"level3ix\">Debug provider <a class=\"url1\" href=\"\/#calibre_link-1019\">401<\/a><\/p>\n<p class=\"level3ix\">EventSource provider <a class=\"url1\" href=\"\/#calibre_link-1019\">401<\/a><\/p>\n<p class=\"level3ix\">list <a class=\"url1\" href=\"\/#calibre_link-1019\">401<\/a><\/p>\n<p class=\"level2ix\">log levels <a class=\"url1\" href=\"\/#calibre_link-1020\">403<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1022\">407<\/a><\/p>\n<p class=\"level2ix\">SQL queries <a class=\"url1\" href=\"\/#calibre_link-942\">477<\/a><\/p>\n<p class=\"grouptitlesix\">M<\/p>\n<p class=\"level1ix\">Middleware<\/p>\n<p class=\"level2ix\">branching <a class=\"url1\" href=\"\/#calibre_link-1023\">302<\/a><\/p>\n<p class=\"level2ix\">built-in middleware <a class=\"url1\" href=\"\/#calibre_link-903\">288<\/a><\/p>\n<p class=\"level2ix\">context objects <a class=\"url1\" href=\"\/#calibre_link-1004\">294<\/a><\/p>\n<p class=\"level2ix\">cookies <a class=\"url1\" href=\"\/#calibre_link-893\">420<\/a><\/p>\n<p class=\"level2ix\">creating <a class=\"url1\" href=\"\/#calibre_link-1024\">293<\/a><\/p>\n<p class=\"level3ix\">using classes <a class=\"url1\" href=\"\/#calibre_link-1025\">297<\/a><\/p>\n<p class=\"level2ix\">exception handling <a class=\"url1\" href=\"\/#calibre_link-949\">441<\/a><\/p>\n<p class=\"level2ix\">Host header filtering <a class=\"url1\" href=\"\/#calibre_link-1026\">447<\/a><\/p>\n<p class=\"level2ix\">HTTPS <a class=\"url1\" href=\"\/#calibre_link-1006\">431<\/a><\/p>\n<p class=\"level2ix\">Map method <a class=\"url1\" href=\"\/#calibre_link-1023\">302<\/a><\/p>\n<p class=\"level2ix\">Map When method <a class=\"url1\" href=\"\/#calibre_link-1027\">303<\/a><\/p>\n<p class=\"level2ix\">next function <a class=\"url1\" href=\"\/#calibre_link-1028\">296<\/a><\/p>\n<p class=\"level2ix\">request pipeline <a class=\"url1\" href=\"\/#calibre_link-903\">288<\/a><\/p>\n<p class=\"level2ix\">response caching <a class=\"url1\" href=\"\/#calibre_link-1029\">461<\/a><\/p>\n<p class=\"level2ix\">response compression<\/p>\n<p class=\"level3ix\">UseResponseCompression method <a class=\"url1\" href=\"\/#calibre_link-1030\">463<\/a><\/p>\n<p class=\"level2ix\">return path <a class=\"url1\" href=\"\/#calibre_link-1031\">299<\/a><\/p>\n<p class=\"level2ix\">Run middleware <a class=\"url1\" href=\"\/#calibre_link-1032\">305<\/a><\/p>\n<p class=\"level2ix\">sessions <a class=\"url1\" href=\"\/#calibre_link-896\">426<\/a><\/p>\n<p class=\"level2ix\">short-circuiting <a class=\"url1\" href=\"\/#calibre_link-1033\">300<\/a><\/p>\n<p class=\"level2ix\">static files <a class=\"url1\" href=\"\/#calibre_link-1034\">412<\/a><\/p>\n<p class=\"level2ix\">terminal middleware <a class=\"url1\" href=\"\/#calibre_link-1035\">304<\/a><\/p>\n<p class=\"level2ix\">URL routing <a class=\"url1\" href=\"\/#calibre_link-1036\">316<\/a><\/p>\n<p class=\"level2ix\">Use method <a class=\"url1\" href=\"\/#calibre_link-1004\">294<\/a><\/p>\n<p class=\"level2ix\">UseMiddleware method <a class=\"url1\" href=\"\/#calibre_link-1037\">298<\/a><\/p>\n<p class=\"level1ix\">Minimal API <a class=\"url1\" href=\"\/#calibre_link-1038\">497<\/a><\/p>\n<p class=\"level1ix\">Model Binding <a class=\"url1\" href=\"\/#calibre_link-1039\">506<\/a><\/p>\n<p class=\"level2ix\">arrays <a class=\"url1\" href=\"\/#calibre_link-740\">816<\/a><\/p>\n<p class=\"level3ix\">specifying indices <a class=\"url1\" href=\"\/#calibre_link-1040\">818<\/a><\/p>\n<p class=\"level2ix\">BindNever attribute <a class=\"url1\" href=\"\/#calibre_link-1041\">808<\/a><\/p>\n<p class=\"level2ix\">BindProperties attribute <a class=\"url1\" href=\"\/#calibre_link-1041\">808<\/a><\/p>\n<p class=\"level2ix\">BindProperty attribute <a class=\"url1\" href=\"\/#calibre_link-1042\">807<\/a><\/p>\n<p class=\"level3ix\">GET requests <a class=\"url1\" href=\"\/#calibre_link-1041\">808<\/a><\/p>\n<p class=\"level2ix\">collections <a class=\"url1\" href=\"\/#calibre_link-740\">816<\/a><\/p>\n<p class=\"level3ix\">arrays <a class=\"url1\" href=\"\/#calibre_link-740\">816<\/a><\/p>\n<p class=\"level3ix\">collections of complex types <a class=\"url1\" href=\"\/#calibre_link-1043\">822<\/a><\/p>\n<p class=\"level3ix\">dictionaries <a class=\"url1\" href=\"\/#calibre_link-868\">820<\/a><\/p>\n<p class=\"level3ix\">key\/value pairs <a class=\"url1\" href=\"\/#calibre_link-868\">820<\/a><\/p>\n<p class=\"level3ix\">lists <a class=\"url1\" href=\"\/#calibre_link-868\">820<\/a><\/p>\n<p class=\"level3ix\">sequences <a class=\"url1\" href=\"\/#calibre_link-868\">820<\/a><\/p>\n<p class=\"level3ix\">sets <a class=\"url1\" href=\"\/#calibre_link-868\">820<\/a><\/p>\n<p class=\"level3ix\">simple collections <a class=\"url1\" href=\"\/#calibre_link-868\">820<\/a><\/p>\n<p class=\"level2ix\">complex types <a class=\"url1\" href=\"\/#calibre_link-1044\">805<\/a><\/p>\n<p class=\"level3ix\">binding to a property <a class=\"url1\" href=\"\/#calibre_link-1042\">807<\/a><\/p>\n<p class=\"level3ix\">nested types <a class=\"url1\" href=\"\/#calibre_link-1045\">809<\/a><\/p>\n<p class=\"level3ix\">property binding <a class=\"url1\" href=\"\/#calibre_link-1042\">807<\/a><\/p>\n<p class=\"level3ix\">selectively binding properties <a class=\"url1\" href=\"\/#calibre_link-1046\">813<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1047\">815<\/a><\/p>\n<p class=\"level2ix\">default values <a class=\"url1\" href=\"\/#calibre_link-1048\">802<\/a><\/p>\n<p class=\"level3ix\">nullable parameters <a class=\"url1\" href=\"\/#calibre_link-1049\">804<\/a><\/p>\n<p class=\"level2ix\">FromBody attribute <a class=\"url1\" href=\"\/#calibre_link-1050\">507<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1051\">824<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1052\">829<\/a><\/p>\n<p class=\"level2ix\">FromForm attribute <a class=\"url1\" href=\"\/#calibre_link-1051\">824<\/a><\/p>\n<p class=\"level2ix\">FromHeader attribute <a class=\"url1\" href=\"\/#calibre_link-1051\">824<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1053\">827<\/a><\/p>\n<p class=\"level2ix\">FromQuery attribute <a class=\"url1\" href=\"\/#calibre_link-1051\">824<\/a><\/p>\n<p class=\"level2ix\">FromRoute attribute <a class=\"url1\" href=\"\/#calibre_link-1051\">824<\/a><\/p>\n<p class=\"level2ix\">FromService attribute <a class=\"url1\" href=\"\/#calibre_link-1054\">825<\/a><\/p>\n<p class=\"level2ix\">manual binding <a class=\"url1\" href=\"\/#calibre_link-1055\">830<\/a><\/p>\n<p class=\"level3ix\">TryUpdateModelAsync method <a class=\"url1\" href=\"\/#calibre_link-1055\">831<\/a><\/p>\n<p class=\"level2ix\">nested types <a class=\"url1\" href=\"\/#calibre_link-1045\">809<\/a><\/p>\n<p class=\"level2ix\">over binding <a class=\"url1\" href=\"\/#calibre_link-1056\">512<\/a><\/p>\n<p class=\"level2ix\">Razor Pages <a class=\"url1\" href=\"\/#calibre_link-1057\">800<\/a><\/p>\n<p class=\"level2ix\">search locations <a class=\"url1\" href=\"\/#calibre_link-1058\">798<\/a><\/p>\n<p class=\"level2ix\">selecting a source <a class=\"url1\" href=\"\/#calibre_link-1051\">824<\/a><\/p>\n<p class=\"level2ix\">simple types <a class=\"url1\" href=\"\/#calibre_link-1059\">799<\/a><\/p>\n<p class=\"level3ix\">default values <a class=\"url1\" href=\"\/#calibre_link-1048\">802<\/a><\/p>\n<p class=\"level2ix\">source, selecting <a class=\"url1\" href=\"\/#calibre_link-1051\">824<\/a><\/p>\n<p class=\"level2ix\">TryUpdateModelAsync method <a class=\"url1\" href=\"\/#calibre_link-1055\">831<\/a><\/p>\n<p class=\"level2ix\">understanding <a class=\"url1\" href=\"\/#calibre_link-1060\">797<\/a><\/p>\n<p class=\"level1ix\">Model expression<\/p>\n<p class=\"level2ix\">null conditional operator <a class=\"url1\" href=\"\/#calibre_link-995\">777<\/a><\/p>\n<p class=\"level1ix\">Model Validation<\/p>\n<p class=\"level2ix\">AddModelError method <a class=\"url1\" href=\"\/#calibre_link-1061\">838<\/a><\/p>\n<p class=\"level2ix\">client-side validation<\/p>\n<p class=\"level3ix\">extending <a class=\"url1\" href=\"\/#calibre_link-1062\">869<\/a><\/p>\n<p class=\"level3ix\">JavaScript packages <a class=\"url1\" href=\"\/#calibre_link-1012\">866<\/a><\/p>\n<p class=\"level2ix\">explicit validation <a class=\"url1\" href=\"\/#calibre_link-1061\">838<\/a><\/p>\n<p class=\"level2ix\">GetValidationState method <a class=\"url1\" href=\"\/#calibre_link-1061\">838<\/a><\/p>\n<p class=\"level2ix\">IsValid property <a class=\"url1\" href=\"\/#calibre_link-1061\">838<\/a><\/p>\n<p class=\"level2ix\">metadata validation <a class=\"url1\" href=\"\/#calibre_link-1063\">857<\/a><\/p>\n<p class=\"level2ix\">mismatched model types <a class=\"url1\" href=\"\/#calibre_link-1064\">935<\/a><\/p>\n<p class=\"level2ix\">model state dictionary <a class=\"url1\" href=\"\/#calibre_link-1061\">838<\/a><\/p>\n<p class=\"level2ix\">new view model objects <a class=\"url1\" href=\"\/#calibre_link-1064\">935<\/a><\/p>\n<p class=\"level2ix\">Razor Pages <a class=\"url1\" href=\"\/#calibre_link-1065\">854<\/a><\/p>\n<p class=\"level2ix\">remote validation <a class=\"url1\" href=\"\/#calibre_link-1062\">869<\/a><\/p>\n<p class=\"level2ix\">understanding <a class=\"url1\" href=\"\/#calibre_link-1066\">837<\/a><\/p>\n<p class=\"level2ix\">validating checkboxes <a class=\"url1\" href=\"\/#calibre_link-1067\">860<\/a><\/p>\n<p class=\"level2ix\">validation attributes <a class=\"url1\" href=\"\/#calibre_link-1063\">857<\/a><\/p>\n<p class=\"level3ix\">Compare attribute <a class=\"url1\" href=\"\/#calibre_link-1068\">858<\/a><\/p>\n<p class=\"level3ix\">custom attributes <a class=\"url1\" href=\"\/#calibre_link-1069\">861<\/a><\/p>\n<p class=\"level3ix\">Range attribute <a class=\"url1\" href=\"\/#calibre_link-1068\">858<\/a><\/p>\n<p class=\"level3ix\">RegularExpression attribute <a class=\"url1\" href=\"\/#calibre_link-1068\">858<\/a><\/p>\n<p class=\"level3ix\">Required attribute <a class=\"url1\" href=\"\/#calibre_link-1068\">858<\/a><\/p>\n<p class=\"level3ix\">StringLength attribute <a class=\"url1\" href=\"\/#calibre_link-1068\">858<\/a><\/p>\n<p class=\"level2ix\">validation messages <a class=\"url1\" href=\"\/#calibre_link-1070\">839<\/a><\/p>\n<p class=\"level3ix\">attributes <a class=\"url1\" href=\"\/#calibre_link-1070\">839<\/a><\/p>\n<p class=\"level3ix\">configuring <a class=\"url1\" href=\"\/#calibre_link-1071\">847<\/a><\/p>\n<p class=\"level3ix\">element highlighting <a class=\"url1\" href=\"\/#calibre_link-1072\">840<\/a><\/p>\n<p class=\"level3ix\">model-level messages <a class=\"url1\" href=\"\/#calibre_link-1073\">851<\/a><\/p>\n<p class=\"level3ix\">property-level messages <a class=\"url1\" href=\"\/#calibre_link-1074\">850<\/a><\/p>\n<p class=\"level3ix\">tag helper <a class=\"url1\" href=\"\/#calibre_link-1075\">841<\/a><\/p>\n<p class=\"level3ix\">ValidationSummary values <a class=\"url1\" href=\"\/#calibre_link-1076\">842<\/a><\/p>\n<p class=\"level2ix\">validation states <a class=\"url1\" href=\"\/#calibre_link-1077\">844<\/a><\/p>\n<p class=\"level2ix\">web services controllers <a class=\"url1\" href=\"\/#calibre_link-1067\">860<\/a><\/p>\n<p class=\"level1ix\">Model-View-Controller Pattern <a class=\"url1\" href=\"\/#calibre_link-1078\">499<\/a><\/p>\n<p class=\"level1ix\">MVC Framework<\/p>\n<p class=\"level2ix\">action results <a class=\"url1\" href=\"\/#calibre_link-735\">514<\/a><\/p>\n<p class=\"level2ix\">actions <a class=\"url1\" href=\"\/#calibre_link-734\">500<\/a><\/p>\n<p class=\"level2ix\">ControllerBase class <a class=\"url1\" href=\"\/#calibre_link-1079\">501<\/a><\/p>\n<p class=\"level2ix\">controllers<\/p>\n<p class=\"level3ix\">defining <a class=\"url1\" href=\"\/#calibre_link-1079\">501<\/a><\/p>\n<p class=\"level3ix\">ModelState property <a class=\"url1\" href=\"\/#calibre_link-901\">520<\/a><\/p>\n<p class=\"level2ix\">Controllers <a class=\"url1\" href=\"\/#calibre_link-882\">498<\/a><\/p>\n<p class=\"level2ix\">enabling <a class=\"url1\" href=\"\/#calibre_link-1078\">499<\/a><\/p>\n<p class=\"level2ix\">MVC pattern <a class=\"url1\" href=\"\/#calibre_link-1078\">499<\/a><\/p>\n<p class=\"level2ix\">NonController attribute <a class=\"url1\" href=\"\/#calibre_link-1079\">501<\/a><\/p>\n<p class=\"level2ix\">pattern <a class=\"url1\" href=\"\/#calibre_link-737\">3<\/a><\/p>\n<p class=\"level2ix\">Razor <a class=\"url1\" href=\"\/#calibre_link-1080\">561<\/a><\/p>\n<p class=\"level2ix\">separation of concerns <a class=\"url1\" href=\"\/#calibre_link-737\">3<\/a><\/p>\n<p class=\"level2ix\">Views <a class=\"url1\" href=\"\/#calibre_link-1080\">561<\/a><\/p>\n<p class=\"grouptitlesix\">N<\/p>\n<p class=\"level1ix\">.NET SDK<\/p>\n<p class=\"level2ix\">installing <a class=\"url1\" href=\"\/#calibre_link-1081\">16<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1018\">17<\/a><\/p>\n<p class=\"level2ix\">versions<\/p>\n<p class=\"level3ix\">Listing <a class=\"url1\" href=\"\/#calibre_link-1081\">16<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1018\">17<\/a><\/p>\n<p class=\"level1ix\">NuGet <a class=\"url1\" href=\"\/#calibre_link-870\">292<\/a><\/p>\n<p class=\"grouptitlesix\">O<\/p>\n<p class=\"level1ix\">OpenAPI <a class=\"url1\" href=\"\/#calibre_link-1082\">553<\/a><\/p>\n<p class=\"level1ix\">Options pattern <a class=\"url1\" href=\"\/#calibre_link-920\">306<\/a>, <a class=\"url1\" href=\"\/#calibre_link-921\">390<\/a><\/p>\n<p class=\"grouptitlesix\">P<\/p>\n<p class=\"level1ix\">Pages<\/p>\n<p class=\"level2ix\">model expressions <a class=\"url1\" href=\"\/#calibre_link-1083\">712<\/a><\/p>\n<p class=\"level1ix\">PATCH method <a class=\"url1\" href=\"\/#calibre_link-1016\">536<\/a><\/p>\n<p class=\"level1ix\">Platform<\/p>\n<p class=\"level2ix\">client-side packages <a class=\"url1\" href=\"\/#calibre_link-867\">411<\/a><\/p>\n<p class=\"level2ix\">configuration <a class=\"url1\" href=\"\/#calibre_link-739\">387<\/a><\/p>\n<p class=\"level2ix\">Cookies <a class=\"url1\" href=\"\/#calibre_link-893\">420<\/a><\/p>\n<p class=\"level2ix\">distributed cache <a class=\"url1\" href=\"\/#calibre_link-1084\">427<\/a><\/p>\n<p class=\"level2ix\">exceptions <a class=\"url1\" href=\"\/#calibre_link-949\">441<\/a><\/p>\n<p class=\"level2ix\">Host header <a class=\"url1\" href=\"\/#calibre_link-1002\">446<\/a><\/p>\n<p class=\"level2ix\">HTTPS\u201d <a class=\"url1\" href=\"\/#calibre_link-1006\">431<\/a><\/p>\n<p class=\"level2ix\">Logging <a class=\"url1\" href=\"\/#calibre_link-1019\">401<\/a><\/p>\n<p class=\"level2ix\">middleware <a class=\"url1\" href=\"\/#calibre_link-903\">288<\/a><\/p>\n<p class=\"level2ix\">options pattern <a class=\"url1\" href=\"\/#calibre_link-920\">306<\/a>, <a class=\"url1\" href=\"\/#calibre_link-921\">390<\/a><\/p>\n<p class=\"level2ix\">request features <a class=\"url1\" href=\"\/#calibre_link-895\">424<\/a><\/p>\n<p class=\"level2ix\">request pipeline <a class=\"url1\" href=\"\/#calibre_link-903\">288<\/a><\/p>\n<p class=\"level3ix\">branching <a class=\"url1\" href=\"\/#calibre_link-1023\">302<\/a><\/p>\n<p class=\"level3ix\">return path <a class=\"url1\" href=\"\/#calibre_link-1031\">299<\/a><\/p>\n<p class=\"level2ix\">services <a class=\"url1\" href=\"\/#calibre_link-903\">288<\/a><\/p>\n<p class=\"level2ix\">sessions <a class=\"url1\" href=\"\/#calibre_link-896\">426<\/a><\/p>\n<p class=\"level2ix\">static content <a class=\"url1\" href=\"\/#calibre_link-867\">411<\/a><\/p>\n<p class=\"level2ix\">understanding <a class=\"url1\" href=\"\/#calibre_link-903\">288<\/a><\/p>\n<p class=\"level1ix\">Project<\/p>\n<p class=\"level2ix\">appsettings.json File <a class=\"url1\" href=\"\/#calibre_link-739\">387<\/a><\/p>\n<p class=\"level2ix\">launch.json File <a class=\"url1\" href=\"\/#calibre_link-884\">393<\/a><\/p>\n<p class=\"level2ix\">launchSettings.json File <a class=\"url1\" href=\"\/#calibre_link-887\">392<\/a><\/p>\n<p class=\"level2ix\">request pipeline<\/p>\n<p class=\"level3ix\">short-circuiting <a class=\"url1\" href=\"\/#calibre_link-1033\">300<\/a><\/p>\n<p class=\"level1ix\">Projects<\/p>\n<p class=\"level2ix\">adding items <a class=\"url1\" href=\"\/#calibre_link-1085\">63<\/a><\/p>\n<p class=\"level2ix\">adding packages <a class=\"url1\" href=\"\/#calibre_link-870\">292<\/a><\/p>\n<p class=\"level2ix\">appsettings files <a class=\"url1\" href=\"\/#calibre_link-1086\">290<\/a><\/p>\n<p class=\"level2ix\">building projects <a class=\"url1\" href=\"\/#calibre_link-1087\">64<\/a><\/p>\n<p class=\"level2ix\">client-side packages <a class=\"url1\" href=\"\/#calibre_link-874\">69<\/a><\/p>\n<p class=\"level2ix\">compiling projects <a class=\"url1\" href=\"\/#calibre_link-1087\">64<\/a><\/p>\n<p class=\"level2ix\">creating<\/p>\n<p class=\"level3ix\">dotnet new command <a class=\"url1\" href=\"\/#calibre_link-1088\">20<\/a><\/p>\n<p class=\"level2ix\">creating projects <a class=\"url1\" href=\"\/#calibre_link-878\">61<\/a><\/p>\n<p class=\"level2ix\">csproj project file <a class=\"url1\" href=\"\/#calibre_link-1086\">290<\/a>, <a class=\"url1\" href=\"\/#calibre_link-870\">292<\/a><\/p>\n<p class=\"level2ix\">debugging <a class=\"url1\" href=\"\/#calibre_link-902\">71<\/a><\/p>\n<p class=\"level2ix\">Empty template <a class=\"url1\" href=\"\/#calibre_link-903\">288<\/a><\/p>\n<p class=\"level2ix\">global.json file <a class=\"url1\" href=\"\/#calibre_link-1086\">290<\/a><\/p>\n<p class=\"level2ix\">launchSettings.json file <a class=\"url1\" href=\"\/#calibre_link-1086\">290<\/a><\/p>\n<p class=\"level2ix\">LibMan <a class=\"url1\" href=\"\/#calibre_link-874\">69<\/a><\/p>\n<p class=\"level2ix\">managing packages<\/p>\n<p class=\"level3ix\">client-side packages <a class=\"url1\" href=\"\/#calibre_link-874\">69<\/a><\/p>\n<p class=\"level3ix\">tool packages <a class=\"url1\" href=\"\/#calibre_link-874\">69<\/a><\/p>\n<p class=\"level2ix\">managing packages <a class=\"url1\" href=\"\/#calibre_link-869\">68<\/a><\/p>\n<p class=\"level2ix\">opening <a class=\"url1\" href=\"\/#calibre_link-1088\">20<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1089\">21<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1090\">62<\/a><\/p>\n<p class=\"level2ix\">scaffolding <a class=\"url1\" href=\"\/#calibre_link-1087\">64<\/a><\/p>\n<p class=\"level2ix\">templates <a class=\"url1\" href=\"\/#calibre_link-1091\">60<\/a><\/p>\n<p class=\"level3ix\">limitations <a class=\"url1\" href=\"\/#calibre_link-1091\">60<\/a><\/p>\n<p class=\"level2ix\">tool packages <a class=\"url1\" href=\"\/#calibre_link-874\">69<\/a><\/p>\n<p class=\"grouptitlesix\">R<\/p>\n<p class=\"level1ix\">Razor <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1092\">25<\/a><\/p>\n<p class=\"level1ix\">Razor Component <a class=\"url1\" href=\"\/#calibre_link-799\">981<\/a><\/p>\n<p class=\"level1ix\">Razor Pages <a class=\"url1\" href=\"\/#calibre_link-741\">4<\/a><\/p>\n<p class=\"level2ix\">action results<\/p>\n<p class=\"level3ix\">Page method, implied <a class=\"url1\" href=\"\/#calibre_link-1093\">647<\/a><\/p>\n<p class=\"level3ix\">redirections <a class=\"url1\" href=\"\/#calibre_link-1094\">650<\/a><\/p>\n<p class=\"level2ix\">actions results <a class=\"url1\" href=\"\/#calibre_link-1093\">647<\/a><\/p>\n<p class=\"level2ix\">code-behind classes <a class=\"url1\" href=\"\/#calibre_link-1095\">644<\/a><\/p>\n<p class=\"level2ix\">common base classes <a class=\"url1\" href=\"\/#calibre_link-1096\">944<\/a><\/p>\n<p class=\"level2ix\">configuration <a class=\"url1\" href=\"\/#calibre_link-1097\">633<\/a><\/p>\n<p class=\"level2ix\">creating <a class=\"url1\" href=\"\/#calibre_link-1098\">634<\/a><\/p>\n<p class=\"level2ix\">dependency injection <a class=\"url1\" href=\"\/#calibre_link-1099\">660<\/a><\/p>\n<p class=\"level2ix\">generated class <a class=\"url1\" href=\"\/#calibre_link-1100\">637<\/a><\/p>\n<p class=\"level2ix\">HTTP methods <a class=\"url1\" href=\"\/#calibre_link-1101\">651<\/a><\/p>\n<p class=\"level2ix\">@inject directive <a class=\"url1\" href=\"\/#calibre_link-1099\">660<\/a><\/p>\n<p class=\"level2ix\">layouts <a class=\"url1\" href=\"\/#calibre_link-1102\">656<\/a><\/p>\n<p class=\"level2ix\">model Validation <a class=\"url1\" href=\"\/#calibre_link-1065\">854<\/a><\/p>\n<p class=\"level2ix\">multiple handler methods <a class=\"url1\" href=\"\/#calibre_link-1103\">942<\/a><\/p>\n<p class=\"level2ix\">@page directive <a class=\"url1\" href=\"\/#calibre_link-1104\">641<\/a><\/p>\n<p class=\"level2ix\">page model <a class=\"url1\" href=\"\/#calibre_link-1105\">636<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1095\">644<\/a><\/p>\n<p class=\"level3ix\">base class <a class=\"url1\" href=\"\/#calibre_link-1095\">644<\/a><\/p>\n<p class=\"level3ix\">code-behind class <a class=\"url1\" href=\"\/#calibre_link-1095\">644<\/a><\/p>\n<p class=\"level3ix\">generating URLs <a class=\"url1\" href=\"\/#calibre_link-1106\">734<\/a><\/p>\n<p class=\"level3ix\">handler parameter\/variable <a class=\"url1\" href=\"\/#calibre_link-1107\">653<\/a><\/p>\n<p class=\"level3ix\">multiple handler methods <a class=\"url1\" href=\"\/#calibre_link-1107\">653<\/a><\/p>\n<p class=\"level3ix\">multiple HTTP methods <a class=\"url1\" href=\"\/#calibre_link-1101\">651<\/a><\/p>\n<p class=\"level3ix\">properties <a class=\"url1\" href=\"\/#calibre_link-1095\">644<\/a><\/p>\n<p class=\"level3ix\">Url property <a class=\"url1\" href=\"\/#calibre_link-1106\">734<\/a><\/p>\n<p class=\"level2ix\">page view <a class=\"url1\" href=\"\/#calibre_link-1100\">637<\/a><\/p>\n<p class=\"level2ix\">partial views <a class=\"url1\" href=\"\/#calibre_link-1108\">658<\/a><\/p>\n<p class=\"level3ix\">search path <a class=\"url1\" href=\"\/#calibre_link-1108\">658<\/a><\/p>\n<p class=\"level3ix\">sharing with MVC Framework <a class=\"url1\" href=\"\/#calibre_link-1108\">658<\/a><\/p>\n<p class=\"level2ix\">registering tag helpers <a class=\"url1\" href=\"\/#calibre_link-1109\">698<\/a><\/p>\n<p class=\"level2ix\">routing <a class=\"url1\" href=\"\/#calibre_link-1110\">639<\/a><\/p>\n<p class=\"level3ix\">conventions <a class=\"url1\" href=\"\/#calibre_link-1111\">642<\/a><\/p>\n<p class=\"level3ix\">default URL <a class=\"url1\" href=\"\/#calibre_link-1112\">640<\/a><\/p>\n<p class=\"level3ix\">defining a route <a class=\"url1\" href=\"\/#calibre_link-1104\">641<\/a><\/p>\n<p class=\"level2ix\">routing convention <a class=\"url1\" href=\"\/#calibre_link-1105\">636<\/a><\/p>\n<p class=\"level2ix\">runtime compilation <a class=\"url1\" href=\"\/#calibre_link-1097\">633<\/a><\/p>\n<p class=\"level2ix\">view components, using <a class=\"url1\" href=\"\/#calibre_link-1113\">670<\/a><\/p>\n<p class=\"level1ix\">React <a class=\"url1\" href=\"\/#calibre_link-737\">3<\/a><\/p>\n<p class=\"level1ix\">Redis <a class=\"url1\" href=\"\/#calibre_link-1114\">456<\/a><\/p>\n<p class=\"level1ix\">Repository pattern<\/p>\n<p class=\"level2ix\">using <a class=\"url1\" href=\"\/#calibre_link-1115\">148<\/a><\/p>\n<p class=\"level1ix\">Response compressions <a class=\"url1\" href=\"\/#calibre_link-1030\">463<\/a><\/p>\n<p class=\"level1ix\">RESTFul web services <a class=\"url1\" href=\"\/#calibre_link-1116\">494<\/a><\/p>\n<p class=\"level1ix\">Routing <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1092\">25<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1117\">315<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1036\">316<\/a><\/p>\n<p class=\"level2ix\">ambiguous routes<\/p>\n<p class=\"level3ix\">avoiding <a class=\"url1\" href=\"\/#calibre_link-1118\">342<\/a><\/p>\n<p class=\"level3ix\">ordering <a class=\"url1\" href=\"\/#calibre_link-1119\">343<\/a><\/p>\n<p class=\"level2ix\">applying middleware <a class=\"url1\" href=\"\/#calibre_link-1036\">316<\/a><\/p>\n<p class=\"level2ix\">areas <a class=\"url1\" href=\"\/#calibre_link-1120\">329<\/a><\/p>\n<p class=\"level2ix\">convention routing <a class=\"url1\" href=\"\/#calibre_link-1121\">565<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1122\">568<\/a><\/p>\n<p class=\"level2ix\">defining endpoints <a class=\"url1\" href=\"\/#calibre_link-1123\">317<\/a><\/p>\n<p class=\"level3ix\">Map methods <a class=\"url1\" href=\"\/#calibre_link-1123\">317<\/a><\/p>\n<p class=\"level2ix\">endpoints<\/p>\n<p class=\"level3ix\">RequestDelegate <a class=\"url1\" href=\"\/#calibre_link-1123\">317<\/a><\/p>\n<p class=\"level2ix\">endpoint selection <a class=\"url1\" href=\"\/#calibre_link-1124\">345<\/a><\/p>\n<p class=\"level3ix\">GetEndpoint method <a class=\"url1\" href=\"\/#calibre_link-1125\">346<\/a><\/p>\n<p class=\"level2ix\">fallback routes <a class=\"url1\" href=\"\/#calibre_link-1126\">340<\/a><\/p>\n<p class=\"level2ix\">generating URLs <a class=\"url1\" href=\"\/#calibre_link-1127\">326<\/a><\/p>\n<p class=\"level3ix\">LinkGenerator class <a class=\"url1\" href=\"\/#calibre_link-1128\">328<\/a><\/p>\n<p class=\"level2ix\">HttpContext.RouteValues property <a class=\"url1\" href=\"\/#calibre_link-1129\">322<\/a><\/p>\n<p class=\"level2ix\">IEndpointRouteBuilder methods <a class=\"url1\" href=\"\/#calibre_link-1123\">317<\/a><\/p>\n<p class=\"level2ix\">MapControllerRoute method <a class=\"url1\" href=\"\/#calibre_link-1121\">565<\/a><\/p>\n<p class=\"level2ix\">MapDefaultControllerRoute method <a class=\"url1\" href=\"\/#calibre_link-1130\">567<\/a><\/p>\n<p class=\"level2ix\">Razor Pages <a class=\"url1\" href=\"\/#calibre_link-1105\">636<\/a><\/p>\n<p class=\"level2ix\">route selection <a class=\"url1\" href=\"\/#calibre_link-1131\">324<\/a><\/p>\n<p class=\"level2ix\">URL patterns <a class=\"url1\" href=\"\/#calibre_link-1132\">321<\/a><\/p>\n<p class=\"level3ix\">catch-all segments <a class=\"url1\" href=\"\/#calibre_link-1133\">334<\/a><\/p>\n<p class=\"level3ix\">complex patterns <a class=\"url1\" href=\"\/#calibre_link-1134\">330<\/a><\/p>\n<p class=\"level3ix\">constraints <a class=\"url1\" href=\"\/#calibre_link-1135\">336<\/a><\/p>\n<p class=\"level3ix\">combining <a class=\"url1\" href=\"\/#calibre_link-1136\">338<\/a><\/p>\n<p class=\"level3ix\">custom <a class=\"url1\" href=\"\/#calibre_link-1137\">341<\/a><\/p>\n<p class=\"level3ix\">default values <a class=\"url1\" href=\"\/#calibre_link-1138\">331<\/a><\/p>\n<p class=\"level3ix\">optional segments <a class=\"url1\" href=\"\/#calibre_link-1139\">333<\/a><\/p>\n<p class=\"level3ix\">regular expressions <a class=\"url1\" href=\"\/#calibre_link-1140\">339<\/a><\/p>\n<p class=\"level3ix\">RouteValuesDictionary class <a class=\"url1\" href=\"\/#calibre_link-1129\">322<\/a><\/p>\n<p class=\"level3ix\">segments <a class=\"url1\" href=\"\/#calibre_link-1132\">321<\/a><\/p>\n<p class=\"level3ix\">segment variables <a class=\"url1\" href=\"\/#calibre_link-1129\">322<\/a><\/p>\n<p class=\"level2ix\">WithMetadata method <a class=\"url1\" href=\"\/#calibre_link-1141\">327<\/a><\/p>\n<p class=\"grouptitlesix\">S<\/p>\n<p class=\"level1ix\">Scaffolding <a class=\"url1\" href=\"\/#calibre_link-1087\">64<\/a><\/p>\n<p class=\"level1ix\">Services<\/p>\n<p class=\"level2ix\">AddDistributedSqlServerCache<\/p>\n<p class=\"level3ix\">AddDistributedMemoryCache method <a class=\"url1\" href=\"\/#calibre_link-1114\">456<\/a><\/p>\n<p class=\"level2ix\">application lifetime service <a class=\"url1\" href=\"\/#calibre_link-885\">475<\/a><\/p>\n<p class=\"level2ix\">caching <a class=\"url1\" href=\"\/#calibre_link-865\">452<\/a><\/p>\n<p class=\"level3ix\">AddDistributedMemoryCache method <a class=\"url1\" href=\"\/#calibre_link-1114\">456<\/a><\/p>\n<p class=\"level3ix\">AddStackExchangeRedisCahce method <a class=\"url1\" href=\"\/#calibre_link-1114\">456<\/a><\/p>\n<p class=\"level3ix\">database preparation <a class=\"url1\" href=\"\/#calibre_link-880\">458<\/a><\/p>\n<p class=\"level3ix\">data cache <a class=\"url1\" href=\"\/#calibre_link-1142\">454<\/a><\/p>\n<p class=\"level3ix\">DistributedCacheEntryOptions class <a class=\"url1\" href=\"\/#calibre_link-1143\">455<\/a><\/p>\n<p class=\"level3ix\">distributed caching <a class=\"url1\" href=\"\/#calibre_link-880\">458<\/a><\/p>\n<p class=\"level3ix\">IDistributedCache implementations <a class=\"url1\" href=\"\/#calibre_link-1114\">456<\/a><\/p>\n<p class=\"level3ix\">IDistributedCache interface <a class=\"url1\" href=\"\/#calibre_link-1143\">455<\/a><\/p>\n<p class=\"level3ix\">memory cache options <a class=\"url1\" href=\"\/#calibre_link-1144\">457<\/a><\/p>\n<p class=\"level3ix\">NuGet package <a class=\"url1\" href=\"\/#calibre_link-1145\">459<\/a><\/p>\n<p class=\"level3ix\">persistent caching <a class=\"url1\" href=\"\/#calibre_link-880\">458<\/a><\/p>\n<p class=\"level3ix\">SQL Server cache options <a class=\"url1\" href=\"\/#calibre_link-1146\">460<\/a><\/p>\n<p class=\"level2ix\">response caching <a class=\"url1\" href=\"\/#calibre_link-1029\">461<\/a><\/p>\n<p class=\"level3ix\">AddResponseCaching method <a class=\"url1\" href=\"\/#calibre_link-1147\">462<\/a><\/p>\n<p class=\"level3ix\">Cache-Control header <a class=\"url1\" href=\"\/#calibre_link-1030\">463<\/a><\/p>\n<p class=\"level3ix\">UseResponseCaching method <a class=\"url1\" href=\"\/#calibre_link-1147\">462<\/a><\/p>\n<p class=\"level3ix\">Vary header <a class=\"url1\" href=\"\/#calibre_link-1030\">463<\/a><\/p>\n<p class=\"level1ix\">Sessions <a class=\"url1\" href=\"\/#calibre_link-896\">426<\/a><\/p>\n<p class=\"level2ix\">configuration <a class=\"url1\" href=\"\/#calibre_link-1084\">427<\/a><\/p>\n<p class=\"level2ix\">data cache <a class=\"url1\" href=\"\/#calibre_link-1084\">427<\/a><\/p>\n<p class=\"level3ix\">AddDistributedMemoryCache method <a class=\"url1\" href=\"\/#calibre_link-1084\">427<\/a><\/p>\n<p class=\"level2ix\">ISession interface <a class=\"url1\" href=\"\/#calibre_link-1148\">429<\/a><\/p>\n<p class=\"level2ix\">session data <a class=\"url1\" href=\"\/#calibre_link-1148\">429<\/a><\/p>\n<p class=\"level3ix\">committing changes <a class=\"url1\" href=\"\/#calibre_link-1149\">430<\/a><\/p>\n<p class=\"level1ix\">SignalR <a class=\"url1\" href=\"\/#calibre_link-743\">5<\/a><\/p>\n<p class=\"level1ix\">Single Page Applications<\/p>\n<p class=\"level2ix\">Angular <a class=\"url1\" href=\"\/#calibre_link-737\">3<\/a><\/p>\n<p class=\"level2ix\">React <a class=\"url1\" href=\"\/#calibre_link-737\">3<\/a><\/p>\n<p class=\"level1ix\">Singleton pattern <a class=\"url1\" href=\"\/#calibre_link-927\">354<\/a><\/p>\n<p class=\"level1ix\">Solutions <a class=\"url1\" href=\"\/#calibre_link-878\">61<\/a><\/p>\n<p class=\"level1ix\">Source maps <a class=\"url1\" href=\"\/#calibre_link-1150\">737<\/a><\/p>\n<p class=\"level1ix\">SportsStore <a class=\"url1\" href=\"\/#calibre_link-1151\">139<\/a><\/p>\n<p class=\"level2ix\">administration <a class=\"url1\" href=\"\/#calibre_link-1152\">238<\/a><\/p>\n<p class=\"level2ix\">Blazor Server <a class=\"url1\" href=\"\/#calibre_link-1153\">240<\/a><\/p>\n<p class=\"level2ix\">cart <a class=\"url1\" href=\"\/#calibre_link-1154\">194<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1155\">212<\/a><\/p>\n<p class=\"level2ix\">checkout <a class=\"url1\" href=\"\/#calibre_link-1156\">224<\/a><\/p>\n<p class=\"level2ix\">client-side packages <a class=\"url1\" href=\"\/#calibre_link-1157\">169<\/a><\/p>\n<p class=\"level2ix\">connection strings <a class=\"url1\" href=\"\/#calibre_link-1158\">146<\/a><\/p>\n<p class=\"level2ix\">creating projects <a class=\"url1\" href=\"\/#calibre_link-1151\">139<\/a><\/p>\n<p class=\"level2ix\">data model <a class=\"url1\" href=\"\/#calibre_link-1159\">144<\/a><\/p>\n<p class=\"level2ix\">deployment <a class=\"url1\" href=\"\/#calibre_link-890\">274<\/a><\/p>\n<p class=\"level2ix\">Entity Framework Core <a class=\"url1\" href=\"\/#calibre_link-1158\">146<\/a><\/p>\n<p class=\"level2ix\">filtering categories <a class=\"url1\" href=\"\/#calibre_link-1160\">176<\/a><\/p>\n<p class=\"level2ix\">migrations <a class=\"url1\" href=\"\/#calibre_link-1161\">151<\/a><\/p>\n<p class=\"level2ix\">navigation menu <a class=\"url1\" href=\"\/#calibre_link-1162\">184<\/a><\/p>\n<p class=\"level2ix\">page counts, fixing <a class=\"url1\" href=\"\/#calibre_link-1163\">191<\/a><\/p>\n<p class=\"level2ix\">pagination <a class=\"url1\" href=\"\/#calibre_link-1164\">157<\/a><\/p>\n<p class=\"level2ix\">repository pattern <a class=\"url1\" href=\"\/#calibre_link-1115\">148<\/a><\/p>\n<p class=\"level2ix\">security <a class=\"url1\" href=\"\/#calibre_link-1165\">261<\/a><\/p>\n<p class=\"level2ix\">sessions <a class=\"url1\" href=\"\/#calibre_link-1166\">199<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1155\">212<\/a><\/p>\n<p class=\"level2ix\">tag helpers <a class=\"url1\" href=\"\/#calibre_link-1167\">160<\/a><\/p>\n<p class=\"level2ix\">Unit Testing <a class=\"url1\" href=\"\/#calibre_link-1151\">139<\/a><\/p>\n<p class=\"level2ix\">validation <a class=\"url1\" href=\"\/#calibre_link-1168\">234<\/a><\/p>\n<p class=\"level1ix\">SQL Server<\/p>\n<p class=\"level2ix\">LocalDB <a class=\"url1\" href=\"\/#calibre_link-1017\">15<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1018\">17<\/a><\/p>\n<p class=\"level1ix\">Startup Class<\/p>\n<p class=\"level2ix\">enabling MVC <a class=\"url1\" href=\"\/#calibre_link-1078\">499<\/a><\/p>\n<p class=\"level1ix\">Static Content <a class=\"url1\" href=\"\/#calibre_link-867\">411<\/a><\/p>\n<p class=\"level2ix\">Library Manager <a class=\"url1\" href=\"\/#calibre_link-1169\">415<\/a><\/p>\n<p class=\"level3ix\">initializing project <a class=\"url1\" href=\"\/#calibre_link-1169\">415<\/a><\/p>\n<p class=\"level3ix\">installing <a class=\"url1\" href=\"\/#calibre_link-1169\">415<\/a><\/p>\n<p class=\"level3ix\">installing packages <a class=\"url1\" href=\"\/#calibre_link-848\">967<\/a><\/p>\n<p class=\"level2ix\">middleware <a class=\"url1\" href=\"\/#calibre_link-1034\">412<\/a><\/p>\n<p class=\"level3ix\">configuration options <a class=\"url1\" href=\"\/#calibre_link-1170\">413<\/a><\/p>\n<p class=\"level2ix\">wwwroot folder <a class=\"url1\" href=\"\/#calibre_link-867\">411<\/a><\/p>\n<p class=\"level1ix\">Status code responses <a class=\"url1\" href=\"\/#calibre_link-951\">444<\/a><\/p>\n<p class=\"grouptitlesix\">T<\/p>\n<p class=\"level1ix\">Tag Helper Components <a class=\"url1\" href=\"\/#calibre_link-1171\">721<\/a><\/p>\n<p class=\"level1ix\">Tag Helpers<\/p>\n<p class=\"level2ix\">anchor elements <a class=\"url1\" href=\"\/#calibre_link-1172\">731<\/a><\/p>\n<p class=\"level3ix\">Razor Pages <a class=\"url1\" href=\"\/#calibre_link-1173\">733<\/a><\/p>\n<p class=\"level2ix\">attributes<\/p>\n<p class=\"level3ix\">HtmlAttributeName attribute <a class=\"url1\" href=\"\/#calibre_link-1174\">697<\/a><\/p>\n<p class=\"level3ix\">naming convention <a class=\"url1\" href=\"\/#calibre_link-1175\">696<\/a><\/p>\n<p class=\"level2ix\">Blazor <a class=\"url1\" href=\"\/#calibre_link-788\">983<\/a><\/p>\n<p class=\"level2ix\">built-in helpers<\/p>\n<p class=\"level3ix\">enabling <a class=\"url1\" href=\"\/#calibre_link-1172\">731<\/a><\/p>\n<p class=\"level2ix\">Cache busting <a class=\"url1\" href=\"\/#calibre_link-1176\">740<\/a><\/p>\n<p class=\"level2ix\">caching content <a class=\"url1\" href=\"\/#calibre_link-1177\">748<\/a><\/p>\n<p class=\"level3ix\">cache expiry <a class=\"url1\" href=\"\/#calibre_link-1178\">750<\/a><\/p>\n<p class=\"level3ix\">distributed caching <a class=\"url1\" href=\"\/#calibre_link-1178\">750<\/a><\/p>\n<p class=\"level3ix\">variations <a class=\"url1\" href=\"\/#calibre_link-1179\">752<\/a><\/p>\n<p class=\"level2ix\">context data <a class=\"url1\" href=\"\/#calibre_link-1175\">696<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1180\">710<\/a><\/p>\n<p class=\"level3ix\">TagHelperContext class <a class=\"url1\" href=\"\/#calibre_link-1175\">696<\/a><\/p>\n<p class=\"level3ix\">ViewContext attribute <a class=\"url1\" href=\"\/#calibre_link-1181\">711<\/a><\/p>\n<p class=\"level2ix\">coordination <a class=\"url1\" href=\"\/#calibre_link-1182\">718<\/a><\/p>\n<p class=\"level3ix\">Items collection <a class=\"url1\" href=\"\/#calibre_link-1182\">718<\/a><\/p>\n<p class=\"level2ix\">creating <a class=\"url1\" href=\"\/#calibre_link-1183\">695<\/a><\/p>\n<p class=\"level2ix\">CSS stylesheets <a class=\"url1\" href=\"\/#calibre_link-1184\">743<\/a><\/p>\n<p class=\"level2ix\">enabling <a class=\"url1\" href=\"\/#calibre_link-1172\">731<\/a><\/p>\n<p class=\"level2ix\">environment element <a class=\"url1\" href=\"\/#calibre_link-1185\">753<\/a><\/p>\n<p class=\"level2ix\">forms <a class=\"url1\" href=\"\/#calibre_link-994\">759<\/a><\/p>\n<p class=\"level2ix\">globbing <a class=\"url1\" href=\"\/#calibre_link-1000\">735<\/a><\/p>\n<p class=\"level2ix\">HtmlAttributeNotBound attribute <a class=\"url1\" href=\"\/#calibre_link-1181\">711<\/a><\/p>\n<p class=\"level2ix\">HtmlTargetElement attribute <a class=\"url1\" href=\"\/#calibre_link-1186\">700<\/a><\/p>\n<p class=\"level3ix\">properties <a class=\"url1\" href=\"\/#calibre_link-1186\">700<\/a><\/p>\n<p class=\"level2ix\">images <a class=\"url1\" href=\"\/#calibre_link-1187\">746<\/a><\/p>\n<p class=\"level2ix\">img elements <a class=\"url1\" href=\"\/#calibre_link-1187\">746<\/a><\/p>\n<p class=\"level2ix\">JavaScript files <a class=\"url1\" href=\"\/#calibre_link-1000\">735<\/a><\/p>\n<p class=\"level2ix\">link elements <a class=\"url1\" href=\"\/#calibre_link-1184\">743<\/a><\/p>\n<p class=\"level3ix\">content delivery networks <a class=\"url1\" href=\"\/#calibre_link-1188\">745<\/a><\/p>\n<p class=\"level2ix\">model expressions <a class=\"url1\" href=\"\/#calibre_link-1083\">712<\/a><\/p>\n<p class=\"level2ix\">output <a class=\"url1\" href=\"\/#calibre_link-1174\">697<\/a><\/p>\n<p class=\"level2ix\">ProcessAsync method <a class=\"url1\" href=\"\/#calibre_link-1175\">696<\/a><\/p>\n<p class=\"level2ix\">Process method <a class=\"url1\" href=\"\/#calibre_link-1183\">695<\/a><\/p>\n<p class=\"level2ix\">property naming convention <a class=\"url1\" href=\"\/#calibre_link-1175\">696<\/a><\/p>\n<p class=\"level2ix\">registering <a class=\"url1\" href=\"\/#calibre_link-1109\">698<\/a><\/p>\n<p class=\"level2ix\">scope<\/p>\n<p class=\"level3ix\">increasing <a class=\"url1\" href=\"\/#calibre_link-1189\">701<\/a><\/p>\n<p class=\"level3ix\">restricting <a class=\"url1\" href=\"\/#calibre_link-1186\">700<\/a><\/p>\n<p class=\"level2ix\">script elements <a class=\"url1\" href=\"\/#calibre_link-1000\">735<\/a><\/p>\n<p class=\"level3ix\">cache busting <a class=\"url1\" href=\"\/#calibre_link-1176\">740<\/a><\/p>\n<p class=\"level3ix\">content delivery networks <a class=\"url1\" href=\"\/#calibre_link-891\">741<\/a><\/p>\n<p class=\"level3ix\">selecting <a class=\"url1\" href=\"\/#calibre_link-1000\">735<\/a><\/p>\n<p class=\"level2ix\">short-hand elements <a class=\"url1\" href=\"\/#calibre_link-1190\">703<\/a><\/p>\n<p class=\"level2ix\">suppressing output <a class=\"url1\" href=\"\/#calibre_link-1191\">720<\/a><\/p>\n<p class=\"level2ix\">TagBuilder class <a class=\"url1\" href=\"\/#calibre_link-1192\">706<\/a><\/p>\n<p class=\"level2ix\">TagHelper base class <a class=\"url1\" href=\"\/#calibre_link-1183\">695<\/a><\/p>\n<p class=\"level2ix\">tag helper components <a class=\"url1\" href=\"\/#calibre_link-1171\">721<\/a><\/p>\n<p class=\"level3ix\">creating <a class=\"url1\" href=\"\/#calibre_link-1171\">721<\/a><\/p>\n<p class=\"level3ix\">expanding selection <a class=\"url1\" href=\"\/#calibre_link-1193\">723<\/a><\/p>\n<p class=\"level3ix\">TagHelperComponent class <a class=\"url1\" href=\"\/#calibre_link-1171\">721<\/a><\/p>\n<p class=\"level3ix\">TagHelperComponentTagHelper class <a class=\"url1\" href=\"\/#calibre_link-1193\">723<\/a><\/p>\n<p class=\"level2ix\">TagHelperContext class<\/p>\n<p class=\"level3ix\">properties <a class=\"url1\" href=\"\/#calibre_link-1175\">696<\/a><\/p>\n<p class=\"level2ix\">TagHelperOutput class<\/p>\n<p class=\"level3ix\">properties <a class=\"url1\" href=\"\/#calibre_link-1174\">697<\/a><\/p>\n<p class=\"level2ix\">validation messages <a class=\"url1\" href=\"\/#calibre_link-1075\">841<\/a><\/p>\n<p class=\"level2ix\">view components <a class=\"url1\" href=\"\/#calibre_link-1194\">669<\/a><\/p>\n<p class=\"level2ix\">ViewContext attribute <a class=\"url1\" href=\"\/#calibre_link-1181\">711<\/a><\/p>\n<p class=\"level2ix\">_ViewImports.cshtml file <a class=\"url1\" href=\"\/#calibre_link-1109\">698<\/a><\/p>\n<p class=\"level1ix\">Temp data <a class=\"url1\" href=\"\/#calibre_link-1195\">604<\/a><\/p>\n<p class=\"level1ix\">Type broker pattern <a class=\"url1\" href=\"\/#calibre_link-928\">355<\/a><\/p>\n<p class=\"grouptitlesix\">U<\/p>\n<p class=\"level1ix\">Unit testing<\/p>\n<p class=\"level2ix\">Assert methods <a class=\"url1\" href=\"\/#calibre_link-1196\">126<\/a><\/p>\n<p class=\"level2ix\">creating the test project <a class=\"url1\" href=\"\/#calibre_link-1197\">124<\/a><\/p>\n<p class=\"level2ix\">Fact attribute <a class=\"url1\" href=\"\/#calibre_link-1196\">126<\/a><\/p>\n<p class=\"level2ix\">isolating components <a class=\"url1\" href=\"\/#calibre_link-1198\">130<\/a><\/p>\n<p class=\"level2ix\">mocking <a class=\"url1\" href=\"\/#calibre_link-1199\">135<\/a><\/p>\n<p class=\"level2ix\">Moq package <a class=\"url1\" href=\"\/#calibre_link-1199\">135<\/a><\/p>\n<p class=\"level2ix\">MSTest <a class=\"url1\" href=\"\/#calibre_link-1197\">124<\/a><\/p>\n<p class=\"level2ix\">NUnit <a class=\"url1\" href=\"\/#calibre_link-1197\">124<\/a><\/p>\n<p class=\"level2ix\">project templates <a class=\"url1\" href=\"\/#calibre_link-1197\">124<\/a><\/p>\n<p class=\"level2ix\">running tests <a class=\"url1\" href=\"\/#calibre_link-1200\">127<\/a>, <a class=\"url1\" href=\"\/#calibre_link-881\">128<\/a><\/p>\n<p class=\"level2ix\">writing tests <a class=\"url1\" href=\"\/#calibre_link-1201\">125<\/a><\/p>\n<p class=\"level2ix\">XUnit <a class=\"url1\" href=\"\/#calibre_link-1197\">124<\/a><\/p>\n<p class=\"level1ix\">Unit Testing <a class=\"url1\" href=\"\/#calibre_link-1151\">139<\/a><\/p>\n<p class=\"level1ix\">URL Routing <a class=\"url1\" href=\"\/#calibre_link-1036\">316<\/a><\/p>\n<p class=\"level1ix\">User Secrets <a class=\"url1\" href=\"\/#calibre_link-889\">398<\/a><\/p>\n<p class=\"grouptitlesix\">V<\/p>\n<p class=\"level1ix\">View Components <a class=\"url1\" href=\"\/#calibre_link-1202\">667<\/a><\/p>\n<p class=\"level2ix\">applying<\/p>\n<p class=\"level3ix\">Component.InvokeAsync expression <a class=\"url1\" href=\"\/#calibre_link-1203\">668<\/a><\/p>\n<p class=\"level3ix\">custom HTML element <a class=\"url1\" href=\"\/#calibre_link-1194\">669<\/a><\/p>\n<p class=\"level3ix\">Razor Pages <a class=\"url1\" href=\"\/#calibre_link-1113\">670<\/a><\/p>\n<p class=\"level3ix\">tag helper <a class=\"url1\" href=\"\/#calibre_link-1194\">669<\/a><\/p>\n<p class=\"level3ix\">vc element <a class=\"url1\" href=\"\/#calibre_link-1194\">669<\/a><\/p>\n<p class=\"level2ix\">context data <a class=\"url1\" href=\"\/#calibre_link-1204\">678<\/a><\/p>\n<p class=\"level3ix\">parent view <a class=\"url1\" href=\"\/#calibre_link-1205\">679<\/a><\/p>\n<p class=\"level2ix\">creating <a class=\"url1\" href=\"\/#calibre_link-1202\">667<\/a><\/p>\n<p class=\"level2ix\">hybrid classes<\/p>\n<p class=\"level3ix\">controllers <a class=\"url1\" href=\"\/#calibre_link-1206\">688<\/a><\/p>\n<p class=\"level3ix\">Razor Pages <a class=\"url1\" href=\"\/#calibre_link-1207\">685<\/a><\/p>\n<p class=\"level3ix\">ViewComponent attribute <a class=\"url1\" href=\"\/#calibre_link-1207\">685<\/a><\/p>\n<p class=\"level3ix\">ViewComponentContext attribute <a class=\"url1\" href=\"\/#calibre_link-1207\">685<\/a><\/p>\n<p class=\"level2ix\">Invoke method <a class=\"url1\" href=\"\/#calibre_link-1202\">667<\/a><\/p>\n<p class=\"level2ix\">parent views <a class=\"url1\" href=\"\/#calibre_link-1205\">679<\/a><\/p>\n<p class=\"level2ix\">results <a class=\"url1\" href=\"\/#calibre_link-1208\">671<\/a><\/p>\n<p class=\"level3ix\">HTML fragments <a class=\"url1\" href=\"\/#calibre_link-1209\">675<\/a><\/p>\n<p class=\"level3ix\">partial views <a class=\"url1\" href=\"\/#calibre_link-1210\">672<\/a><\/p>\n<p class=\"level2ix\">ViewComponent attribute <a class=\"url1\" href=\"\/#calibre_link-1207\">685<\/a><\/p>\n<p class=\"level2ix\">ViewComponent class <a class=\"url1\" href=\"\/#calibre_link-1202\">667<\/a><\/p>\n<p class=\"level3ix\">context data <a class=\"url1\" href=\"\/#calibre_link-1204\">678<\/a><\/p>\n<p class=\"level3ix\">properties <a class=\"url1\" href=\"\/#calibre_link-1204\">678<\/a><\/p>\n<p class=\"level2ix\">views <a class=\"url1\" href=\"\/#calibre_link-1210\">672<\/a><\/p>\n<p class=\"level3ix\">search locations <a class=\"url1\" href=\"\/#calibre_link-1211\">674<\/a><\/p>\n<p class=\"level1ix\">Views <a class=\"url1\" href=\"\/#calibre_link-1092\">25<\/a><\/p>\n<p class=\"level2ix\">AddControllersWithViews method <a class=\"url1\" href=\"\/#calibre_link-1121\">565<\/a><\/p>\n<p class=\"level2ix\">AddRazorRuntimeCompilation method <a class=\"url1\" href=\"\/#calibre_link-1121\">565<\/a><\/p>\n<p class=\"level2ix\">content encoding<\/p>\n<p class=\"level3ix\">disable <a class=\"url1\" href=\"\/#calibre_link-1212\">627<\/a><\/p>\n<p class=\"level2ix\">content-encoding <a class=\"url1\" href=\"\/#calibre_link-1003\">626<\/a><\/p>\n<p class=\"level2ix\">CSHTML files <a class=\"url1\" href=\"\/#calibre_link-1213\">569<\/a><\/p>\n<p class=\"level2ix\">directives <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level3ix\">@addTagHelper <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level3ix\">@attribute <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level3ix\">@functions <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level3ix\">@implements <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level3ix\">@inherits <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level3ix\">@inject <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1099\">660<\/a><\/p>\n<p class=\"level3ix\">@model <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level3ix\">@namespace <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level3ix\">@page <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level3ix\">@section <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1215\">616<\/a><\/p>\n<p class=\"level3ix\">@using <a class=\"url1\" href=\"\/#calibre_link-1216\">583<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level2ix\">expressions<\/p>\n<p class=\"level3ix\">@ <a class=\"url1\" href=\"\/#calibre_link-1217\">588<\/a><\/p>\n<p class=\"level3ix\">attribute values <a class=\"url1\" href=\"\/#calibre_link-1218\">590<\/a><\/p>\n<p class=\"level3ix\">@await <a class=\"url1\" href=\"\/#calibre_link-1217\">588<\/a><\/p>\n<p class=\"level3ix\">code blocks <a class=\"url1\" href=\"\/#calibre_link-1217\">588<\/a><\/p>\n<p class=\"level3ix\">conditional expressions<\/p>\n<p class=\"level2ix\">if expressions <a class=\"url1\" href=\"\/#calibre_link-1219\">591<\/a><\/p>\n<p class=\"level3ix\">select expressions <a class=\"url1\" href=\"\/#calibre_link-1220\">592<\/a><\/p>\n<p class=\"level3ix\">element content <a class=\"url1\" href=\"\/#calibre_link-1221\">589<\/a><\/p>\n<p class=\"level3ix\">@foreach <a class=\"url1\" href=\"\/#calibre_link-1217\">588<\/a><\/p>\n<p class=\"level3ix\">@if <a class=\"url1\" href=\"\/#calibre_link-1217\">588<\/a><\/p>\n<p class=\"level3ix\">@Model <a class=\"url1\" href=\"\/#calibre_link-1217\">588<\/a><\/p>\n<p class=\"level3ix\">sequences<\/p>\n<p class=\"level2ix\">foreach expressions <a class=\"url1\" href=\"\/#calibre_link-1222\">594<\/a><\/p>\n<p class=\"level3ix\">@switch <a class=\"url1\" href=\"\/#calibre_link-1217\">588<\/a><\/p>\n<p class=\"level3ix\">@try <a class=\"url1\" href=\"\/#calibre_link-1217\">588<\/a><\/p>\n<p class=\"level2ix\">generated classes <a class=\"url1\" href=\"\/#calibre_link-1223\">575<\/a><\/p>\n<p class=\"level3ix\">generating URLs <a class=\"url1\" href=\"\/#calibre_link-1106\">734<\/a><\/p>\n<p class=\"level3ix\">Url property <a class=\"url1\" href=\"\/#calibre_link-1106\">734<\/a><\/p>\n<p class=\"level2ix\">HTML content-encoding <a class=\"url1\" href=\"\/#calibre_link-1003\">626<\/a><\/p>\n<p class=\"level2ix\">IntelliSense support <a class=\"url1\" href=\"\/#calibre_link-1224\">580<\/a><\/p>\n<p class=\"level2ix\">JSON content-encoding <a class=\"url1\" href=\"\/#calibre_link-1225\">628<\/a><\/p>\n<p class=\"level2ix\">layouts <a class=\"url1\" href=\"\/#calibre_link-1226\">607<\/a><\/p>\n<p class=\"level3ix\">configuring <a class=\"url1\" href=\"\/#calibre_link-1227\">609<\/a><\/p>\n<p class=\"level3ix\">disabling <a class=\"url1\" href=\"\/#calibre_link-1228\">614<\/a><\/p>\n<p class=\"level3ix\">optional sections <a class=\"url1\" href=\"\/#calibre_link-1229\">618<\/a><\/p>\n<p class=\"level3ix\">overriding the default layout <a class=\"url1\" href=\"\/#calibre_link-1230\">612<\/a><\/p>\n<p class=\"level3ix\">RenderSection method <a class=\"url1\" href=\"\/#calibre_link-1215\">616<\/a><\/p>\n<p class=\"level3ix\">section expressions <a class=\"url1\" href=\"\/#calibre_link-1215\">616<\/a><\/p>\n<p class=\"level3ix\">sections <a class=\"url1\" href=\"\/#calibre_link-1231\">615<\/a><\/p>\n<p class=\"level3ix\">selecting a layout <a class=\"url1\" href=\"\/#calibre_link-1232\">608<\/a><\/p>\n<p class=\"level3ix\">selecting programmatically <a class=\"url1\" href=\"\/#calibre_link-1233\">613<\/a><\/p>\n<p class=\"level2ix\">@model expressions <a class=\"url1\" href=\"\/#calibre_link-1234\">579<\/a><\/p>\n<p class=\"level2ix\">model expressions <a class=\"url1\" href=\"\/#calibre_link-1083\">712<\/a><\/p>\n<p class=\"level2ix\">@Model expressions <a class=\"url1\" href=\"\/#calibre_link-1235\">570<\/a><\/p>\n<p class=\"level2ix\">partial views <a class=\"url1\" href=\"\/#calibre_link-1236\">622<\/a><\/p>\n<p class=\"level3ix\">partial element <a class=\"url1\" href=\"\/#calibre_link-1237\">623<\/a><\/p>\n<p class=\"level2ix\">RazorPage&lt;T&gt; class <a class=\"url1\" href=\"\/#calibre_link-1238\">576<\/a><\/p>\n<p class=\"level3ix\">additional properties <a class=\"url1\" href=\"\/#calibre_link-1239\">578<\/a><\/p>\n<p class=\"level2ix\">RazorPage&lt;T&gt; properties <a class=\"url1\" href=\"\/#calibre_link-1238\">576<\/a><\/p>\n<p class=\"level2ix\">Razor syntax <a class=\"url1\" href=\"\/#calibre_link-1214\">587<\/a><\/p>\n<p class=\"level2ix\">recompilation <a class=\"url1\" href=\"\/#calibre_link-1235\">570<\/a><\/p>\n<p class=\"level2ix\">registering tag helpers <a class=\"url1\" href=\"\/#calibre_link-1109\">698<\/a><\/p>\n<p class=\"level2ix\">search path <a class=\"url1\" href=\"\/#calibre_link-1122\">568<\/a><\/p>\n<p class=\"level2ix\">selecting by name <a class=\"url1\" href=\"\/#calibre_link-1240\">571<\/a><\/p>\n<p class=\"level2ix\">shared views <a class=\"url1\" href=\"\/#calibre_link-1241\">573<\/a><\/p>\n<p class=\"level2ix\">temp data <a class=\"url1\" href=\"\/#calibre_link-1195\">604<\/a><\/p>\n<p class=\"level3ix\">keep method <a class=\"url1\" href=\"\/#calibre_link-1242\">606<\/a><\/p>\n<p class=\"level3ix\">peek method <a class=\"url1\" href=\"\/#calibre_link-1242\">606<\/a><\/p>\n<p class=\"level3ix\">TempData attribute <a class=\"url1\" href=\"\/#calibre_link-1242\">606<\/a><\/p>\n<p class=\"level3ix\">TempData property <a class=\"url1\" href=\"\/#calibre_link-1243\">605<\/a><\/p>\n<p class=\"level2ix\">templated delegates <a class=\"url1\" href=\"\/#calibre_link-1244\">625<\/a><\/p>\n<p class=\"level2ix\">view bag <a class=\"url1\" href=\"\/#calibre_link-1245\">602<\/a><\/p>\n<p class=\"level2ix\">view components <a class=\"url1\" href=\"\/#calibre_link-1210\">672<\/a><\/p>\n<p class=\"level2ix\">view import file <a class=\"url1\" href=\"\/#calibre_link-1236\">622<\/a><\/p>\n<p class=\"level2ix\">view imports file<\/p>\n<p class=\"level3ix\">_ViewImports.cshtml file <a class=\"url1\" href=\"\/#calibre_link-1246\">581<\/a><\/p>\n<p class=\"level2ix\">view model object <a class=\"url1\" href=\"\/#calibre_link-1235\">570<\/a><\/p>\n<p class=\"level2ix\">View model type<\/p>\n<p class=\"level3ix\">@model expressions <a class=\"url1\" href=\"\/#calibre_link-1239\">578<\/a><\/p>\n<p class=\"level2ix\">view start files <a class=\"url1\" href=\"\/#calibre_link-1247\">611<\/a><\/p>\n<p class=\"level1ix\">Visual Studio<\/p>\n<p class=\"level2ix\">installing <a class=\"url1\" href=\"\/#calibre_link-1248\">14<\/a><\/p>\n<p class=\"level3ix\">LocalDB <a class=\"url1\" href=\"\/#calibre_link-1017\">15<\/a><\/p>\n<p class=\"level3ix\">workloads <a class=\"url1\" href=\"\/#calibre_link-1017\">15<\/a><\/p>\n<p class=\"level1ix\">Visual Studio Code<\/p>\n<p class=\"level2ix\">installing <a class=\"url1\" href=\"\/#calibre_link-1081\">16<\/a><\/p>\n<p class=\"level3ix\">LocalDB <a class=\"url1\" href=\"\/#calibre_link-1018\">17<\/a><\/p>\n<p class=\"grouptitlesix\">W<\/p>\n<p class=\"level1ix\">Web Pages <a class=\"url1\" href=\"\/#calibre_link-737\">3<\/a><\/p>\n<p class=\"level1ix\">Web services<\/p>\n<p class=\"level2ix\">authentication <a class=\"url1\" href=\"\/#calibre_link-751\">1194<\/a><\/p>\n<p class=\"level2ix\">authorization <a class=\"url1\" href=\"\/#calibre_link-751\">1194<\/a><\/p>\n<p class=\"level1ix\">Web Services <a class=\"url1\" href=\"\/#calibre_link-737\">3<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1116\">494<\/a><\/p>\n<p class=\"level2ix\">action methods<\/p>\n<p class=\"level3ix\">results <a class=\"url1\" href=\"\/#calibre_link-1249\">503<\/a><\/p>\n<p class=\"level2ix\">action results <a class=\"url1\" href=\"\/#calibre_link-735\">514<\/a><\/p>\n<p class=\"level2ix\">actions<\/p>\n<p class=\"level3ix\">HTTP method attributes <a class=\"url1\" href=\"\/#calibre_link-1250\">502<\/a><\/p>\n<p class=\"level2ix\">ApiController attribute <a class=\"url1\" href=\"\/#calibre_link-1251\">521<\/a><\/p>\n<p class=\"level2ix\">Content Formatting <a class=\"url1\" href=\"\/#calibre_link-1252\">540<\/a><\/p>\n<p class=\"level3ix\">custom formatters <a class=\"url1\" href=\"\/#calibre_link-1253\">543<\/a><\/p>\n<p class=\"level3ix\">default policy <a class=\"url1\" href=\"\/#calibre_link-1252\">540<\/a><\/p>\n<p class=\"level3ix\">JSON formatting <a class=\"url1\" href=\"\/#calibre_link-1253\">543<\/a><\/p>\n<p class=\"level3ix\">Restricting Formats <a class=\"url1\" href=\"\/#calibre_link-1254\">549<\/a><\/p>\n<p class=\"level3ix\">XML formatting <a class=\"url1\" href=\"\/#calibre_link-1253\">543<\/a><\/p>\n<p class=\"level2ix\">Content Negotiation <a class=\"url1\" href=\"\/#calibre_link-892\">542<\/a><\/p>\n<p class=\"level3ix\">Accept header <a class=\"url1\" href=\"\/#calibre_link-892\">542<\/a><\/p>\n<p class=\"level3ix\">Consumes attribute <a class=\"url1\" href=\"\/#calibre_link-1254\">549<\/a><\/p>\n<p class=\"level3ix\">Respecting Accept header <a class=\"url1\" href=\"\/#calibre_link-1255\">545<\/a><\/p>\n<p class=\"level3ix\">Using the URL <a class=\"url1\" href=\"\/#calibre_link-1256\">548<\/a><\/p>\n<p class=\"level2ix\">controllers <a class=\"url1\" href=\"\/#calibre_link-882\">498<\/a><\/p>\n<p class=\"level3ix\">asynchronous actions <a class=\"url1\" href=\"\/#calibre_link-1257\">511<\/a><\/p>\n<p class=\"level3ix\">attributes <a class=\"url1\" href=\"\/#calibre_link-1079\">501<\/a><\/p>\n<p class=\"level3ix\">ControllerBase class <a class=\"url1\" href=\"\/#calibre_link-1079\">501<\/a><\/p>\n<p class=\"level3ix\">creating <a class=\"url1\" href=\"\/#calibre_link-734\">500<\/a><\/p>\n<p class=\"level3ix\">dependency injection <a class=\"url1\" href=\"\/#calibre_link-909\">504<\/a><\/p>\n<p class=\"level3ix\">FromServices attribute <a class=\"url1\" href=\"\/#calibre_link-915\">505<\/a><\/p>\n<p class=\"level3ix\">model binding <a class=\"url1\" href=\"\/#calibre_link-1039\">506<\/a><\/p>\n<p class=\"level3ix\">services <a class=\"url1\" href=\"\/#calibre_link-909\">504<\/a><\/p>\n<p class=\"level2ix\">CORS <a class=\"url1\" href=\"\/#calibre_link-899\">510<\/a><\/p>\n<p class=\"level2ix\">data validation <a class=\"url1\" href=\"\/#calibre_link-900\">519<\/a><\/p>\n<p class=\"level2ix\">GraphQL <a class=\"url1\" href=\"\/#calibre_link-1001\">495<\/a><\/p>\n<p class=\"level2ix\">gRPC <a class=\"url1\" href=\"\/#calibre_link-1001\">495<\/a><\/p>\n<p class=\"level2ix\">HTTP PATCH method <a class=\"url1\" href=\"\/#calibre_link-1016\">536<\/a><\/p>\n<p class=\"level3ix\">JSON Patch <a class=\"url1\" href=\"\/#calibre_link-1016\">536<\/a><\/p>\n<p class=\"level2ix\">JSON <a class=\"url1\" href=\"\/#calibre_link-1001\">495<\/a><\/p>\n<p class=\"level2ix\">JSON Patch <a class=\"url1\" href=\"\/#calibre_link-1016\">536<\/a>, <a class=\"url1\" href=\"\/#calibre_link-1258\">538<\/a><\/p>\n<p class=\"level3ix\">JSON.NET Serializer <a class=\"url1\" href=\"\/#calibre_link-1015\">537<\/a><\/p>\n<p class=\"level3ix\">JsonPatchDocument&lt;T&gt; class <a class=\"url1\" href=\"\/#calibre_link-1259\">539<\/a><\/p>\n<p class=\"level3ix\">NuGet package <a class=\"url1\" href=\"\/#calibre_link-1015\">537<\/a><\/p>\n<p class=\"level3ix\">specification <a class=\"url1\" href=\"\/#calibre_link-1016\">536<\/a><\/p>\n<p class=\"level2ix\">model validation <a class=\"url1\" href=\"\/#calibre_link-1067\">860<\/a><\/p>\n<p class=\"level2ix\">null properties <a class=\"url1\" href=\"\/#calibre_link-1260\">523<\/a><\/p>\n<p class=\"level2ix\">OpenAPI <a class=\"url1\" href=\"\/#calibre_link-1082\">553<\/a><\/p>\n<p class=\"level3ix\">API Analyzer <a class=\"url1\" href=\"\/#calibre_link-1261\">557<\/a><\/p>\n<p class=\"level3ix\">conflicts <a class=\"url1\" href=\"\/#calibre_link-1082\">553<\/a><\/p>\n<p class=\"level3ix\">Nuget Package <a class=\"url1\" href=\"\/#calibre_link-1262\">554<\/a><\/p>\n<p class=\"level3ix\">ProducesResponseType attribute <a class=\"url1\" href=\"\/#calibre_link-1263\">559<\/a><\/p>\n<p class=\"level2ix\">Related data <a class=\"url1\" href=\"\/#calibre_link-944\">533<\/a><\/p>\n<p class=\"level2ix\">remote model validation <a class=\"url1\" href=\"\/#calibre_link-1062\">869<\/a><\/p>\n<p class=\"level2ix\">REST <a class=\"url1\" href=\"\/#calibre_link-1116\">494<\/a><\/p>\n<p class=\"level2ix\">routing <a class=\"url1\" href=\"\/#calibre_link-1079\">501<\/a><\/p>\n<p class=\"level2ix\">URLs and HTTP Methods <a class=\"url1\" href=\"\/#calibre_link-1001\">495<\/a><\/p>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Praises from reviewers of Pro ASP.NET Core 7, Tenth Edition If you\u2019re looking for breadth and depth coverage of ASP.NET Core development, this is the book for you. &mdash;Greg White, Software Development Manager, PicoBrew Inc. A must have book for the .NET developer\/engineer. &mdash;Foster Haines, Consultant, Foster\u2019s Website Company The book for web development professionals. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-852","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/852","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=852"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/852\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=852"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=852"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=852"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}