+ All Categories
Home > Documents > Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... ·...

Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... ·...

Date post: 10-Mar-2021
Category:
Upload: others
View: 3 times
Download: 0 times
Share this document with a friend
127
Transcript
Page 1: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.
Page 2: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

1.1

1.2

1.2.1

1.2.2

1.2.3

1.3

1.3.1

1.3.2

1.3.3

1.3.4

1.3.5

1.3.6

1.3.7

1.4

1.5

1.5.1

1.5.2

1.5.3

1.5.4

1.6

1.6.1

1.6.2

1.7

TableofContentsIntroduction

Yourfirstapplication

GettheSDK

HelloWorldinC#

CreateanASP.NETCoreproject

MVCbasics

Createacontroller

Createmodels

Createaview

Addaserviceclass

Usedependencyinjection

Finishthecontroller

Updatethelayout

Addexternalpackages

Useadatabase

Connecttoadatabase

Updatethecontext

Createamigration

Createanewserviceclass

Addmorefeatures

Addnewto-doitems

Completeitemswithacheckbox

Securityandidentity

2

Page 3: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

1.7.1

1.7.2

1.7.3

1.7.4

1.8

1.8.1

1.8.2

1.9

1.9.1

1.9.2

1.10

Requireauthentication

Usingidentityintheapplication

Authorizationwithroles

Moreresources

Automatedtesting

Unittesting

Integrationtesting

Deploytheapplication

DeploytoAzure

DeploywithDocker

Conclusion

3

Page 4: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

TheLittleASP.NETCoreBookbyNateBarbettini

Copyright©2018.Allrightsreserved.

ISBN:978-1-387-75615-5

ReleasedundertheCreativeCommonsAttribution4.0license.Youarefreetoshare,copy,andredistributethisbookinanyformat,orremixandtransformitforanypurpose(evencommercially).Youmustgiveappropriatecreditandprovidealinktothelicense.

Formoreinformation,visithttps://creativecommons.org/licenses/by/4.0/

IntroductionThanksforpickingupTheLittleASP.NETCoreBook!IwrotethisshortbooktohelpdevelopersandpeopleinterestedinwebprogramminglearnaboutASP.NETCore,anewframeworkforbuildingwebapplicationsandAPIs.

TheLittleASP.NETCoreBookisstructuredasatutorial.You'llbuildanapplicationfromstarttofinishandlearn:

ThebasicsoftheMVC(Model-View-Controller)patternHowfront-endcode(HTML,CSS,JavaScript)workstogetherwithback-endcodeWhatdependencyinjectionisandwhyit'susefulHowtoreadandwritedatatoadatabaseHowtoaddlog-in,registration,andsecurityHowtodeploytheapplicationtotheweb

Introduction

4

Page 5: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Don'tworry,youdon'tneedtoknowanythingaboutASP.NETCore(oranyoftheabove)togetstarted.

BeforeyoubeginThecodeforthefinishedversionoftheapplicationyou'llbuildisavailableonGitHub:

https://www.github.com/nbarbettini/little-aspnetcore-todo

Feelfreetodownloaditifyouwanttoseethefinishedproduct,orcompareasyouwriteyourowncode.

Thebookitselfisupdatedfrequentlywithbugfixesandnewcontent.Ifyou'rereadingaPDF,e-book,orprintversion,checktheofficialwebsite(littleasp.net/book)toseeifthere'sanupdatedversionavailable.Theverylastpageofthebookcontainsversioninformationandachangelog.

Readinginyourownlanguage

Thankstosomefantasticmultilingualcontributors,theLittleASP.NETCoreBookhasbeentranslatedintootherlanguages:

Turkish:https://sahinyanlik.gitbooks.io/kisa-asp-net-core-kitabi/

Chinese:https://windsting.github.io/little-aspnetcore-book/book/

WhothisbookisforIfyou'renewtoprogramming,thisbookwillintroduceyoutothepatternsandconceptsusedtobuildmodernwebapplications.You'lllearnhowtobuildawebapp(andhowthebigpiecesfittogether)by

Introduction

5

Page 6: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

buildingsomethingfromscratch!Whilethislittlebookwon'tbeabletocoverabsolutelyeverythingyouneedtoknowaboutprogramming,it'llgiveyouastartingpointsoyoucanlearnmoreadvancedtopics.

IfyoualreadycodeinabackendlanguagelikeNode,Python,Ruby,Go,orJava,you'llnoticealotoffamiliarideaslikeMVC,viewtemplates,anddependencyinjection.ThecodewillbeinC#,butitwon'tlooktoodifferentfromwhatyoualreadyknow.

Ifyou'reanASP.NETMVCdeveloper,you'llfeelrightathome!ASP.NETCoreaddssomenewtoolsandreuses(andsimplifies)thethingsyoualreadyknow.I'llpointoutsomeofthedifferencesbelow.

Nomatterwhatyourpreviousexperiencewithwebprogramming,thisbookwillteachyoueverythingyouneedtocreateasimpleandusefulwebapplicationinASP.NETCore.You'lllearnhowtobuildfunctionalityusingbackendandfrontendcode,howtointeractwithadatabase,andhowtodeploytheapptotheworld.

WhatisASP.NETCore?ASP.NETCoreisawebframeworkcreatedbyMicrosoftforbuildingwebapplications,APIs,andmicroservices.ItusescommonpatternslikeMVC(Model-View-Controller),dependencyinjection,andarequestpipelinecomprisedofmiddleware.It'sopen-sourceundertheApache2.0license,whichmeansthesourcecodeisfreelyavailable,andthecommunityisencouragedtocontributebugfixesandnewfeatures.

ASP.NETCorerunsontopofMicrosoft's.NETruntime,similartotheJavaVirtualMachine(JVM)ortheRubyinterpreter.YoucanwriteASP.NETCoreapplicationsinanumberoflanguages(C#,VisualBasic,F#).C#isthemostpopularchoice,andit'swhatI'lluseinthisbook.YoucanbuildandrunASP.NETCoreapplicationsonWindows,Mac,andLinux.

Introduction

6

Page 7: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Whydoweneedanotherwebframework?Therearealotofgreatwebframeworkstochoosefromalready:Node/Express,Spring,RubyonRails,Django,Laravel,andmanymore.WhatadvantagesdoesASP.NETCorehave?

Speed.ASP.NETCoreisfast.Because.NETcodeiscompiled,itexecutesmuchfasterthancodeininterpretedlanguageslikeJavaScriptorRuby.ASP.NETCoreisalsooptimizedformultithreadingandasynchronoustasks.It'scommontoseea5-10xspeedimprovementovercodewritteninNode.js.

Ecosystem.ASP.NETCoremaybenew,but.NEThasbeenaroundforalongtime.TherearethousandsofpackagesavailableonNuGet(the.NETpackagemanager;thinknpm,Rubygems,orMaven).TherearealreadypackagesavailableforJSONdeserialization,databaseconnectors,PDFgeneration,oralmostanythingelseyoucanthinkof.

Security.TheteamatMicrosofttakessecurityseriously,andASP.NETCoreisbuilttobesecurefromthegroundup.Ithandlesthingslikesanitizinginputdataandpreventingcross-siterequestforgery(CSRF)attacks,soyoudon'thaveto.Youalsogetthebenefitofstatictypingwiththe.NETcompiler,whichislikehavingaveryparanoidlinterturnedonatalltimes.Thismakesithardertodosomethingyoudidn'tintendwithavariableorchunkofdata.

.NETCoreand.NETStandardThroughoutthisbook,you'llbelearningaboutASP.NETCore(thewebframework).I'lloccasionallymentionthe.NETruntime,thesupportinglibrarythatruns.NETcode.IfthisalreadysoundslikeGreektoyou,just

Introduction

7

Page 8: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

skiptothenextchapter!

Youmayalsohearabout.NETCoreand.NETStandard.Thenaminggetsconfusing,sohere'sasimpleexplanation:

.NETStandardisaplatform-agnosticinterfacethatdefinesfeaturesandAPIs.It'simportanttonotethat.NETStandarddoesn'trepresentanyactualcodeorfunctionality,justtheAPIdefinition.Therearedifferent"versions"orlevelsof.NETStandardthatreflecthowmanyAPIsareavailable(orhowwidetheAPIsurfaceareais).Forexample,.NETStandard2.0hasmoreAPIsavailablethan.NETStandard1.5,whichhasmoreAPIsthan.NETStandard1.0.

.NETCoreisthe.NETruntimethatcanbeinstalledonWindows,Mac,orLinux.ItimplementstheAPIsdefinedinthe.NETStandardinterfacewiththeappropriateplatform-specificcodeoneachoperatingsystem.Thisiswhatyou'llinstallonyourownmachinetobuildandrunASP.NETCoreapplications.

Andjustforgoodmeasure,.NETFrameworkisadifferentimplementationof.NETStandardthatisWindows-only.Thiswastheonly.NETruntimeuntil.NETCorecamealongandbrought.NETtoMacandLinux.ASP.NETCorecanalsorunonWindows-only.NETFramework,butIwon'ttouchonthistoomuch.

Ifyou'reconfusedbyallthisnaming,noworries!We'llgettosomerealcodeinabit.

AnotetoASP.NET4developersIfyouhaven'tusedapreviousversionofASP.NET,skipaheadtothenextchapter.

Introduction

8

Page 9: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

ASP.NETCoreisacompleteground-uprewriteofASP.NET,withafocusonmodernizingtheframeworkandfinallydecouplingitfromSystem.Web,IIS,andWindows.IfyourememberalltheOWIN/KatanastufffromASP.NET4,you'realreadyhalfwaythere:theKatanaprojectbecameASP.NET5whichwasultimatelyrenamedtoASP.NETCore.

BecauseoftheKatanalegacy,theStartupclassisfrontandcenter,andthere'snomoreApplication_StartorGlobal.asax.Theentirepipelineisdrivenbymiddleware,andthere'snolongerasplitbetweenMVCandWebAPI:controllerscansimplyreturnviews,statuscodes,ordata.Dependencyinjectioncomesbakedin,soyoudon'tneedtoinstallandconfigureacontainerlikeStructureMaporNinjectifyoudon'twantto.Andtheentireframeworkhasbeenoptimizedforspeedandruntimeefficiency.

Alright,enoughintroduction.Let'sdiveintoASP.NETCore!

Introduction

9

Page 10: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

YourfirstapplicationReadytobuildyourfirstwebappwithASP.NETCore?You'llneedtogatherafewthingsfirst:

Yourfavoritecodeeditor.YoucanuseAtom,Sublime,Notepad,orwhatevereditoryoupreferwritingcodein.Ifyoudon'thaveafavorite,giveVisualStudioCodeatry.It'safree,cross-platformcodeeditorthathasrichsupportforwritingC#,JavaScript,HTML,andmore.Justsearchfor"downloadvisualstudiocode"andfollowtheinstructions.

Ifyou'reonWindows,youcanalsouseVisualStudiotobuildASP.NETCoreapplications.You'llneedVisualStudio2017version15.3orlater(thefreeCommunityEditionisfine).VisualStudiohasgreatcodecompletionandrefactoringsupportforC#,althoughVisualStudioCodeisclosebehind.

The.NETCoreSDK.Regardlessoftheeditororplatformyou'reusing,you'llneedtoinstallthe.NETCoreSDK,whichincludestheruntime,baselibraries,andcommandlinetoolsyouneedforbuildingASP.NETCoreapplications.TheSDKcanbeinstalledonWindows,Mac,orLinux.

Onceyou'vedecidedonaneditor,you'llneedtogettheSDK.

Yourfirstapplication

10

Page 11: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

GettheSDKSearchfor"download.netcore"andfollowtheinstructionsonMicrosoft'sdownloadpagetogetthe.NETCoreSDK.AftertheSDKhasfinishedinstalling,openuptheTerminal(orPowerShellonWindows)andusethedotnetcommandlinetool(alsocalledaCLI)tomakesureeverythingisworking:

dotnet--version

2.1.104

Youcangetmoreinformationaboutyourplatformwiththe--infoflag:

dotnet--info

.NETCommandLineTools(2.1.104)

ProductInformation:

Version:2.1.104

CommitSHA-1hash:48ec687460

RuntimeEnvironment:

OSName:MacOSX

OSVersion:10.13

(moredetails...)

Ifyouseeoutputliketheabove,you'rereadytogo!

GettheSDK

11

Page 12: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

HelloWorldinC#BeforeyoudiveintoASP.NETCore,trycreatingandrunningasimpleC#application.

Youcandothisallfromthecommandline.First,openuptheTerminal(orPowerShellonWindows).Navigatetothelocationyouwanttostoreyourprojects,suchasyourDocumentsdirectory:

cdDocuments

Usethedotnetcommandtocreateanewproject:

dotnetnewconsole-oCsharpHelloWorld

Thedotnetnewcommandcreatesanew.NETprojectinC#bydefault.Theconsoleparameterselectsatemplateforaconsoleapplication(aprogramthatoutputstexttothescreen).The-oCsharpHelloWorldparametertellsdotnetnewtocreateanewdirectorycalledCsharpHelloWorldforalltheprojectfiles.Moveintothisnewdirectory:

cdCsharpHelloWorld

dotnetnewconsolecreatesabasicC#programthatwritesthetextHelloWorld!tothescreen.Theprogramiscomprisedoftwofiles:aprojectfile(witha.csprojextension)andaC#codefile(witha.csextension).Ifyouopentheformerinatextorcodeeditor,you'llseethis:

CsharpHelloWorld.csproj

<ProjectSdk="Microsoft.NET.Sdk">

HelloWorldinC#

12

Page 13: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

<PropertyGroup>

<OutputType>Exe</OutputType>

<TargetFramework>netcoreapp2.0</TargetFramework>

</PropertyGroup>

</Project>

TheprojectfileisXML-basedanddefinessomemetadataabouttheproject.Later,whenyoureferenceotherpackages,thosewillbelistedhere(similartoapackage.jsonfilefornpm).Youwon'thavetoeditthisfilebyhandveryoften.

Program.cs

usingSystem;

namespaceCsharpHelloWorld

{

classProgram

{

staticvoidMain(string[]args)

{

Console.WriteLine("HelloWorld!");

}

}

}

staticvoidMainistheentrypointmethodofaC#program,andbyconventionit'splacedinaclass(atypeofcodestructureormodule)calledProgram.Theusingstatementatthetopimportsthebuilt-inSystemclassesfrom.NETandmakesthemavailabletothecodeinyourclass.

Frominsidetheprojectdirectory,usedotnetruntoruntheprogram.You'llseetheoutputwrittentotheconsoleafterthecodecompiles:

dotnetrun

HelloWorldinC#

13

Page 14: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

HelloWorld!

That'sallittakestoscaffoldandruna.NETprogram!Next,you'lldothesamethingforanASP.NETCoreapplication.

HelloWorldinC#

14

Page 15: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

CreateanASP.NETCoreprojectIfyou'restillinthedirectoryyoucreatedfortheHelloWorldsample,movebackuptoyourDocumentsorhomedirectory:

cd..

Next,createanewdirectorytostoreyourentireproject,andmoveintoit:

mkdirAspNetCoreTodo

cdAspNetCoreTodo

Next,createanewprojectwithdotnetnew,thistimewithsomeextraoptions:

dotnetnewmvc--authIndividual-oAspNetCoreTodo

cdAspNetCoreTodo

Thiscreatesanewprojectfromthemvctemplate,andaddssomeadditionalauthenticationandsecuritybitstotheproject.(I'llcoversecurityintheSecurityandidentitychapter.)

YoumightbewonderingwhyyouhaveadirectorycalledAspNetCoreTodoinsideanotherdirectorycalledAspNetCoreTodo.Thetop-levelor"root"directorycancontainoneormoreprojectdirectories.Therootdirectoryissometimescalledasolutiondirectory.Later,you'lladdmoreprojectdirectoriesside-by-sidewiththeAspNetCoreTodoprojectdirectory,allwithinasinglerootsolutiondirectory.

CreateanASP.NETCoreproject

15

Page 16: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

You'llseequiteafewfilesshowupinthenewprojectdirectory.Onceyoucdintothenewdirectory,allyouhavetodoisruntheproject:

dotnetrun

Nowlisteningon:http://localhost:5000

Applicationstarted.PressCtrl+Ctoshutdown.

Insteadofprintingtotheconsoleandexiting,thisprogramstartsawebserverandwaitsforrequestsonport5000.

Openyourwebbrowserandnavigatetohttp://localhost:5000.You'llseethedefaultASP.NETCoresplashpage,whichmeansyourprojectisworking!Whenyou'redone,pressCtrl-Cintheterminalwindowtostoptheserver.

ThepartsofanASP.NETCoreproject

Thedotnetnewmvctemplategeneratesanumberoffilesanddirectoriesforyou.Herearethemostimportantthingsyougetoutofthebox:

TheProgram.csandStartup.csfilessetupthewebserverandASP.NETCorepipeline.TheStartupclassiswhereyoucanaddmiddlewarethathandlesandmodifiesincomingrequests,andservesthingslikestaticcontentorerrorpages.It'salsowhereyouaddyourownservicestothedependencyinjectioncontainer(moreonthislater).

TheModels,Views,andControllersdirectoriescontainthecomponentsoftheModel-View-Controller(MVC)architecture.You'llexploreallthreeinthenextchapter.

CreateanASP.NETCoreproject

16

Page 17: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

ThewwwrootdirectorycontainsstaticassetslikeCSS,JavaScript,andimagefiles.Filesinwwwrootwillbeservedasstaticcontent,andcanbebundledandminifiedautomatically.

Theappsettings.jsonfilecontainsconfigurationsettingsASP.NETCorewillloadonstartup.Youcanusethistostoredatabaseconnectionstringsorotherthingsthatyoudon'twanttohard-code.

TipsforVisualStudioCode

Ifyou'reusingVisualStudioCodeforthefirsttime,hereareacoupleofhelpfultipstogetyoustarted:

Opentheprojectrootfolder:InVisualStudioCode,chooseFile-OpenorFile-OpenFolder.OpentheAspNetCoreTodofolder(therootdirectory),nottheinnerprojectdirectory.IfVisualStudioCodepromptsyoutoinstallmissingfiles,clickYestoaddthem.

F5torun(anddebugbreakpoints):Withyourprojectopen,pressF5toruntheprojectindebugmode.Thisisthesameasdotnetrunonthecommandline,butyouhavethebenefitofsettingbreakpointsinyourcodebyclickingontheleftmargin:

Lightbulbtofixproblems:Ifyourcodecontainsredsquiggles(compilererrors),putyourcursoronthecodethat'sredandlookforthelightbulbiconontheleftmargin.Thelightbulbmenuwillsuggest

CreateanASP.NETCoreproject

17

Page 18: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

commonfixes,likeaddingamissingusingstatementtoyourcode:

Compilequickly:UsetheshortcutCommand-Shift-BorControl-Shift-BtoruntheBuildtask,whichdoesthesamethingasdotnetbuild.

ThesetipsapplytoVisualStudio(notCode)onWindowstoo.Ifyou'reusingVisualStudio,you'llneedtoopenthe.csprojprojectfiledirectly.VisualStudiowilllaterpromptyoutosavetheSolutionfile,whichyoushouldsaveintherootdirectory(thefirstAspNetCoreTodofolder).YoucanalsocreateanASP.NETCoreprojectdirectlywithinVisualStudiousingthetemplatesinFile-NewProject.

AnoteaboutGit

IfyouuseGitorGitHubtomanageyoursourcecode,nowisagoodtimetodogitinitandinitializeaGitrepositoryintheprojectrootdirectory:

cd..

gitinit

Makesureyouadda.gitignorefilethatignoresthebinandobjdirectories.TheVisualStudiotemplateonGitHub'sgitignoretemplaterepo(https://github.com/github/gitignore)worksgreat.

CreateanASP.NETCoreproject

18

Page 19: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

There'splentymoretoexplore,solet'sdiveinandstartbuildinganapplication!

CreateanASP.NETCoreproject

19

Page 20: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

MVCbasicsInthischapter,you'llexploretheMVCsysteminASP.NETCore.MVC(Model-View-Controller)isapatternforbuildingwebapplicationsthat'susedinalmosteverywebframework(RubyonRailsandExpressarepopularexamples),plusfrontendJavaScriptframeworkslikeAngular.MobileappsoniOSandAndroiduseavariationofMVCaswell.

Asthenamesuggests,MVChasthreecomponents:models,views,andcontrollers.Controllershandleincomingrequestsfromaclientorwebbrowserandmakedecisionsaboutwhatcodetorun.Viewsaretemplates(usuallyHTMLplusatemplatinglanguagelikeHandlebars,Pug,orRazor)thatgetdataaddedtothemandthenaredisplayedtotheuser.Modelsholdthedatathatisaddedtoviews,ordatathatisenteredbytheuser.

AcommonpatternforMVCcodeis:

ThecontrollerreceivesarequestandlooksupsomeinformationinadatabaseThecontrollercreatesamodelwiththeinformationandattachesittoaviewTheviewisrenderedanddisplayedintheuser'sbrowserTheuserclicksabuttonorsubmitsaform,whichsendsanewrequesttothecontroller,andthecyclerepeats

Ifyou'veworkedwithMVCinotherlanguages,you'llfeelrightathomeinASP.NETCoreMVC.Ifyou'renewtoMVC,thischapterwillteachyouthebasicsandwillhelpgetyoustarted.

Whatyou'llbuild

MVCbasics

20

Page 21: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

The"HelloWorld"exerciseofMVCisbuildingato-dolistapplication.It'sagreatprojectsinceit'ssmallandsimpleinscope,butittoucheseachpartofMVCandcoversmanyoftheconceptsyou'duseinalargerapplication.

Inthisbook,you'llbuildato-doappthatletstheuseradditemstotheirto-dolistandcheckthemoffoncecomplete.Morespecifically,you'llbecreating:

Awebapplicationserver(sometimescalledthe"backend")usingASP.NETCore,C#,andtheMVCpatternAdatabasetostoretheuser'sto-doitemsusingtheSQLitedatabaseengineandasystemcalledEntityFrameworkCoreWebpagesandaninterfacethattheuserwillinteractwithviatheirbrowser,usingHTML,CSS,andJavaScript(calledthe"frontend")Aloginformandsecuritycheckssoeachuser'sto-dolistiskeptprivate

Soundgood?Let'sbuiltit!Ifyouhaven'talreadycreatedanewASP.NETCoreprojectusingdotnetnewmvc,followthestepsinthepreviouschapter.Youshouldbeabletobuildandruntheprojectandseethedefaultwelcomescreen.

MVCbasics

21

Page 22: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

CreateacontrollerTherearealreadyafewcontrollersintheproject'sControllersdirectory,includingtheHomeControllerthatrendersthedefaultwelcomescreenyouseewhenyouvisithttp://localhost:5000.Youcanignorethesecontrollersfornow.

Createanewcontrollerfortheto-dolistfunctionality,calledTodoController,andaddthefollowingcode:

Controllers/TodoController.cs

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Threading.Tasks;

usingMicrosoft.AspNetCore.Mvc;

namespaceAspNetCoreTodo.Controllers

{

publicclassTodoController:Controller

{

//Actionsgohere

}

}

Routesthatarehandledbycontrollersarecalledactions,andarerepresentedbymethodsinthecontrollerclass.Forexample,theHomeControllerincludesthreeactionmethods(Index,About,andContact)whicharemappedbyASP.NETCoretotheserouteURLs:

localhost:5000/Home->Index()

localhost:5000/Home/About->About()

localhost:5000/Home/Contact->Contact()

Createacontroller

22

Page 23: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Thereareanumberofconventions(commonpatterns)usedbyASP.NETCore,suchasthepatternthatFooControllerbecomes/Foo,andtheIndexactionnamecanbeleftoutoftheURL.Youcancustomizethisbehaviorifyou'dlike,butfornow,we'llsticktothedefaultconventions.

AddanewactioncalledIndextotheTodoController,replacingthe//Actionsgoherecomment:

publicclassTodoController:Controller

{

publicIActionResultIndex()

{

//Getto-doitemsfromdatabase

//Putitemsintoamodel

//Renderviewusingthemodel

}

}

Actionmethodscanreturnviews,JSONdata,orHTTPstatuscodeslike200OKand404NotFound.TheIActionResultreturntypegivesyoutheflexibilitytoreturnanyofthesefromtheaction.

It'sabestpracticetokeepcontrollersaslightweightaspossible.Inthiscase,thecontrollerwillberesponsibleforgettingtheto-doitemsfromthedatabase,puttingthoseitemsintoamodeltheviewcanunderstand,andsendingtheviewbacktotheuser'sbrowser.

Beforeyoucanwritetherestofthecontrollercode,youneedtocreateamodelandaview.

Createacontroller

23

Page 24: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

CreatemodelsTherearetwoseparatemodelclassesthatneedtobecreated:amodelthatrepresentsato-doitemstoredinthedatabase(sometimescalledanentity),andthemodelthatwillbecombinedwithaview(theMVinMVC)andsentbacktotheuser'sbrowser.Becausebothofthemcanbereferredtoas"models",I'llrefertothelatterasaviewmodel.

First,createaclasscalledTodoItemintheModelsdirectory:

Models/TodoItem.cs

usingSystem;

usingSystem.ComponentModel.DataAnnotations;

namespaceAspNetCoreTodo.Models

{

publicclassTodoItem

{

publicGuidId{get;set;}

publicboolIsDone{get;set;}

[Required]

publicstringTitle{get;set;}

publicDateTimeOffset?DueAt{get;set;}

}

}

Thisclassdefineswhatthedatabasewillneedtostoreforeachto-doitem:anID,atitleorname,whethertheitemiscomplete,andwhattheduedateis.Eachlinedefinesapropertyoftheclass:

Createmodels

24

Page 25: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

TheIdpropertyisaguid,oragloballyuniqueidentifier.Guids(orGUIDs)arelongstringsoflettersandnumbers,like43ec09f2-7f70-4f4b-9559-65011d5781bb.Becauseguidsarerandomandareextremelyunlikelytobeaccidentallyduplicated,theyarecommonlyusedasuniqueIDs.Youcouldalsouseanumber(integer)asadatabaseentityID,butyou'dneedtoconfigureyourdatabasetoalwaysincrementthenumberwhennewrowsareaddedtothedatabase.Guidsaregeneratedrandomly,soyoudon'thavetoworryaboutauto-incrementing.

TheIsDonepropertyisaboolean(true/falsevalue).Bydefault,itwillbefalseforallnewitems.Lateryou'llusewritecodetoswitchthispropertytotruewhentheuserclicksanitem'scheckboxintheview.

TheTitlepropertyisastring(textvalue).Thiswillholdthenameordescriptionoftheto-doitem.The[Required]attributetellsASP.NETCorethatthisstringcan'tbenullorempty.

TheDueAtpropertyisaDateTimeOffset,whichisaC#typethatstoresadate/timestampalongwithatimezoneoffsetfromUTC.Storingthedate,time,andtimezoneoffsettogethermakesiteasytorenderdatesaccuratelyonsystemsindifferenttimezones.

Noticethe?questionmarkaftertheDateTimeOffsettype?ThatmarkstheDueAtpropertyasnullable,oroptional.Ifthe?wasn'tincluded,everyto-doitemwouldneedtohaveaduedate.TheIdandIsDonepropertiesaren'tmarkedasnullable,sotheyarerequiredandwillalwayshaveavalue(oradefaultvalue).

StringsinC#arealwaysnullable,sothere'snoneedtomarktheTitlepropertyasnullable.C#stringscanbenull,empty,orcontaintext.

Createmodels

25

Page 26: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Eachpropertyisfollowedbyget;set;,whichisashorthandwayofsayingthepropertyisread/write(or,moretechnically,ithasagetterandsettermethods).

Atthispoint,itdoesn'tmatterwhattheunderlyingdatabasetechnologyis.ItcouldbeSQLServer,MySQL,MongoDB,Redis,orsomethingmoreexotic.ThismodeldefineswhatthedatabaseroworentrywilllooklikeinC#soyoudon'thavetoworryaboutthelow-leveldatabasestuffinyourcode.Thissimplestyleofmodelissometimescalleda"plainoldC#object"orPOCO.

Theviewmodel

Often,themodel(entity)youstoreinthedatabaseissimilarbutnotexactlythesameasthemodelyouwanttouseinMVC(theviewmodel).Inthiscase,theTodoItemmodelrepresentsasingleiteminthedatabase,buttheviewmightneedtodisplaytwo,ten,orahundredto-doitems(dependingonhowbadlytheuserisprocrastinating).

Becauseofthis,theviewmodelshouldbeaseparateclassthatholdsanarrayofTodoItems:

Models/TodoViewModel.cs

namespaceAspNetCoreTodo.Models

{

publicclassTodoViewModel

{

publicTodoItem[]Items{get;set;}

}

}

Nowthatyouhavesomemodels,it'stimetocreateaviewthatwilltakeaTodoViewModelandrendertherightHTMLtoshowtheusertheirto-dolist.

Createmodels

26

Page 27: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Createmodels

27

Page 28: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

CreateaviewViewsinASP.NETCorearebuiltusingtheRazortemplatinglanguage,whichcombinesHTMLandC#code.(Ifyou'vewrittenpagesusingHandlebarsmoustaches,ERBinRubyonRails,orThymeleafinJava,you'vealreadygotthebasicidea.)

MostviewcodeisjustHTML,withtheoccasionalC#statementaddedintopulldataoutoftheviewmodelandturnitintotextorHTML.TheC#statementsareprefixedwiththe@symbol.

TheviewrenderedbytheIndexactionoftheTodoControllerneedstotakethedataintheviewmodel(asequenceofto-doitems)anddisplayitinanicetablefortheuser.Byconvention,viewsareplacedintheViewsdirectory,inasubdirectorycorrespondingtothecontrollername.Thefilenameoftheviewisthenameoftheactionwitha.cshtmlextension.

CreateaTododirectoryinsidetheViewsdirectory,andaddthisfile:

Views/Todo/Index.cshtml

@modelTodoViewModel

@{

ViewData["Title"]="Manageyourtodolist";

}

<divclass="panelpanel-defaulttodo-panel">

<divclass="panel-heading">@ViewData["Title"]</div>

<tableclass="tabletable-hover">

<thead>

<tr>

<td>&#x2714;</td>

<td>Item</td>

<td>Due</td>

Createaview

28

Page 29: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

</tr>

</thead>

@foreach(variteminModel.Items)

{

<tr>

<td>

<inputtype="checkbox"class="done-checkbox">

</td>

<td>@item.Title</td>

<td>@item.DueAt</td>

</tr>

}

</table>

<divclass="panel-footeradd-item-form">

<!--TODO:Additemform-->

</div>

</div>

Attheverytopofthefile,the@modeldirectivetellsRazorwhichmodeltoexpectthisviewtobeboundto.ThemodelisaccessedthroughtheModelproperty.

Assumingthereareanyto-doitemsinModel.Items,theforeachstatementwillloopovereachto-doitemandrenderatablerow(<tr>element)containingtheitem'snameandduedate.Acheckboxisalsorenderedthatwilllettheusermarktheitemascomplete.

Thelayoutfile

YoumightbewonderingwheretherestoftheHTMLis:whataboutthe<body>tag,ortheheaderandfooterofthepage?ASP.NETCoreusesalayoutviewthatdefinesthebasestructurethateveryotherviewisrenderedinsideof.It'sstoredinViews/Shared/_Layout.cshtml.

Createaview

29

Page 30: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

ThedefaultASP.NETCoretemplateincludesBootstrapandjQueryinthislayoutfile,soyoucanquicklycreateawebapplication.Ofcourse,youcanuseyourownCSSandJavaScriptlibrariesifyou'dlike.

Customizingthestylesheet

ThedefaulttemplatealsoincludesastylesheetwithsomebasicCSSrules.Thestylesheetisstoredinthewwwroot/cssdirectory.AddafewnewCSSstylerulestothebottomofthesite.cssfile:

wwwroot/css/site.css

div.todo-panel{

margin-top:15px;

}

tabletr.done{

text-decoration:line-through;

color:#888;

}

YoucanuseCSSruleslikethesetocompletelycustomizehowyourpageslookandfeel.

ASP.NETCoreandRazorcandomuchmore,suchaspartialviewsandserver-renderedviewcomponents,butasimplelayoutandviewisallyouneedfornow.TheofficialASP.NETCoredocumentation(athttps://docs.asp.net)containsanumberofexamplesifyou'dliketolearnmore.

Createaview

30

Page 31: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

AddaserviceclassYou'vecreatedamodel,aview,andacontroller.Beforeyouusethemodelandviewinthecontroller,youalsoneedtowritecodethatwillgettheuser'sto-doitemsfromadatabase.

Youcouldwritethisdatabasecodedirectlyinthecontroller,butit'sabetterpracticetokeepyourcodeseparate.Why?Inabig,real-worldapplication,you'llhavetojugglemanyconcerns:

Renderingviewsandhandlingincomingdata:thisiswhatyourcontrolleralreadydoes.Performingbusinesslogic,orcodeandlogicthat'srelatedtothepurposeand"business"ofyourapplication.Inato-dolistapplication,businesslogicmeansdecisionslikesettingadefaultduedateonnewtasks,oronlydisplayingtasksthatareincomplete.Otherexamplesofbusinesslogicincludecalculatingatotalcostbasedonproductpricesandtaxrates,orcheckingwhetheraplayerhasenoughpointstolevelupinagame.Savingandretrievingitemsfromadatabase.

Again,it'spossibletodoallofthesethingsinasingle,massivecontroller,butthatquicklybecomestoohardtomanageandtest.Instead,it'scommontoseeapplicationssplitupintotwo,three,ormore"layers"ortiersthateachhandleone(andonlyone)concern.Thishelpskeepthecontrollersassimpleaspossible,andmakesiteasiertotestandchangethebusinesslogicanddatabasecodelater.

Separatingyourapplicationthiswayissometimescalledamulti-tierorn-tierarchitecture.Insomecases,thetiers(layers)areisolatedincompletelyseparateprojects,butothertimesitjustreferstohowthe

Addaserviceclass

31

Page 32: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

classesareorganizedandused.Theimportantthingisthinkingabouthowtosplityourapplicationintomanageablepieces,andavoidhavingcontrollersorbloatedclassesthattrytodoeverything.

Forthisproject,you'llusetwoapplicationlayers:apresentationlayermadeupofthecontrollersandviewsthatinteractwiththeuser,andaservicelayerthatcontainsbusinesslogicanddatabasecode.Thepresentationlayeralreadyexists,sothenextstepistobuildaservicethathandlesto-dobusinesslogicandsavesto-doitemstoadatabase.

Mostlargerprojectsusea3-tierarchitecture:apresentationlayer,aservicelogiclayer,andadatarepositorylayer.Arepositoryisaclassthat'sonlyfocusedondatabasecode(nobusinesslogic).Inthisapplication,you'llcombinetheseintoasingleservicelayerforsimplicity,butfeelfreetoexperimentwithdifferentwaysofarchitectingthecode.

Createaninterface

TheC#languageincludestheconceptofinterfaces,wherethedefinitionofanobject'smethodsandpropertiesisseparatefromtheclassthatactuallycontainsthecodeforthosemethodsandproperties.Interfacesmakeiteasytokeepyourclassesdecoupledandeasytotest,asyou'llseehere(andlaterintheAutomatedtestingchapter).You'lluseaninterfacetorepresenttheservicethatcaninteractwithto-doitemsinthedatabase.

Byconvention,interfacesareprefixedwith"I".CreateanewfileintheServicesdirectory:

Services/ITodoItemService.cs

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Threading.Tasks;

usingAspNetCoreTodo.Models;

Addaserviceclass

32

Page 33: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

namespaceAspNetCoreTodo.Services

{

publicinterfaceITodoItemService

{

Task<TodoItem[]>GetIncompleteItemsAsync();

}

}

NotethatthenamespaceofthisfileisAspNetCoreTodo.Services.Namespacesareawaytoorganize.NETcodefiles,andit'scustomaryforthenamespacetofollowthedirectorythefileisstoredin(AspNetCoreTodo.ServicesforfilesintheServicesdirectory,andsoon).

Becausethisfile(intheAspNetCoreTodo.Servicesnamespace)referencestheTodoItemclass(intheAspNetCoreTodo.Modelsnamespace),itneedstoincludeausingstatementatthetopofthefiletoimportthatnamespace.Withouttheusingstatement,you'llseeanerrorlike:

Thetypeornamespacename'TodoItem'couldnotbefound(areyou

missingausingdirectiveoranassemblyreference?)

Sincethisisaninterface,thereisn'tanyactualcodehere,justthedefinition(ormethodsignature)oftheGetIncompleteItemsAsyncmethod.ThismethodrequiresnoparametersandreturnsaTask<TodoItem[]>.

Ifthissyntaxlooksconfusing,think:"aTaskthatcontainsanarrayofTodoItems".

TheTasktypeissimilartoafutureorapromise,andit'susedherebecausethismethodwillbeasynchronous.Inotherwords,themethodmaynotbeabletoreturnthelistofto-doitemsrightawaybecauseitneedstogotalktothedatabasefirst.(Moreonthislater.)

Createtheserviceclass

Addaserviceclass

33

Page 34: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Nowthattheinterfaceisdefined,you'rereadytocreatetheactualserviceclass.I'llcoverdatabasecodeindepthintheUseadatabasechapter,sofornowyou'lljustfakeitandalwaysreturntwohard-codeditems:

Services/FakeTodoItemService.cs

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Threading.Tasks;

usingAspNetCoreTodo.Models;

namespaceAspNetCoreTodo.Services

{

publicclassFakeTodoItemService:ITodoItemService

{

publicTask<TodoItem[]>GetIncompleteItemsAsync()

{

varitem1=newTodoItem

{

Title="LearnASP.NETCore",

DueAt=DateTimeOffset.Now.AddDays(1)

};

varitem2=newTodoItem

{

Title="Buildawesomeapps",

DueAt=DateTimeOffset.Now.AddDays(2)

};

returnTask.FromResult(new[]{item1,item2});

}

}

}

ThisFakeTodoItemServiceimplementstheITodoItemServiceinterfacebutalwaysreturnsthesamearrayoftwoTodoItems.You'llusethistotestthecontrollerandview,andthenaddrealdatabasecodeinUseadatabase.

Addaserviceclass

34

Page 35: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Addaserviceclass

35

Page 36: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

UsedependencyinjectionBackintheTodoController,addsomecodetoworkwiththeITodoItemService:

publicclassTodoController:Controller

{

privatereadonlyITodoItemService_todoItemService;

publicTodoController(ITodoItemServicetodoItemService)

{

_todoItemService=todoItemService;

}

publicIActionResultIndex()

{

//Getto-doitemsfromdatabase

//Putitemsintoamodel

//Passtheviewtoamodelandrender

}

}

SinceITodoItemServiceisintheServicesnamespace,you'llalsoneedtoaddausingstatementatthetop:

usingAspNetCoreTodo.Services;

ThefirstlineoftheclassdeclaresaprivatevariabletoholdareferencetotheITodoItemService.ThisvariableletsyouusetheservicefromtheIndexactionmethodlater(you'llseehowinaminute).

ThepublicTodoController(ITodoItemServicetodoItemService)linedefinesaconstructorfortheclass.Theconstructorisaspecialmethodthatiscalledwhenyouwanttocreateanewinstanceofaclass(the

Usedependencyinjection

36

Page 37: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

TodoControllerclass,inthiscase).ByaddinganITodoItemServiceparametertotheconstructor,you'vedeclaredthatinordertocreatetheTodoController,you'llneedtoprovideanobjectthatmatchestheITodoItemServiceinterface.

Interfacesareawesomebecausetheyhelpdecouple(separate)thelogicofyourapplication.SincethecontrollerdependsontheITodoItemServiceinterface,andnotonanyspecificclass,itdoesn'tknoworcarewhichclassit'sactuallygiven.ItcouldbetheFakeTodoItemService,adifferentonethattalkstoalivedatabase,orsomethingelse!Aslongasitmatchestheinterface,thecontrollercanuseit.Thismakesitreallyeasytotestpartsofyourapplicationseparately.I'llcovertestingindetailintheAutomatedtestingchapter.

NowyoucanfinallyusetheITodoItemService(viatheprivatevariableyoudeclared)inyouractionmethodtogetto-doitemsfromtheservicelayer:

publicIActionResultIndex()

{

varitems=await_todoItemService.GetIncompleteItemsAsync();

//...

}

RememberthattheGetIncompleteItemsAsyncmethodreturnedaTask<TodoItem[]>?ReturningaTaskmeansthatthemethodwon'tnecessarilyhavearesultrightaway,butyoucanusetheawaitkeywordtomakesureyourcodewaitsuntiltheresultisreadybeforecontinuingon.

TheTaskpatterniscommonwhenyourcodecallsouttoadatabaseoranAPIservice,becauseitwon'tbeabletoreturnarealresultuntilthedatabase(ornetwork)responds.Ifyou'veusedpromisesorcallbacksin

Usedependencyinjection

37

Page 38: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

JavaScriptorotherlanguages,Taskisthesameidea:thepromisethattherewillbearesult-sometimeinthefuture.

Ifyou'vehadtodealwith"callbackhell"inolderJavaScriptcode,you'reinluck.Dealingwithasynchronouscodein.NETismucheasierthankstothemagicoftheawaitkeyword!awaitletsyourcodepauseonanasyncoperation,andthenpickupwhereitleftoffwhentheunderlyingdatabaseornetworkrequestfinishes.Inthemeantime,yourapplicationisn'tblocked,becauseitcanprocessotherrequestsasneeded.Thispatternissimplebuttakesalittlegettingusedto,sodon'tworryifthisdoesn'tmakesenserightaway.Justkeepfollowingalong!

TheonlycatchisthatyouneedtoupdatetheIndexmethodsignaturetoreturnaTask<IActionResult>insteadofjustIActionResult,andmarkitasasync:

publicasyncTask<IActionResult>Index()

{

varitems=await_todoItemService.GetIncompleteItemsAsync();

//Putitemsintoamodel

//Passtheviewtoamodelandrender

}

You'realmostthere!You'vemadetheTodoControllerdependontheITodoItemServiceinterface,butyouhaven'tyettoldASP.NETCorethatyouwanttheFakeTodoItemServicetobetheactualservicethat'susedunderthehood.ItmightseemobviousrightnowsinceyouonlyhaveoneclassthatimplementsITodoItemService,butlateryou'llhavemultipleclassesthatimplementthesameinterface,sobeingexplicitisnecessary.

Usedependencyinjection

38

Page 39: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Declaring(or"wiringup")whichconcreteclasstouseforeachinterfaceisdoneintheConfigureServicesmethodoftheStartupclass.Rightnow,itlookssomethinglikethis:

Startup.cs

publicvoidConfigureServices(IServiceCollectionservices)

{

//(...somecode)

services.AddMvc();

}

ThejoboftheConfigureServicesmethodisaddingthingstotheservicecontainer,orthecollectionofservicesthatASP.NETCoreknowsabout.Theservices.AddMvclineaddstheservicesthattheinternalASP.NETCoresystemsneed(asanexperiment,trycommentingoutthisline).AnyotherservicesyouwanttouseinyourapplicationmustbeaddedtotheservicecontainerhereinConfigureServices.

AddthefollowinglineanywhereinsidetheConfigureServicesmethod:

services.AddSingleton<ITodoItemService,FakeTodoItemService>();

ThislinetellsASP.NETCoretousetheFakeTodoItemServicewhenevertheITodoItemServiceinterfaceisrequestedinaconstructor(oranywhereelse).

AddSingletonaddsyourservicetotheservicecontainerasasingleton.ThismeansthatonlyonecopyoftheFakeTodoItemServiceiscreated,andit'sreusedwhenevertheserviceisrequested.Later,whenyouwriteadifferentserviceclassthattalkstoadatabase,you'lluseadifferentapproach(calledscoped)instead.I'llexplainwhyintheUseadatabasechapter.

Usedependencyinjection

39

Page 40: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

That'sit!WhenarequestcomesinandisroutedtotheTodoController,ASP.NETCorewilllookattheavailableservicesandautomaticallysupplytheFakeTodoItemServicewhenthecontrollerasksforanITodoItemService.Becausetheservicesare"injected"fromtheservicecontainer,thispatterniscalleddependencyinjection.

Usedependencyinjection

40

Page 41: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

FinishthecontrollerThelaststepistofinishthecontrollercode.Thecontrollernowhasalistofto-doitemsfromtheservicelayer,anditneedstoputthoseitemsintoaTodoViewModelandbindthatmodeltotheviewyoucreatedearlier:

Controllers/TodoController.cs

publicasyncTask<IActionResult>Index()

{

varitems=await_todoItemService.GetIncompleteItemsAsync();

varmodel=newTodoViewModel()

{

Items=items

};

returnView(model);

}

Ifyouhaven'talready,makesuretheseusingstatementsareatthetopofthefile:

usingAspNetCoreTodo.Services;

usingAspNetCoreTodo.Models;

Ifyou'reusingVisualStudioorVisualStudioCode,theeditorwillsuggesttheseusingstatementswhenyouputyourcursoronaredsquigglyline.

Testitout

Finishthecontroller

41

Page 42: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Tostarttheapplication,pressF5(ifyou'reusingVisualStudioorVisualStudioCode),orjusttypedotnetrunintheterminal.Ifthecodecompileswithouterrors,theserverwillstartuponport5000bydefault.

Ifyourwebbrowserdidn'topenautomatically,openitandnavigatetohttp://localhost:5000/todo.You'llseetheviewyoucreated,withthedatapulledfromyourfakedatabase(fornow).

Althoughit'spossibletogodirectlytohttp://localhost:5000/todo,itwouldbenicertoaddanitemcalledMyto-dostothenavbar.Todothis,youcaneditthesharedlayoutfile.

Finishthecontroller

42

Page 43: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

UpdatethelayoutThelayoutfileatViews/Shared/_Layout.cshtmlcontainsthe"base"HTMLforeachview.Thisincludesthenavbar,whichisrenderedatthetopofeachpage.

Toaddanewitemtothenavbar,findtheHTMLcodefortheexistingnavbaritems:

Views/Shared/_Layout.cshtml

<ulclass="navnavbar-nav">

<li><aasp-area=""asp-controller="Home"asp-action="Index">

Home

</a></li>

<li><aasp-area=""asp-controller="Home"asp-action="About">

About

</a></li>

<li><aasp-area=""asp-controller="Home"asp-action="Contact">

Contact

</a></li>

</ul>

AddyourownitemthatpointstotheTodocontrollerinsteadofHome:

<li>

<aasp-controller="Todo"asp-action="Index">Myto-dos</a>

</li>

Theasp-controllerandasp-actionattributesonthe<a>elementarecalledtaghelpers.Beforetheviewisrendered,ASP.NETCorereplacesthesetaghelperswithrealHTMLattributes.Inthiscase,aURLtothe/Todo/Indexrouteisgeneratedandaddedtothe<a>element

Updatethelayout

43

Page 44: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

asanhrefattribute.Thismeansyoudon'thavetohard-codetheroutetotheTodoController.Instead,ASP.NETCoregeneratesitforyouautomatically.

Ifyou'veusedRazorinASP.NET4.x,you'[email protected]()togeneratealinktoanaction,taghelpersarenowtherecommendedwaytocreatelinksinyourviews.Taghelpersareusefulforforms,too(you'llseewhyinalaterchapter).Youcanlearnaboutothertaghelpersinthedocumentationathttps://docs.asp.net.

Updatethelayout

44

Page 45: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

AddexternalpackagesOneofthebigadvantagesofusingamatureecosystemlike.NETisthatthenumberofthird-partypackagesandpluginsishuge.Justlikeotherpackagesystems,youcandownloadandinstall.NETpackagesthathelpwithalmostanytaskorproblemyoucanimagine.

NuGetisboththepackagemanagertoolandtheofficialpackagerepository(athttps://www.nuget.org).YoucansearchforNuGetpackagesontheweb,andinstallthemfromyourlocalmachinethroughtheterminal(ortheGUI,ifyou'reusingVisualStudio).

InstalltheHumanizerpackageAttheendofthelastchapter,theto-doapplicationdisplayedto-doitemslikethis:

Theduedatecolumnisdisplayingdatesinaformatthat'sgoodformachines(calledISO8601),butclunkyforhumans.Wouldn'titbenicerifitsimplyread"Xdaysfromnow"?

YoucouldwritecodeyourselfthatconvertedanISO8601dateintoahuman-friendlystring,butfortunately,there'safasterway.

TheHumanizerpackageonNuGetsolvesthisproblembyprovidingmethodsthatcan"humanize"orrewritealmostanything:dates,times,durations,numbers,andsoon.It'safantasticandusefulopen-source

Addexternalpackages

45

Page 46: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

projectthat'spublishedunderthepermissiveMITlicense.

Toaddittoyourproject,runthiscommandintheterminal:

dotnetaddpackageHumanizer

IfyoupeekattheAspNetCoreTodo.csprojprojectfile,you'llseeanewPackageReferencelinethatreferencesHumanizer.

UseHumanizerintheviewTouseapackageinyourcode,youusuallyneedtoaddausingstatementthatimportsthepackageatthetopofthefile.

SinceHumanizerwillbeusedtorewritedatesrenderedintheview,youcanuseitdirectlyintheviewitself.First,adda@usingstatementatthetopoftheview:

Views/Todo/Index.cshtml

@modelTodoViewModel

@usingHumanizer

//...

Then,updatethelinethatwritestheDueAtpropertytouseHumanizer'sHumanizemethod:

<td>@item.DueAt.Humanize()</td>

Nowthedatesaremuchmorereadable:

Addexternalpackages

46

Page 47: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

TherearepackagesavailableonNuGetforeverythingfromparsingXMLtomachinelearningtopostingtoTwitter.ASP.NETCoreitself,underthehood,isnothingmorethanacollectionofNuGetpackagesthatareaddedtoyourproject.

TheprojectfilecreatedbydotnetnewmvcincludesasinglereferencetotheMicrosoft.AspNetCore.Allpackage,whichisaconvenient"metapackage"thatreferencesalloftheotherASP.NETCorepackagesyouneedforatypicalproject.Thatway,youdon'tneedtohavehundredsofpackagereferencesinyourprojectfile.

Inthenextchapter,you'lluseanothersetofNuGetpackages(asystemcalledEntityFrameworkCore)towritecodethatinteractswithadatabase.

Addexternalpackages

47

Page 48: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

UseadatabaseWritingdatabasecodecanbetricky.Unlessyoureallyknowwhatyou'redoing,it'sabadideatopasterawSQLquerystringsintoyourapplicationcode.Anobject-relationalmapper(ORM)makesiteasiertowritecodethatinteractswithadatabasebyaddingalayerofabstractionbetweenyourcodeandthedatabaseitself.HibernateinJavaandActiveRecordinRubyaretwowell-knownORMs.

ThereareanumberofORMsfor.NET,includingonebuiltbyMicrosoftandincludedinASP.NETCorebydefault:EntityFrameworkCore.EntityFrameworkCoremakesiteasytoconnecttoanumberofdifferentdatabasetypes,andletsyouuseC#codetocreatedatabasequeriesthataremappedbackintoC#models(POCOs).

Rememberhowcreatingaserviceinterfacedecoupledthecontrollercodefromtheactualserviceclass?EntityFrameworkCoreislikeabiginterfaceoveryourdatabase.YourC#codecanstaydatabase-agnostic,andyoucanswapoutdifferentprovidersdependingontheunderlyingdatabasetechnology.

EntityFrameworkCorecanconnecttorelationaldatabaseslikeSQLServer,PostgreSQL,andMySQL,andalsoworkswithNoSQL(document)databaseslikeMongo.Duringdevelopment,you'lluseSQLiteinthisprojecttomakethingseasytosetup.

Useadatabase

48

Page 49: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

ConnecttoadatabaseThereareafewthingsyouneedtouseEntityFrameworkCoretoconnecttoadatabase.SinceyouuseddotnetnewandtheMVC+IndividualAuthtemplatetosetyourproject,you'vealreadygotthem:

TheEntityFrameworkCorepackages.TheseareincludedbydefaultinallASP.NETCoreprojects.

Adatabase(naturally).Theapp.dbfileintheprojectrootdirectoryisasmallSQLitedatabasecreatedforyoubydotnetnew.SQLiteisalightweightdatabaseenginethatcanrunwithoutrequiringyoutoinstallanyextratoolsonyourmachine,soit'seasyandquicktouseindevelopment.

Adatabasecontextclass.ThedatabasecontextisaC#classthatprovidesanentrypointintothedatabase.It'showyourcodewillinteractwiththedatabasetoreadandsaveitems.AbasiccontextclassalreadyexistsintheData/ApplicationDbContext.csfile.

Aconnectionstring.Whetheryouareconnectingtoalocalfiledatabase(likeSQLite)oradatabasehostedelsewhere,you'lldefineastringthatcontainsthenameoraddressofthedatabasetoconnectto.Thisisalreadysetupforyouintheappsettings.jsonfile:theconnectionstringfortheSQLitedatabaseisDataSource=app.db.

EntityFrameworkCoreusesthedatabasecontext,togetherwiththeconnectionstring,toestablishaconnectiontothedatabase.YouneedtotellEntityFrameworkCorewhichcontext,connectionstring,anddatabaseprovidertouseintheConfigureServicesmethodoftheStartupclass.Here'swhat'sdefinedforyou,thankstothetemplate:

services.AddDbContext<ApplicationDbContext>(options=>

Connecttoadatabase

49

Page 50: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

options.UseSqlite(

Configuration.GetConnectionString("DefaultConnection")));

ThiscodeaddstheApplicationDbContexttotheservicecontainer,andtellsEntityFrameworkCoretousetheSQLitedatabaseprovider,withtheconnectionstringfromconfiguration(appsettings.json).

Asyoucansee,dotnetnewcreatesalotofstuffforyou!Thedatabaseissetupandreadytobeused.However,itdoesn'thaveanytablesforstoringto-doitems.InordertostoreyourTodoItementities,you'llneedtoupdatethecontextandmigratethedatabase.

Connecttoadatabase

50

Page 51: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

UpdatethecontextThere'snotawholelotgoingoninthedatabasecontextyet:

Data/ApplicationDbContext.cs

publicclassApplicationDbContext

:IdentityDbContext<ApplicationUser>

{

publicApplicationDbContext(

DbContextOptions<ApplicationDbContext>options)

:base(options)

{

}

protectedoverridevoidOnModelCreating(ModelBuilderbuilder)

{

base.OnModelCreating(builder);

//...

}

}

AddaDbSetpropertytotheApplicationDbContext,rightbelowtheconstructor:

publicApplicationDbContext(

DbContextOptions<ApplicationDbContext>options)

:base(options)

{

}

publicDbSet<TodoItem>Items{get;set;}

//...

Updatethecontext

51

Page 52: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

ADbSetrepresentsatableorcollectioninthedatabase.BycreatingaDbSet<TodoItem>propertycalledItems,you'retellingEntityFrameworkCorethatyouwanttostoreTodoItementitiesinatablecalledItems.

You'veupdatedthecontextclass,butnowthere'sonesmallproblem:thecontextanddatabasearenowoutofsync,becausethereisn'tactuallyanItemstableinthedatabase.(Justupdatingthecodeofthecontextclassdoesn'tchangethedatabaseitself.)

Inordertoupdatethedatabasetoreflectthechangeyoujustmadetothecontext,youneedtocreateamigration.

Ifyoualreadyhaveanexistingdatabase,searchthewebfor"scaffold-dbcontextexistingdatabase"andreadMicrosoft'sdocumentationonusingtheScaffold-DbContexttooltoreverse-engineeryourdatabasestructureintotheproperDbContextandmodelclassesautomatically.

Updatethecontext

52

Page 53: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

CreateamigrationMigrationskeeptrackofchangestothedatabasestructureovertime.Theymakeitpossibletoundo(rollback)asetofchanges,orcreateaseconddatabasewiththesamestructureasthefirst.Withmigrations,youhaveafullhistoryofmodificationslikeaddingorremovingcolumns(andentiretables).

Inthepreviouschapter,youaddedanItemssettothecontext.Sincethecontextnowincludesaset(ortable)thatdoesn'texistinthedatabase,youneedtocreateamigrationtoupdatethedatabase:

dotnetefmigrationsaddAddItems

ThiscreatesanewmigrationcalledAddItemsbyexamininganychangesyou'vemadetothecontext.

IfyougetanerrorlikeNoexecutablefoundmatchingcommand"dotnet-ef",makesureyou'reintherightdirectory.Thesecommandsmustberunfromtheprojectrootdirectory(wheretheProgram.csfileis).

IfyouopenuptheData/Migrationsdirectory,you'llseeafewfiles:

Createamigration

53

Page 54: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Thefirstmigrationfile(withanamelike00_CreateIdentitySchema.cs)wascreatedandappliedforyouwaybackwhenyourandotnetnew.YournewAddItemmigrationisprefixedwithatimestampwhenyoucreateit.

Youcanseealistofmigrationswithdotnetefmigrationslist.

Ifyouopenyourmigrationfile,you'llseetwomethodscalledUpandDown:

Data/Migrations/_AddItems.cs

protectedoverridevoidUp(MigrationBuildermigrationBuilder)

{

//(...somecode)

migrationBuilder.CreateTable(

name:"Items",

columns:table=>new

{

Id=table.Column<Guid>(nullable:false),

DueAt=table.Column<DateTimeOffset>(nullable:true),

IsDone=table.Column<bool>(nullable:false),

Title=table.Column<string>(nullable:true)

},

constraints:table=>

{

table.PrimaryKey("PK_Items",x=>x.Id);

});

//(somecode...)

}

protectedoverridevoidDown(MigrationBuildermigrationBuilder)

{

//(...somecode)

migrationBuilder.DropTable(

name:"Items");

//(somecode...)

}

Createamigration

54

Page 55: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

TheUpmethodrunswhenyouapplythemigrationtothedatabase.SinceyouaddedaDbSet<TodoItem>tothedatabasecontext,EntityFrameworkCorewillcreateanItemstable(withcolumnsthatmatchaTodoItem)whenyouapplythemigration.

TheDownmethoddoestheopposite:ifyouneedtoundo(rollback)themigration,theItemstablewillbedropped.

WorkaroundforSQLitelimitations

TherearesomelimitationsofSQLitethatgetinthewayifyoutrytorunthemigrationas-is.Untilthisproblemisfixed,usethisworkaround:

CommentoutorremovethemigrationBuilder.AddForeignKeylinesintheUpmethod.CommentoutorremoveanymigrationBuilder.DropForeignKeylinesintheDownmethod.

Ifyouuseafull-fledgedSQLdatabase,likeSQLServerorMySQL,thiswon'tbeanissueandyouwon'tneedtodothis(admittedlyhackish)workaround.

Applythemigration

Thefinalstepaftercreatingone(ormore)migrationsistoactuallyapplythemtothedatabase:

dotnetefdatabaseupdate

ThiscommandwillcauseEntityFrameworkCoretocreatetheItemstableinthedatabase.

Createamigration

55

Page 56: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Ifyouwanttorollbackthedatabase,youcanprovidethenameofthepreviousmigration:dotnetefdatabaseupdateCreateIdentitySchemaThiswillruntheDownmethodsofanymigrationsnewerthanthemigrationyouspecify.

Ifyouneedtocompletelyerasethedatabaseandstartover,rundotnetefdatabasedropfollowedbydotnetefdatabaseupdatetore-scaffoldthedatabaseandbringituptothecurrentmigration.

That'sit!Boththedatabaseandthecontextarereadytogo.Next,you'llusethecontextinyourservicelayer.

Createamigration

56

Page 57: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

CreateanewserviceclassBackintheMVCbasicschapter,youcreatedaFakeTodoItemServicethatcontainedhard-codedto-doitems.Nowthatyouhaveadatabasecontext,youcancreateanewserviceclassthatwilluseEntityFrameworkCoretogettherealitemsfromthedatabase.

DeletetheFakeTodoItemService.csfile,andcreateanewfile:

Services/TodoItemService.cs

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Threading.Tasks;

usingAspNetCoreTodo.Data;

usingAspNetCoreTodo.Models;

usingMicrosoft.EntityFrameworkCore;

namespaceAspNetCoreTodo.Services

{

publicclassTodoItemService:ITodoItemService

{

privatereadonlyApplicationDbContext_context;

publicTodoItemService(ApplicationDbContextcontext)

{

_context=context;

}

publicasyncTask<TodoItem[]>GetIncompleteItemsAsync()

{

returnawait_context.Items

.Where(x=>x.IsDone==false)

.ToArrayAsync();

}

}

}

Createanewserviceclass

57

Page 58: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

You'llnoticethesamedependencyinjectionpatternherethatyousawintheMVCbasicschapter,exceptthistimeit'stheApplicationDbContextthat'sgettinginjected.TheApplicationDbContextisalreadybeingaddedtotheservicecontainerintheConfigureServicesmethod,soit'savailableforinjectionhere.

Let'stakeacloserlookatthecodeoftheGetIncompleteItemsAsyncmethod.First,itusestheItemspropertyofthecontexttoaccessalltheto-doitemsintheDbSet:

varitems=await_context.Items

Then,theWheremethodisusedtofilteronlytheitemsthatarenotcomplete:

.Where(x=>x.IsDone==false)

TheWheremethodisafeatureofC#calledLINQ(languageintegratedquery),whichtakesinspirationfromfunctionalprogrammingandmakesiteasytoexpressdatabasequeriesincode.Underthehood,EntityFrameworkCoretranslatestheWheremethodintoastatementlikeSELECT*FROMItemsWHEREIsDone=0,oranequivalentquerydocumentinaNoSQLdatabase.

Finally,theToArrayAsyncmethodtellsEntityFrameworkCoretogetalltheentitiesthatmatchedthefilterandreturnthemasanarray.TheToArrayAsyncmethodisasynchronous(itreturnsaTask),soitmustbeawaitedtogetitsvalue.

Tomakethemethodalittleshorter,youcanremovetheintermediateitemsvariableandjustreturntheresultofthequerydirectly(whichdoesthesamething):

publicasyncTask<TodoItem[]>GetIncompleteItemsAsync()

Createanewserviceclass

58

Page 59: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

{

returnawait_context.Items

.Where(x=>x.IsDone==false)

.ToArrayAsync();

}

Updatetheservicecontainer

BecauseyoudeletedtheFakeTodoItemServiceclass,you'llneedtoupdatethelineinConfigureServicesthatiswiringuptheITodoItemServiceinterface:

services.AddScoped<ITodoItemService,TodoItemService>();

AddScopedaddsyourservicetotheservicecontainerusingthescopedlifecycle.ThismeansthatanewinstanceoftheTodoItemServiceclasswillbecreatedduringeachwebrequest.Thisisrequiredforserviceclassesthatinteractwithadatabase.

AddingaserviceclassthatinteractswithEntityFrameworkCore(andyourdatabase)withthesingletonlifecycle(orotherlifecycles)cancauseproblems,becauseofhowEntityFrameworkCoremanagesdatabaseconnectionsperrequestunderthehood.Toavoidthat,alwaysusethescopedlifecycleforservicesthatinteractwithEntityFrameworkCore.

TheTodoControllerthatdependsonaninjectedITodoItemServicewillbeblissfullyunawareofthechangeinservicesclasses,butunderthehoodit'llbeusingEntityFrameworkCoreandtalkingtoarealdatabase!

Testitout

Startuptheapplicationandnavigatetohttp://localhost:5000/todo.Thefakeitemsaregone,andyourapplicationismakingrealqueriestothedatabase.Theredoesn'thappentobeanysavedto-doitems,soit's

Createanewserviceclass

59

Page 60: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

blankfornow.

Inthenextchapter,you'lladdmorefeaturestotheapplication,startingwiththeabilitytocreatenewto-doitems.

Createanewserviceclass

60

Page 61: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

AddmorefeaturesNowthatyou'veconnectedtoadatabaseusingEntityFrameworkCore,you'rereadytoaddsomemorefeaturestotheapplication.First,you'llmakeitpossibletoaddnewto-doitemsusingaform.

Addmorefeatures

61

Page 62: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Addnewto-doitemsTheuserwilladdnewto-doitemswithasimpleformbelowthelist:

Addingthisfeaturerequiresafewsteps:

AddingaformtotheviewCreatinganewactiononthecontrollertohandletheformAddingcodetotheservicelayertoupdatethedatabase

Addaform

TheViews/Todo/Index.cshtmlviewhasaplaceholderfortheAddItemform:

<divclass="panel-footeradd-item-form">

<!--TODO:Additemform-->

</div>

Tokeepthingsseparateandorganized,you'llcreatetheformasapartialview.Apartialviewisasmallpieceofalargerviewthatlivesinaseparatefile.

CreateanAddItemPartial.cshtmlview:

Views/Todo/AddItemPartial.cshtml

Addnewto-doitems

62

Page 63: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

@modelTodoItem

<formasp-action="AddItem"method="POST">

<labelasp-for="Title">Addanewitem:</label>

<inputasp-for="Title">

<buttontype="submit">Add</button>

</form>

Theasp-actiontaghelpercangenerateaURLfortheform,justlikewhenyouuseitonan<a>element.Inthiscase,theasp-actionhelpergetsreplacedwiththerealpathtotheAddItemrouteyou'llcreate:

<formaction="/Todo/AddItem"method="POST">

Addinganasp-taghelpertothe<form>elementalsoaddsahiddenfieldtotheformcontainingaverificationtoken.Thisverificationtokencanbeusedtopreventcross-siterequestforgery(CSRF)attacks.You'llverifythetokenwhenyouwritetheaction.

Thattakescareofcreatingthepartialview.Now,referenceitfromthemainTodoview:

Views/Todo/Index.cshtml

<divclass="panel-footeradd-item-form">

@awaitHtml.PartialAsync("AddItemPartial",newTodoItem())

</div>

Addanaction

WhenauserclicksAddontheformyoujustcreated,theirbrowserwillconstructaPOSTrequestto/Todo/AddItemonyourapplication.Thatwon'tworkrightnow,becausethereisn'tanyactionthatcanhandlethe/Todo/AddItemroute.Ifyoutryitnow,ASP.NETCorewillreturna404NotFounderror.

Addnewto-doitems

63

Page 64: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

You'llneedtocreateanewactioncalledAddItemontheTodoController:

[ValidateAntiForgeryToken]

publicasyncTask<IActionResult>AddItem(TodoItemnewItem)

{

if(!ModelState.IsValid)

{

returnRedirectToAction("Index");

}

varsuccessful=await_todoItemService.AddItemAsync(newItem);

if(!successful)

{

returnBadRequest("Couldnotadditem.");

}

returnRedirectToAction("Index");

}

NoticehowthenewAddItemactionacceptsaTodoItemparameter?ThisisthesameTodoItemmodelyoucreatedintheMVCbasicschaptertostoreinformationaboutato-doitem.Whenit'susedhereasanactionparameter,ASP.NETCorewillautomaticallyperformaprocesscalledmodelbinding.

Modelbindinglooksatthedatainarequestandtriestointelligentlymatchtheincomingfieldswithpropertiesonthemodel.Inotherwords,whentheusersubmitsthisformandtheirbrowserPOSTstothisaction,ASP.NETCorewillgrabtheinformationfromtheformandplaceitinthenewItemvariable.

The[ValidateAntiForgeryToken]attributebeforetheactiontellsASP.NETCorethatitshouldlookfor(andverify)thehiddenverificationtokenthatwasaddedtotheformbytheasp-actiontaghelper.Thisisanimportantsecuritymeasuretopreventcross-siterequestforgery

Addnewto-doitems

64

Page 65: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

(CSRF)attacks,whereyouruserscouldbetrickedintosubmittingdatafromamalicioussite.Theverificationtokenensuresthatyourapplicationisactuallytheonethatrenderedandsubmittedtheform.

TakealookattheAddItemPartial.cshtmlviewoncemore.The@modelTodoItemlineatthetopofthefiletellsASP.NETCorethattheviewshouldexpecttobepairedwiththeTodoItemmodel.Thismakesitpossibletouseasp-for="Title"onthe<input>tagtoletASP.NETCoreknowthatthisinputelementisfortheTitleproperty.

Becauseofthe@modelline,thepartialviewwillexpecttobepassedaTodoItemobjectwhenit'srendered.PassingitanewTodoItemviaHtml.PartialAsyncinitializestheformwithanemptyitem.(Tryappending{Title="hello"}andseewhathappens!)

Duringmodelbinding,anymodelpropertiesthatcan'tbematchedupwithfieldsintherequestareignored.SincetheformonlyincludesaTitleinputelement,youcanexpectthattheotherpropertiesonTodoItem(theIsDoneflag,theDueAtdate)willbeemptyorcontaindefaultvalues.

InsteadofreusingtheTodoItemmodel,anotherapproachwouldbetocreateaseparatemodel(likeNewTodoItem)that'sonlyusedforthisactionandonlyhasthespecificproperties(Title)youneedforaddinganewto-doitem.Modelbindingisstillused,butthiswayyou'veseparatedthemodelthat'susedforstoringato-doiteminthedatabasefromthemodelthat'susedforbindingincomingrequestdata.Thisissometimescalledabindingmodeloradatatransferobject(DTO).Thispatterniscommoninlarger,morecomplexprojects.

Afterbindingtherequestdatatothemodel,ASP.NETCorealsoperformsmodelvalidation.Validationcheckswhetherthedataboundtothemodelfromtheincomingrequestmakessenseorisvalid.Youcan

Addnewto-doitems

65

Page 66: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

addattributestothemodeltotellASP.NETCorehowitshouldbevalidated.

The[Required]attributeontheTitlepropertytellsASP.NETCore'smodelvalidatortoconsiderthetitleinvalidifitismissingorblank.TakealookatthecodeoftheAddItemaction:thefirstblockcheckswhethertheModelState(themodelvalidationresult)isvalid.It'scustomarytodothisvalidationcheckrightatthebeginningoftheaction:

if(!ModelState.IsValid)

{

returnRedirectToAction("Index");

}

IftheModelStateisinvalidforanyreason,thebrowserwillberedirectedtothe/Todo/Indexroute,whichrefreshesthepage.

Next,thecontrollercallsintotheservicelayertodotheactualdatabaseoperationofsavingthenewto-doitem:

varsuccessful=await_todoItemService.AddItemAsync(newItem);

if(!successful)

{

returnBadRequest(new{error="Couldnotadditem."});

}

TheAddItemAsyncmethodwillreturntrueorfalsedependingonwhethertheitemwassuccessfullyaddedtothedatabase.Ifitfailsforsomereason,theactionwillreturnanHTTP400BadRequesterroralongwithanobjectthatcontainsanerrormessage.

Finally,ifeverythingcompletedwithouterrors,theactionredirectsthebrowsertothe/Todo/Indexroute,whichrefreshesthepageanddisplaysthenew,updatedlistofto-doitemstotheuser.

Addaservicemethod

Addnewto-doitems

66

Page 67: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Ifyou'reusingacodeeditorthatunderstandsC#,you'llseeredsquiggelylinesunderAddItemAsyncbecausethemethoddoesn'texistyet.

Asalaststep,youneedtoaddamethodtotheservicelayer.First,addittotheinterfacedefinitioninITodoItemService:

publicinterfaceITodoItemService

{

Task<TodoItem[]>GetIncompleteItemsAsync();

Task<bool>AddItemAsync(TodoItemnewItem);

}

Then,theactualimplementationinTodoItemService:

publicasyncTask<bool>AddItemAsync(TodoItemnewItem)

{

newItem.Id=Guid.NewGuid();

newItem.IsDone=false;

newItem.DueAt=DateTimeOffset.Now.AddDays(3);

_context.Items.Add(newItem);

varsaveResult=await_context.SaveChangesAsync();

returnsaveResult==1;

}

ThenewItem.TitlepropertyhasalreadybeensetbyASP.NETCore'smodelbinder,sothismethodonlyneedstoassignanIDandsetthedefaultvaluesfortheotherproperties.Then,thenewitemisaddedtothedatabasecontext.Itisn'tactuallysaveduntilyoucallSaveChangesAsync().Ifthesaveoperationwassuccessful,SaveChangesAsync()willreturn1.

Tryitout

Addnewto-doitems

67

Page 68: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Runtheapplicationandaddsomeitemstoyourto-dolistwiththeform.Sincetheitemsarebeingstoredinthedatabase,they'llstillbethereevenafteryoustopandstarttheapplicationagain.

Asanextrachallenge,tryaddingadatepickerusingHTMLandJavaScript,andlettheuserchoosean(optional)datefortheDueAtproperty.Then,usethatdateinsteadofalwaysmakingnewtasksthatareduein3days.

Addnewto-doitems

68

Page 69: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

CompleteitemswithacheckboxAddingitemstoyourto-dolistisgreat,buteventuallyyou'llneedtogetthingsdone,too.IntheViews/Todo/Index.cshtmlview,acheckboxisrenderedforeachto-doitem:

<inputtype="checkbox"class="done-checkbox">

Clickingthecheckboxdoesn'tdoanything(yet).Justlikethelastchapter,you'lladdthisbehaviorusingformsandactions.Inthiscase,you'llalsoneedatinybitofJavaScriptcode.

Addformelementstotheview

First,updatetheviewandwrapeachcheckboxwitha<form>element.Then,addahiddenelementcontainingtheitem'sID:

Views/Todo/Index.cshtml

<td>

<formasp-action="MarkDone"method="POST">

<inputtype="checkbox"class="done-checkbox">

<inputtype="hidden"name="id"value="@item.Id">

</form>

</td>

Whentheforeachlooprunsintheviewandprintsarowforeachto-doitem,acopyofthisformwillexistineachrow.Thehiddeninputcontainingtheto-doitem'sIDmakesitpossibleforyourcontrollercodetotellwhichboxwaschecked.(Withoutit,you'dbeabletotellthatsomeboxwaschecked,butnotwhichone.)

Completeitemswithacheckbox

69

Page 70: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Ifyourunyourapplicationrightnow,thecheckboxesstillwon'tdoanything,becausethere'snosubmitbuttontotellthebrowsertocreateaPOSTrequestwiththeform'sdata.Youcouldaddasubmitbuttonundereachcheckbox,butthatwouldbeasillyuserexperience.Ideally,clickingthecheckboxshouldautomaticallysubmittheform.YoucanachievethatbyaddingsomeJavaScript.

AddJavaScriptcode

Findthesite.jsfileinthewwwroot/jsdirectoryandaddthiscode:

wwwroot/js/site.js

$(document).ready(function(){

//WireupallofthecheckboxestorunmarkCompleted()

$('.done-checkbox').on('click',function(e){

markCompleted(e.target);

});

});

functionmarkCompleted(checkbox){

checkbox.disabled=true;

varrow=checkbox.closest('tr');

$(row).addClass('done');

varform=checkbox.closest('form');

form.submit();

}

ThiscodefirstusesjQuery(aJavaScripthelperlibrary)toattachsomecodetotheclickevenofallthecheckboxesonthepagewiththeCSSclassdone-checkbox.Whenacheckboxisclicked,themarkCompleted()functionisrun.

ThemarkCompleted()functiondoesafewthings:

Completeitemswithacheckbox

70

Page 71: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Addsthedisabledattributetothecheckboxsoitcan'tbeclickedagainAddsthedoneCSSclasstotheparentrowthatcontainsthecheckbox,whichchangesthewaytherowlooksbasedontheCSSrulesinstyle.cssSubmitstheform

Thattakescareoftheviewandfrontendcode.Nowit'stimetoaddanewaction!

Addanactiontothecontroller

Asyou'veprobablyguessed,youneedtoaddanactioncalledMarkDoneintheTodoController:

[ValidateAntiForgeryToken]

publicasyncTask<IActionResult>MarkDone(Guidid)

{

if(id==Guid.Empty)

{

returnRedirectToAction("Index");

}

varsuccessful=await_todoItemService.MarkDoneAsync(id);

if(!successful)

{

returnBadRequest("Couldnotmarkitemasdone.");

}

returnRedirectToAction("Index");

}

Let'sstepthrougheachlineofthisactionmethod.First,themethodacceptsaGuidparametercalledidinthemethodsignature.UnliketheAddItemaction,whichusedamodelandmodelbinding/validation,theidparameterisverysimple.Iftheincomingrequestdataincludesa

Completeitemswithacheckbox

71

Page 72: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

fieldcalledid,ASP.NETCorewilltrytoparseitasaguid.Thisworksbecausethehiddenelementyouaddedtothecheckboxformisnamedid.

Sinceyouaren'tusingmodelbinding,there'snoModelStatetocheckforvalidity.Instead,youcanchecktheguidvaluedirectlytomakesureit'svalid.Ifforsomereasontheidparameterintherequestwasmissingorcouldn'tbeparsedasaguid,idwillhaveavalueofGuid.Empty.Ifthat'sthecase,theactiontellsthebrowsertoredirectto/Todo/Indexandrefreshthepage.

Next,thecontrollerneedstocalltheservicelayertoupdatethedatabase.ThiswillbehandledbyanewmethodcalledMarkDoneAsyncontheITodoItemServiceinterface,whichwillreturntrueorfalsedependingonwhethertheupdatesucceeded:

varsuccessful=await_todoItemService.MarkDoneAsync(id);

if(!successful)

{

returnBadRequest("Couldnotmarkitemasdone.");

}

Finally,ifeverythinglooksgood,thebrowserisredirectedtothe/Todo/Indexactionandthepageisrefreshed.

Withtheviewandcontrollerupdated,allthat'sleftisaddingthemissingservicemethod.

Addaservicemethod

First,addMarkDoneAsynctotheinterfacedefinition:

Services/ITodoItemService.cs

Task<bool>MarkDoneAsync(Guidid);

Completeitemswithacheckbox

72

Page 73: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Then,addtheconcreteimplementationtotheTodoItemService:

Services/TodoItemService.cs

publicasyncTask<bool>MarkDoneAsync(Guidid)

{

varitem=await_context.Items

.Where(x=>x.Id==id)

.SingleOrDefaultAsync();

if(item==null)returnfalse;

item.IsDone=true;

varsaveResult=await_context.SaveChangesAsync();

returnsaveResult==1;//Oneentityshouldhavebeenupdated

}

ThismethodusesEntityFrameworkCoreandWhere()tofindanitembyIDinthedatabase.TheSingleOrDefaultAsync()methodwilleitherreturntheitemornullifitcouldn'tbefound.

Onceyou'resurethatitemisn'tnull,it'sasimplematterofsettingtheIsDoneproperty:

item.IsDone=true;

ChangingthepropertyonlyaffectsthelocalcopyoftheitemuntilSaveChangesAsync()iscalledtopersistthechangebacktothedatabase.SaveChangesAsync()returnsanumberthatindicateshowmanyentitieswereupdatedduringthesaveoperation.Inthiscase,it'lleitherbe1(theitemwasupdated)or0(somethingwentwrong).

Tryitout

Completeitemswithacheckbox

73

Page 74: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Runtheapplicationandtrycheckingsomeitemsoffthelist.Refreshthepageandthey'lldisappearcompletely,becauseoftheWhere()filterintheGetIncompleteItemsAsync()method.

Rightnow,theapplicationcontainsasingle,sharedto-dolist.It'dbeevenmoreusefulifitkepttrackofindividualto-dolistsforeachuser.Inthenextchapter,you'lladdloginandsecurityfeaturestotheproject.

Completeitemswithacheckbox

74

Page 75: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

SecurityandidentitySecurityisamajorconcernofanymodernwebapplicationorAPI.It'simportanttokeepyouruserorcustomerdatasafeandoutofthehandsofattackers.Thisisaverybroadtopic,involvingthingslike:

SanitizingdatainputtopreventSQLinjectionattacksPreventingcross-domain(CSRF)attacksinformsUsingHTTPS(connectionencryption)sodatacan'tbeinterceptedasittravelsovertheInternetGivingusersawaytosecurelysigninwithapasswordorothercredentialsDesigningpasswordreset,accountrecovery,andmulti-factorauthenticationflows

ASP.NETCorecanhelpmakeallofthiseasiertoimplement.Thefirsttwo(protectionagainstSQLinjectionandcross-domainattacks)arealreadybuilt-in,andyoucanaddafewlinesofcodetoenableHTTPSsupport.Thischapterwillmainlyfocusontheidentityaspectsofsecurity:handlinguseraccounts,authenticating(loggingin)youruserssecurely,andmakingauthorizationdecisionsoncetheyareauthenticated.

Authenticationandauthorizationaredistinctideasthatareoftenconfused.Authenticationdealswithwhetherauserisloggedin,whileauthorizationdealswithwhattheyareallowedtodoaftertheylogin.Youcanthinkofauthenticationasaskingthequestion,"DoIknowwhothisuseris?"Whileauthorizationasks,"DoesthisuserhavepermissiontodoX?"

Securityandidentity

75

Page 76: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

TheMVC+IndividualAuthenticationtemplateyouusedtoscaffoldtheprojectincludesanumberofclassesbuiltontopofASP.NETCoreIdentity,anauthenticationandidentitysystemthat'spartofASP.NETCore.Outofthebox,thisaddstheabilitytologinwithanemailandpassword.

WhatisASP.NETCoreIdentity?ASP.NETCoreIdentityistheidentitysystemthatshipswithASP.NETCore.LikeeverythingelseintheASP.NETCoreecosystem,it'sasetofNuGetpackagesthatcanbeinstalledinanyproject(andarealreadyincludedifyouusethedefaulttemplate).

ASP.NETCoreIdentitytakescareofstoringuseraccounts,hashingandstoringpasswords,andmanagingrolesforusers.Itsupportsemail/passwordlogin,multi-factorauthentication,socialloginwithproviderslikeGoogleandFacebook,aswellasconnectingtootherservicesusingprotocolslikeOAuth2.0andOpenIDConnect.

TheRegisterandLoginviewsthatshipwiththeMVC+IndividualAuthenticationtemplatealreadytakeadvantageofASP.NETCoreIdentity,andtheyalreadywork!Tryregisteringforanaccountandloggingin.

Securityandidentity

76

Page 77: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

RequireauthenticationOftenyou'llwanttorequiretheusertologinbeforetheycanaccesscertainpartsofyourapplication.Forexample,itmakessensetoshowthehomepagetoeveryone(whetheryou'reloggedinornot),butonlyshowyourto-dolistafteryou'veloggedin.

Youcanusethe[Authorize]attributeinASP.NETCoretorequirealogged-inuserforaparticularaction,oranentirecontroller.TorequireauthenticationforallactionsoftheTodoController,addtheattributeabovethefirstlineofthecontroller:

Controllers/TodoController.cs

[Authorize]

publicclassTodoController:Controller

{

//...

}

Addthisusingstatementatthetopofthefile:

usingMicrosoft.AspNetCore.Authorization;

Tryrunningtheapplicationandaccessing/todowithoutbeingloggedin.You'llberedirectedtotheloginpageautomatically.

The[Authorize]attributeisactuallydoinganauthenticationcheckhere,notanauthorizationcheck(despitethenameoftheattribute).Later,you'llusetheattributetocheckbothauthenticationandauthorization.

Requireauthentication

77

Page 78: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Requireauthentication

78

Page 79: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

UsingidentityintheapplicationTheto-dolistitemsthemselvesarestillsharedbetweenallusers,becausethestoredto-doentitiesaren'ttiedtoaparticularuser.Nowthatthe[Authorize]attributeensuresthatyoumustbeloggedintoseetheto-doview,youcanfilterthedatabasequerybasedonwhoisloggedin.

First,injectaUserManager<ApplicationUser>intotheTodoController:

Controllers/TodoController.cs

[Authorize]

publicclassTodoController:Controller

{

privatereadonlyITodoItemService_todoItemService;

privatereadonlyUserManager<ApplicationUser>_userManager;

publicTodoController(ITodoItemServicetodoItemService,

UserManager<ApplicationUser>userManager)

{

_todoItemService=todoItemService;

_userManager=userManager;

}

//...

}

You'llneedtoaddanewusingstatementatthetop:

usingMicrosoft.AspNetCore.Identity;

TheUserManagerclassispartofASP.NETCoreIdentity.YoucanuseittogetthecurrentuserintheIndexaction:

publicasyncTask<IActionResult>Index()

Usingidentityintheapplication

79

Page 80: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

{

varcurrentUser=await_userManager.GetUserAsync(User);

if(currentUser==null)returnChallenge();

varitems=await_todoItemService

.GetIncompleteItemsAsync(currentUser);

varmodel=newTodoViewModel()

{

Items=items

};

returnView(model);

}

ThenewcodeatthetopoftheactionmethodusestheUserManagertolookupthecurrentuserfromtheUserpropertyavailableintheaction:

varcurrentUser=await_userManager.GetUserAsync(User);

Ifthereisalogged-inuser,theUserpropertycontainsalightweightobjectwithsome(butnotall)oftheuser'sinformation.TheUserManagerusesthistolookupthefulluserdetailsinthedatabaseviatheGetUserAsync()method.

ThevalueofcurrentUsershouldneverbenull,becausethe[Authorize]attributeispresentonthecontroller.However,it'sagoodideatodoasanitycheck,justincase.YoucanusetheChallenge()methodtoforcetheusertologinagainiftheirinformationismissing:

if(currentUser==null)returnChallenge();

Sinceyou'renowpassinganApplicationUserparametertoGetIncompleteItemsAsync(),you'llneedtoupdatetheITodoItemServiceinterface:

Services/ITodoItemService.cs

Usingidentityintheapplication

80

Page 81: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

publicinterfaceITodoItemService

{

Task<TodoItem[]>GetIncompleteItemsAsync(

ApplicationUseruser);

//...

}

SinceyouchangedtheITodoItemServiceinterface,youalsoneedtoupdatethesignatureoftheGetIncompleteItemsAsync()methodintheTodoItemService:

Services/TodoItemService

publicasyncTask<TodoItem[]>GetIncompleteItemsAsync(

ApplicationUseruser)

Thenextstepistoupdatethedatabasequeryandaddafiltertoshowonlytheitemscreatedbythecurrentuser.Beforeyoucandothat,youneedtoaddanewpropertytothedatabase.

Updatethedatabase

You'llneedtoaddanewpropertytotheTodoItementitymodelsoeachitemcan"remember"theuserthatownsit:

Models/TodoItem.cs

publicstringUserId{get;set;}

Sinceyouupdatedtheentitymodelusedbythedatabasecontext,youalsoneedtomigratethedatabase.Createanewmigrationusingdotnetefintheterminal:

dotnetefmigrationsaddAddItemUserId

Usingidentityintheapplication

81

Page 82: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

ThiscreatesanewmigrationcalledAddItemUserIdwhichwilladdanewcolumntotheItemstable,mirroringthechangeyoumadetotheTodoItemmodel.

Usedotnetefagaintoapplyittothedatabase:

dotnetefdatabaseupdate

Updatetheserviceclass

Withthedatabaseandthedatabasecontextupdated,youcannowupdatetheGetIncompleteItemsAsync()methodintheTodoItemServiceandaddanotherclausetotheWherestatement:

Services/TodoItemService.cs

publicasyncTask<TodoItem[]>GetIncompleteItemsAsync(

ApplicationUseruser)

{

returnawait_context.Items

.Where(x=>x.IsDone==false&&x.UserId==user.Id)

.ToArrayAsync();

}

Ifyouruntheapplicationandregisterorlogin,you'llseeanemptyto-dolistonceagain.Unfortunately,anyitemsyoutrytoadddisappearintotheether,becauseyouhaven'tupdatedtheAddItemactiontobeuser-awareyet.

UpdatetheAddItemandMarkDoneactions

You'llneedtousetheUserManagertogetthecurrentuserintheAddItemandMarkDoneactionmethods,justlikeyoudidinIndex.

Herearebothupdatedmethods:

Usingidentityintheapplication

82

Page 83: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Controllers/TodoController.cs

[ValidateAntiForgeryToken]

publicasyncTask<IActionResult>AddItem(TodoItemnewItem)

{

if(!ModelState.IsValid)

{

returnRedirectToAction("Index");

}

varcurrentUser=await_userManager.GetUserAsync(User);

if(currentUser==null)returnChallenge();

varsuccessful=await_todoItemService

.AddItemAsync(newItem,currentUser);

if(!successful)

{

returnBadRequest("Couldnotadditem.");

}

returnRedirectToAction("Index");

}

[ValidateAntiForgeryToken]

publicasyncTask<IActionResult>MarkDone(Guidid)

{

if(id==Guid.Empty)

{

returnRedirectToAction("Index");

}

varcurrentUser=await_userManager.GetUserAsync(User);

if(currentUser==null)returnChallenge();

varsuccessful=await_todoItemService

.MarkDoneAsync(id,currentUser);

if(!successful)

{

returnBadRequest("Couldnotmarkitemasdone.");

}

returnRedirectToAction("Index");

Usingidentityintheapplication

83

Page 84: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

}

BothservicemethodsmustnowacceptanApplicationUserparameter.UpdatetheinterfacedefinitioninITodoItemService:

Task<bool>AddItemAsync(TodoItemnewItem,ApplicationUseruser);

Task<bool>MarkDoneAsync(Guidid,ApplicationUseruser);

Andfinally,updatetheservicemethodimplementationsintheTodoItemService.InAddItemAsyncmethod,settheUserIdpropertywhenyouconstructanewTodoItem:

publicasyncTask<bool>AddItemAsync(

TodoItemnewItem,ApplicationUseruser)

{

newItem.Id=Guid.NewGuid();

newItem.IsDone=false;

newItem.DueAt=DateTimeOffset.Now.AddDays(3);

newItem.UserId=user.Id;

//...

}

TheWhereclauseintheMarkDoneAsyncmethodalsoneedstocheckfortheuser'sID,soarogueusercan'tcompletesomeoneelse'sitemsbyguessingtheirIDs:

publicasyncTask<bool>MarkDoneAsync(

Guidid,ApplicationUseruser)

{

varitem=await_context.Items

.Where(x=>x.Id==id&&x.UserId==user.Id)

.SingleOrDefaultAsync();

//...

}

Usingidentityintheapplication

84

Page 85: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Alldone!Tryusingtheapplicationwithtwodifferentuseraccounts.Theto-doitemsstayprivateforeachaccount.

Usingidentityintheapplication

85

Page 86: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

AuthorizationwithrolesRolesareacommonapproachtohandlingauthorizationandpermissionsinawebapplication.Forexample,it'scommontocreateanAdministratorrolethatgivesadminusersmorepermissionsorpowerthannormalusers.

Inthisproject,you'lladdaManageUserspagethatonlyadministratorscansee.Ifnormaluserstrytoaccessit,they'llseeanerror.

AddaManageUserspage

First,createanewcontroller:

Controllers/ManageUsersController.cs

usingSystem;

usingSystem.Linq;

usingSystem.Threading.Tasks;

usingMicrosoft.AspNetCore.Mvc;

usingMicrosoft.AspNetCore.Authorization;

usingMicrosoft.AspNetCore.Identity;

usingAspNetCoreTodo.Models;

usingMicrosoft.EntityFrameworkCore;

namespaceAspNetCoreTodo.Controllers

{

[Authorize(Roles="Administrator")]

publicclassManageUsersController:Controller

{

privatereadonlyUserManager<ApplicationUser>

_userManager;

publicManageUsersController(

UserManager<ApplicationUser>userManager)

{

_userManager=userManager;

}

Authorizationwithroles

86

Page 87: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

publicasyncTask<IActionResult>Index()

{

varadmins=(await_userManager

.GetUsersInRoleAsync("Administrator"))

.ToArray();

vareveryone=await_userManager.Users

.ToArrayAsync();

varmodel=newManageUsersViewModel

{

Administrators=admins,

Everyone=everyone

};

returnView(model);

}

}

}

SettingtheRolespropertyonthe[Authorize]attributewillensurethattheusermustbeloggedinandassignedtheAdministratorroleinordertoviewthepage.

Next,createaviewmodel:

Models/ManageUsersViewModel.cs

usingSystem.Collections.Generic;

usingAspNetCoreTodo.Models;

namespaceAspNetCoreTodo.Models

{

publicclassManageUsersViewModel

{

publicApplicationUser[]Administrators{get;set;}

publicApplicationUser[]Everyone{get;set;}

}

}

Authorizationwithroles

87

Page 88: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Finally,createaViews/ManageUsersfolderandaviewfortheIndexaction:

Views/ManageUsers/Index.cshtml

@modelManageUsersViewModel

@{

ViewData["Title"]="Manageusers";

}

<h2>@ViewData["Title"]</h2>

<h3>Administrators</h3>

<tableclass="table">

<thead>

<tr>

<td>Id</td>

<td>Email</td>

</tr>

</thead>

@foreach(varuserinModel.Administrators)

{

<tr>

<td>@user.Id</td>

<td>@user.Email</td>

</tr>

}

</table>

<h3>Everyone</h3>

<tableclass="table">

<thead>

<tr>

<td>Id</td>

<td>Email</td>

</tr>

</thead>

@foreach(varuserinModel.Everyone)

Authorizationwithroles

88

Page 89: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

{

<tr>

<td>@user.Id</td>

<td>@user.Email</td>

</tr>

}

</table>

Startuptheapplicationandtrytoaccessthe/ManageUsersroutewhileloggedinasanormaluser.You'llseethisaccessdeniedpage:

That'sbecauseusersaren'tassignedtheAdministratorroleautomatically.

Createatestadministratoraccount

Forobvioussecurityreasons,itisn'tpossibleforanyonetoregisteranewadministratoraccountthemselves.Infact,theAdministratorroledoesn'tevenexistinthedatabaseyet!

YoucanaddtheAdministratorroleplusatestadministratoraccounttothedatabasethefirsttimetheapplicationstartsup.Addingfirst-timedatatothedatabaseiscalledinitializingorseedingthedatabase.

CreateanewclassintherootoftheprojectcalledSeedData:

Authorizationwithroles

89

Page 90: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

SeedData.cs

usingSystem;

usingSystem.Threading.Tasks;

usingAspNetCoreTodo.Models;

usingMicrosoft.AspNetCore.Identity;

usingMicrosoft.EntityFrameworkCore;

usingMicrosoft.Extensions.DependencyInjection;

namespaceAspNetCoreTodo

{

publicstaticclassSeedData

{

publicstaticasyncTaskInitializeAsync(

IServiceProviderservices)

{

varroleManager=services

.GetRequiredService<RoleManager<IdentityRole>>();

awaitEnsureRolesAsync(roleManager);

varuserManager=services

.GetRequiredService<UserManager<ApplicationUser>>(

);

awaitEnsureTestAdminAsync(userManager);

}

}

}

TheInitializeAsync()methodusesanIServiceProvider(thecollectionofservicesthatissetupintheStartup.ConfigureServices()method)togettheRoleManagerandUserManagerfromASP.NETCoreIdentity.

AddtwomoremethodsbelowtheInitializeAsync()method.First,theEnsureRolesAsync()method:

privatestaticasyncTaskEnsureRolesAsync(

RoleManager<IdentityRole>roleManager)

{

varalreadyExists=awaitroleManager

.RoleExistsAsync(Constants.AdministratorRole);

Authorizationwithroles

90

Page 91: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

if(alreadyExists)return;

awaitroleManager.CreateAsync(

newIdentityRole(Constants.AdministratorRole));

}

ThismethodcheckstoseeifanAdministratorroleexistsinthedatabase.Ifnot,itcreatesone.Insteadofrepeatedlytypingthestring"Administrator",createasmallclasscalledConstantstoholdthevalue:

Constants.cs

namespaceAspNetCoreTodo

{

publicstaticclassConstants

{

publicconststringAdministratorRole="Administrator";

}

}

Ifyouwant,youcanupdatetheManageUsersControllertousethisconstantvalueaswell.

Next,writetheEnsureTestAdminAsync()method:

SeedData.cs

privatestaticasyncTaskEnsureTestAdminAsync(

UserManager<ApplicationUser>userManager)

{

vartestAdmin=awaituserManager.Users

.Where(x=>x.UserName=="[email protected]")

.SingleOrDefaultAsync();

if(testAdmin!=null)return;

testAdmin=newApplicationUser

{

Authorizationwithroles

91

Page 92: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

UserName="[email protected]",

Email="[email protected]"

};

awaituserManager.CreateAsync(

testAdmin,"NotSecure123!!");

awaituserManager.AddToRoleAsync(

testAdmin,Constants.AdministratorRole);

}

Ifthereisn'[email protected],thismethodwillcreateoneandassignatemporarypassword.Afteryouloginforthefirsttime,youshouldchangetheaccount'spasswordtosomethingsecure!

Next,youneedtotellyourapplicationtorunthislogicwhenitstartsup.ModifyProgram.csandupdateMain()tocallanewmethod,InitializeDatabase():

Program.cs

publicstaticvoidMain(string[]args)

{

varhost=BuildWebHost(args);

InitializeDatabase(host);

host.Run();

}

Then,addthenewmethodtotheclassbelowMain():

privatestaticvoidInitializeDatabase(IWebHosthost)

{

using(varscope=host.Services.CreateScope())

{

varservices=scope.ServiceProvider;

try

{

SeedData.InitializeAsync(services).Wait();

}

Authorizationwithroles

92

Page 93: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

catch(Exceptionex)

{

varlogger=services

.GetRequiredService<ILogger<Program>>();

logger.LogError(ex,"ErroroccurredseedingtheDB.");

}

}

}

Addthisusingstatementtothetopofthefile:

usingMicrosoft.Extensions.DependencyInjection;

ThismethodgetstheservicecollectionthatSeedData.InitializeAsync()needsandthenrunsthemethodtoseedthedatabase.Ifsomethinggoeswrong,anerrorislogged.

BecauseInitializeAsync()returnsaTask,theWait()methodmustbeusedtomakesureitfinishesbeforetheapplicationstartsup.You'dnormallyuseawaitforthis,butfortechnicalreasonsyoucan'tuseawaitintheProgramclass.Thisisarareexception.Youshoulduseawaiteverywhereelse!

Whenyoustarttheapplicationnext,theadmin@todo.localaccountwillbecreatedandassignedtheAdministratorrole.Trylogginginwiththisaccount,andnavigatingtohttp://localhost:5000/ManageUsers.You'llseealistofallusersregisteredfortheapplication.

Asanextrachallenge,tryaddingmoreadministrationfeaturestothispage.Forexample,youcouldaddabuttonthatgivesanadministratortheabilitytodeleteauseraccount.

Checkforauthorizationinaview

Authorizationwithroles

93

Page 94: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

The[Authorize]attributemakesiteasytoperformanauthorizationcheckinacontrolleroractionmethod,butwhatifyouneedtocheckauthorizationinaview?Forexample,itwouldbenicetodisplaya"Manageusers"linkinthenavigationbarifthelogged-inuserisanadministrator.

YoucaninjecttheUserManagerdirectlyintoaviewtodothesetypesofauthorizationchecks.Tokeepyourviewscleanandorganized,createanewpartialviewthatwilladdanitemtothenavbarinthelayout:

Views/Shared/_AdminActionsPartial.cshtml

@usingMicrosoft.AspNetCore.Identity

@usingAspNetCoreTodo.Models

@injectSignInManager<ApplicationUser>signInManager

@injectUserManager<ApplicationUser>userManager

@if(signInManager.IsSignedIn(User))

{

varcurrentUser=awaitUserManager.GetUserAsync(User);

varisAdmin=currentUser!=null

&&awaituserManager.IsInRoleAsync(

currentUser,

Constants.AdministratorRole);

if(isAdmin)

{

<ulclass="navnavbar-navnavbar-right">

<li>

<aasp-controller="ManageUsers"

asp-action="Index">

ManageUsers

</a>

</li>

</ul>

}

}

Authorizationwithroles

94

Page 95: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

It'sconventionaltonamesharedpartialviewsstartingwithan_underscore,butit'snotrequired.

ThispartialviewfirstusestheSignInManagertoquicklydeterminewhethertheuserisloggedin.Iftheyaren't,therestoftheviewcodecanbeskipped.Ifthereisalogged-inuser,theUserManagerisusedtolookuptheirdetailsandperformanauthorizationcheckwithIsInRoleAsync().Ifallcheckssucceedandtheuserisanadminstrator,aManageuserslinkisaddedtothenavbar.

Toincludethispartialinthemainlayout,edit_Layout.cshtmlandadditinthenavbarsection:

Views/Shared/_Layout.cshtml

<divclass="navbar-collapsecollapse">

<ulclass="navnavbar-nav">

<!--existingcodehere-->

</ul>

@awaitHtml.PartialAsync("_LoginPartial")

@awaitHtml.PartialAsync("_AdminActionsPartial")

</div>

Whenyouloginwithanadministratoraccount,you'llnowseeanewitemonthetopright:

Authorizationwithroles

95

Page 96: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

MoreresourcesASP.NETCoreIdentityhelpsyouaddsecurityandidentityfeatureslikeloginandregistrationtoyourapplication.Thedotnetnewtemplatesgiveyoupre-builtviewsandcontrollersthathandlethesecommonscenariossoyoucangetupandrunningquickly.

There'smuchmorethatASP.NETCoreIdentitycando,suchaspasswordresetandsociallogin.Thedocumentationavailableathttp://docs.asp.netisafantasticresourceforlearninghowtoaddthesefeatures.

AlternativestoASP.NETCoreIdentity

ASP.NETCoreIdentityisn'ttheonlywaytoaddidentityfunctionality.Anotherapproachistouseacloud-hostedidentityservicelikeAzureActiveDirectoryB2CorOktatohandleidentityforyourapplication.Youcanthinkoftheseoptionsaspartofaprogression:

Do-it-yourselfsecurity:Notrecommended,unlessyouareasecurityexpert!ASP.NETCoreIdentity:Yougetalotofcodeforfreewiththetemplates,whichmakesitprettyeasytogetstarted.You'llstillneedtowritesomecodeformoreadvancedscenarios,andmaintainadatabasetostoreuserinformation.Cloud-hostedidentityservices.Theservicehandlesbothsimpleandadvancedscenarios(multi-factorauthentication,accountrecovery,federation),andsignificantlyreducestheamountofcodeyouneedtowriteandmaintaininyourapplication.Plus,sensitiveuserdataisn'tstoredinyourowndatabase.

Moreresources

96

Page 97: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Forthisproject,ASP.NETCoreIdentityisagreatfit.Formorecomplexprojects,I'drecommenddoingsomeresearchandexperimentingwithbothoptionstounderstandwhichisbestforyourusecase.

Moreresources

97

Page 98: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

AutomatedtestingWritingtestsisanimportantpartofbuildinganyapplication.Testingyourcodehelpsyoufindandavoidbugs,andmakesiteasiertorefactoryourcodelaterwithoutbreakingfunctionalityorintroducingnewproblems.

Inthischapteryou'lllearnhowtowritebothunittestsandintegrationteststhatexerciseyourASP.NETCoreapplication.Unittestsaresmallteststhatmakesureasinglemethodorchunkoflogicworksproperly.Integrationtests(sometimescalledfunctionaltests)arelargerteststhatsimulatereal-worldscenariosandtestmultiplelayersorpartsofyourapplication.

Automatedtesting

98

Page 99: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

UnittestingUnittestsaresmall,shortteststhatcheckthebehaviorofasinglemethodorclass.Whenthecodeyou'retestingreliesonothermethodsorclasses,unittestsrelyonmockingthoseotherclassessothatthetestonlyfocusesononethingatatime.

Forexample,theTodoControllerclasshastwodependencies:anITodoItemServiceandtheUserManager.TheTodoItemService,inturn,dependsontheApplicationDbContext.(TheideathatyoucandrawalinefromTodoController>TodoItemService>ApplicationDbContextiscalledadependencygraph).

Whentheapplicationrunsnormally,theASP.NETCoreservicecontaineranddependencyinjectionsysteminjectseachofthoseobjectsintothedependencygraphwhentheTodoControllerortheTodoItemServiceiscreated.

Whenyouwriteaunittest,ontheotherhand,youhavetohandlethedependencygraphyourself.It'stypicaltoprovidetest-onlyor"mocked"versionsofthosedependencies.Thismeansyoucanisolatejustthelogicintheclassormethodyouaretesting.(Thisisimportant!Ifyou'retestingaservice,youdon'twanttoalsobeaccidentallywritingtoyourdatabase.)

Createatestproject

It'sabestpracticetocreateaseparateprojectforyourtests,sotheyarekeptseparatefromyourapplicationcode.Thenewtestprojectshouldliveinadirectorythat'snextto(notinside)yourmainproject'sdirectory.

Unittesting

99

Page 100: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Ifyou'recurrentlyinyourprojectdirectory,cduponelevel.(ThisrootdirectorywillalsobecalledAspNetCoreTodo).Thenusethiscommandtoscaffoldanewtestproject:

dotnetnewxunit-oAspNetCoreTodo.UnitTests

xUnit.NETisapopulartestframeworkfor.NETcodethatcanbeusedtowritebothunitandintegrationtests.Likeeverythingelse,it'sasetofNuGetpackagesthatcanbeinstalledinanyproject.Thedotnetnewxunittemplatealreadyincludeseverythingyouneed.

Yourdirectorystructureshouldnowlooklikethis:

AspNetCoreTodo/

AspNetCoreTodo/

AspNetCoreTodo.csproj

Controllers/

(etc...)

AspNetCoreTodo.UnitTests/

AspNetCoreTodo.UnitTests.csproj

Sincethetestprojectwillusetheclassesdefinedinyourmainproject,you'llneedtoaddareferencetotheAspNetCoreTodoproject:

dotnetaddreference../AspNetCoreTodo/AspNetCoreTodo.csproj

DeletetheUnitTest1.csfilethat'sautomaticallycreated.You'rereadytowriteyourfirsttest.

Ifyou'reusingVisualStudioCode,youmayneedtocloseandreopentheVisualStudioCodewindowtogetcodecompletionworkinginthenewproject.

Writeaservicetest

Unittesting

100

Page 101: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

TakealookatthelogicintheAddItemAsync()methodoftheTodoItemService:

publicasyncTask<bool>AddItemAsync(

TodoItemnewItem,ApplicationUseruser)

{

newItem.Id=Guid.NewGuid();

newItem.IsDone=false;

newItem.DueAt=DateTimeOffset.Now.AddDays(3);

newItem.UserId=user.Id;

_context.Items.Add(newItem);

varsaveResult=await_context.SaveChangesAsync();

returnsaveResult==1;

}

Thismethodmakesanumberofdecisionsorassumptionsaboutthenewitem(inotherwords,performsbusinesslogiconthenewitem)beforeitactuallysavesittothedatabase:

TheUserIdpropertyshouldbesettotheuser'sIDNewitemsshouldalwaysbeincomplete(IsDone=false)ThetitleofthenewitemshouldbecopiedfromnewItem.TitleNewitemsshouldalwaysbedue3daysfromnow

ImagineifyouorsomeoneelserefactoredtheAddItemAsync()methodandforgotaboutpartofthisbusinesslogic.Thebehaviorofyourapplicationcouldchangewithoutyourealizingit!Youcanpreventthisbywritingatestthatdouble-checksthatthisbusinesslogichasn'tchanged(evenifthemethod'sinternalimplementationchanges).

Itmightseemunlikelynowthatyoucouldintroduceachangeinbusinesslogicwithoutrealizingit,butitbecomesmuchhardertokeeptrackofdecisionsandassumptionsinalarge,complexproject.Thelargeryourprojectis,themoreimportantitistohaveautomatedchecksthatmakesurenothinghaschanged!

Unittesting

101

Page 102: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

TowriteaunittestthatwillverifythelogicintheTodoItemService,createanewclassinyourtestproject:

AspNetCoreTodo.UnitTests/TodoItemServiceShould.cs

usingSystem;

usingSystem.Threading.Tasks;

usingAspNetCoreTodo.Data;

usingAspNetCoreTodo.Models;

usingAspNetCoreTodo.Services;

usingMicrosoft.EntityFrameworkCore;

usingXunit;

namespaceAspNetCoreTodo.UnitTests

{

publicclassTodoItemServiceShould

{

[Fact]

publicasyncTaskAddNewItemAsIncompleteWithDueDate()

{

//...

}

}

}

Therearemanydifferentwaysofnamingandorganizingtests,allwithdifferentprosandcons.IlikepostfixingmytestclasseswithShouldtocreateareadablesentencewiththetestmethodname,butfeelfreetouseyourownstyle!

The[Fact]attributecomesfromthexUnit.NETpackage,anditmarksthismethodasatestmethod.

TheTodoItemServicerequiresanApplicationDbContext,whichisnormallyconnectedtoyourdatabase.Youwon'twanttousethatfortests.Instead,youcanuseEntityFrameworkCore'sin-memorydatabaseproviderinyourtestcode.Sincetheentiredatabaseexistsinmemory,

Unittesting

102

Page 103: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

it'swipedouteverytimethetestisrestarted.And,sinceit'saproperEntityFrameworkCoreprovider,theTodoItemServicewon'tknowthedifference!

UseaDbContextOptionsBuildertoconfigurethein-memorydatabaseprovider,andthenmakeacalltoAddItemAsync():

varoptions=newDbContextOptionsBuilder<ApplicationDbContext>()

.UseInMemoryDatabase(databaseName:"Test_AddNewItem").Options;

//Setupacontext(connectiontothe"DB")forwriting

using(varcontext=newApplicationDbContext(options))

{

varservice=newTodoItemService(context);

varfakeUser=newApplicationUser

{

Id="fake-000",

UserName="[email protected]"

};

awaitservice.AddItemAsync(newTodoItem

{

Title="Testing?"

},fakeUser);

}

Thelastlinecreatesanewto-doitemcalledTesting?,andtellstheservicetosaveittothe(in-memory)database.

Toverifythatthebusinesslogicrancorrectly,writesomemorecodebelowtheexistingusingblock:

//Useaseparatecontexttoreaddatabackfromthe"DB"

using(varcontext=newApplicationDbContext(options))

{

varitemsInDatabase=awaitcontext

.Items.CountAsync();

Assert.Equal(1,itemsInDatabase);

Unittesting

103

Page 104: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

varitem=awaitcontext.Items.FirstAsync();

Assert.Equal("Testing?",item.Title);

Assert.Equal(false,item.IsDone);

//Itemshouldbedue3daysfromnow(giveortakeasecond)

vardifference=DateTimeOffset.Now.AddDays(3)-item.DueAt;

Assert.True(difference<TimeSpan.FromSeconds(1));

}

Thefirstassertionisasanitycheck:thereshouldneverbemorethanoneitemsavedtothein-memorydatabase.Assumingthat'strue,thetestretrievesthesaveditemwithFirstAsyncandthenassertsthatthepropertiesaresettotheexpectedvalues.

BothunitandintegrationteststypicallyfollowtheAAA(Arrange-Act-Assert)pattern:objectsanddataaresetupfirst,thensomeactionisperformed,andfinallythetestchecks(asserts)thattheexpectedbehavioroccurred.

Assertingadatetimevalueisalittletricky,sincecomparingtwodatesforequalitywillfailifeventhemillisecondcomponentsaredifferent.Instead,thetestchecksthattheDueAtvalueislessthanasecondawayfromtheexpectedvalue.

Runthetest

Ontheterminal,runthiscommand(makesureyou'restillintheAspNetCoreTodo.UnitTestsdirectory):

dotnettest

Thetestcommandscansthecurrentprojectfortests(markedwith[Fact]attributesinthiscase),andrunsallthetestsitfinds.You'llseeoutputsimilarto:

Startingtestexecution,pleasewait...

Unittesting

104

Page 105: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Discovering:AspNetCoreTodo.UnitTests

Discovered:AspNetCoreTodo.UnitTests

Starting:AspNetCoreTodo.UnitTests

Finished:AspNetCoreTodo.UnitTests

Totaltests:1.Passed:1.Failed:0.Skipped:0.

TestRunSuccessful.

Testexecutiontime:1.9074Seconds

YounowhaveonetestprovidingtestcoverageoftheTodoItemService.Asanextrachallenge,trywritingunitteststhatensure:

TheMarkDoneAsync()methodreturnsfalseifit'spassedanIDthatdoesn'texistTheMarkDoneAsync()methodreturnstruewhenitmakesavaliditemascompleteTheGetIncompleteItemsAsync()methodreturnsonlytheitemsownedbyaparticularuser

Unittesting

105

Page 106: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

IntegrationtestingComparedtounittests,integrationtestsaremuchlargerinscope.exercisethewholeapplicationstack.Insteadofisolatingoneclassormethod,integrationtestsensurethatallofthecomponentsofyourapplicationareworkingtogetherproperly:routing,controllers,services,databasecode,andsoon.

Integrationtestsareslowerandmoreinvolvedthanunittests,soit'scommonforaprojecttohavelotsofsmallunittestsbutonlyahandfulofintegrationtests.

Inordertotestthewholestack(includingcontrollerrouting),integrationteststypicallymakeHTTPcallstoyourapplicationjustlikeawebbrowserwould.

TowriteintegrationteststhatmakeHTTPrequests,youcouldmanuallystartyourapplicationandtestsatthesametime,andwriteyourteststomakerequeststohttp://localhost:5000.ASP.NETCoreprovidesanicerwaytohostyourapplicationfortesting,however:theTestServerclass.TestServercanhostyourapplicationforthedurationofthetest,andthenstopitautomaticallywhenthetestiscomplete.

Createatestproject

Ifyou'recurrentlyinyourprojectdirectory,cduponeleveltotherootAspNetCoreTododirectory.Usethiscommandtoscaffoldanewtestproject:

dotnetnewxunit-oAspNetCoreTodo.IntegrationTests

Yourdirectorystructureshouldnowlooklikethis:

Integrationtesting

106

Page 107: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

AspNetCoreTodo/

AspNetCoreTodo/

AspNetCoreTodo.csproj

Controllers/

(etc...)

AspNetCoreTodo.UnitTests/

AspNetCoreTodo.UnitTests.csproj

AspNetCoreTodo.IntegrationTests/

AspNetCoreTodo.IntegrationTests.csproj

Ifyouprefer,youcankeepyourunittestsandintegrationtestsinthesameproject.Forlargeprojects,it'scommontosplitthemupsoit'seasytorunthemseparately.

Sincethetestprojectwillusetheclassesdefinedinyourmainproject,you'llneedtoaddareferencetothemainproject:

dotnetaddreference../AspNetCoreTodo/AspNetCoreTodo.csproj

You'llalsoneedtoaddtheMicrosoft.AspNetCore.TestHostNuGetpackage:

dotnetaddpackageMicrosoft.AspNetCore.TestHost

DeletetheUnitTest1.csfilethat'screatedbydotnetnew.You'rereadytowriteanintegrationtest.

Writeanintegrationtest

Thereareafewthingsthatneedtobeconfiguredonthetestserverbeforeeachtest.Insteadofclutteringthetestwiththissetupcode,youcankeepthissetupinaseparateclass.CreateanewclasscalledTestFixture:

Integrationtesting

107

Page 108: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

AspNetCoreTodo.IntegrationTests/TestFixture.cs

usingSystem;

usingSystem.Collections.Generic;

usingSystem.IO;

usingSystem.Net.Http;

usingMicrosoft.AspNetCore.Hosting;

usingMicrosoft.AspNetCore.TestHost;

usingMicrosoft.Extensions.Configuration;

namespaceAspNetCoreTodo.IntegrationTests

{

publicclassTestFixture:IDisposable

{

privatereadonlyTestServer_server;

publicHttpClientClient{get;}

publicTestFixture()

{

varbuilder=newWebHostBuilder()

.UseStartup<AspNetCoreTodo.Startup>()

.ConfigureAppConfiguration((context,config)=>

{

config.SetBasePath(Path.Combine(

Directory.GetCurrentDirectory(),

"..\\..\\..\\..\\AspNetCoreTodo"));

config.AddJsonFile("appsettings.json");

});

_server=newTestServer(builder);

Client=_server.CreateClient();

Client.BaseAddress=newUri("http://localhost:8888");

}

publicvoidDispose()

{

Client.Dispose();

_server.Dispose();

}

}

}

Integrationtesting

108

Page 109: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

ThisclasstakescareofsettingupaTestServer,andwillhelpkeeptheteststhemselvescleanandtidy.

Nowyou're(really)readytowriteanintegrationtest.CreateanewclasscalledTodoRouteShould:

AspNetCoreTodo.IntegrationTests/TodoRouteShould.cs

usingSystem.Net;

usingSystem.Net.Http;

usingSystem.Threading.Tasks;

usingXunit;

namespaceAspNetCoreTodo.IntegrationTests

{

publicclassTodoRouteShould:IClassFixture<TestFixture>

{

privatereadonlyHttpClient_client;

publicTodoRouteShould(TestFixturefixture)

{

_client=fixture.Client;

}

[Fact]

publicasyncTaskChallengeAnonymousUser()

{

//Arrange

varrequest=newHttpRequestMessage(

HttpMethod.Get,"/todo");

//Act:requestthe/todoroute

varresponse=await_client.SendAsync(request);

//Assert:theuserissenttotheloginpage

Assert.Equal(

HttpStatusCode.Redirect,

response.StatusCode);

Assert.Equal(

"http://localhost:8888/Account"+

Integrationtesting

109

Page 110: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

"/Login?ReturnUrl=%2Ftodo",

response.Headers.Location.ToString());

}

}

}

Thistestmakesananonymous(not-logged-in)requesttothe/todorouteandverifiesthatthebrowserisredirectedtotheloginpage.

Thisscenarioisagoodcandidateforanintegrationtest,becauseitinvolvesmultiplecomponentsoftheapplication:theroutingsystem,thecontroller,thefactthatthecontrollerismarkedwith[Authorize],andsoon.It'salsoagoodtestbecauseitensuresyouwon'teveraccidentallyremovethe[Authorize]attributeandmaketheto-doviewaccessibletoeveryone.

RunthetestRunthetestintheterminalwithdotnettest.Ifeverything'sworkingright,you'llseeasuccessmessage:

Startingtestexecution,pleasewait...

Discovering:AspNetCoreTodo.IntegrationTests

Discovered:AspNetCoreTodo.IntegrationTests

Starting:AspNetCoreTodo.IntegrationTests

Finished:AspNetCoreTodo.IntegrationTests

Totaltests:1.Passed:1.Failed:0.Skipped:0.

TestRunSuccessful.

Testexecutiontime:2.0588Seconds

Wrapup

Integrationtesting

110

Page 111: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Testingisabroadtopic,andthere'smuchmoretolearn.Thischapterdoesn'ttouchonUItestingortestingfrontend(JavaScript)code,whichprobablydeserveentirebooksoftheirown.Youshould,however,havetheskillsandbaseknowledgeyouneedtolearnmoreabouttestingandtopracticewritingtestsforyourownapplications.

TheASP.NETCoredocumentation(https://docs.asp.net)andStackOverflowaregreatresourcesforlearningmoreandfindinganswerswhenyougetstuck.

Integrationtesting

111

Page 112: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

DeploytheapplicationYou'vecomealongway,butyou'renotquitedoneyet.Onceyou'vecreatedagreatapplication,youneedtoshareitwiththeworld!

BecauseASP.NETCoreapplicationscanrunonWindows,Mac,orLinux,thereareanumberofdifferentwaysyoucandeployyourapplication.Inthischapter,I'llshowyouthemostcommon(andeasiest)waystogolive.

DeploymentoptionsASP.NETCoreapplicationsaretypicallydeployedtooneoftheseenvironments:

ADockerhost.AnymachinecapableofhostingDockercontainerscanbeusedtohostanASP.NETCoreapplication.CreatingaDockerimageisaveryquickwaytogetyourapplicationdeployed,especiallyifyou'refamiliarwithDocker.(Ifyou'renot,don'tworry!I'llcoverthestepslater.)

Azure.MicrosoftAzurehasnativesupportforASP.NETCoreapplications.IfyouhaveanAzuresubscription,youjustneedtocreateaWebAppanduploadyourprojectfiles.I'llcoverhowtodothiswiththeAzureCLIinthenextsection.

Linux(withNginx).Ifyoudon'twanttogotheDockerroute,youcanstillhostyourapplicationonanyLinuxserver(thisincludesAmazonEC2andDigitalOceanvirtualmachines).It'stypicaltopairASP.NETCorewiththeNginxreverseproxy.(MoreaboutNginxbelow.)

Deploytheapplication

112

Page 113: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Windows.YoucanusetheIISwebserveronWindowstohostASP.NETCoreapplications.It'susuallyeasier(andcheaper)tojustdeploytoAzure,butifyouprefermanagingWindowsserversyourself,it'llworkjustfine.

KestrelandreverseproxiesIfyoudon'tcareaboutthegutsofhostingASP.NETCoreapplicationsandjustwantthestep-by-stepinstructions,feelfreetoskiptooneofthenexttwosections.

ASP.NETCoreincludesafast,lightweightwebservercalledKestrel.It'stheserveryou'vebeenusingeverytimeyourandotnetrunandbrowsedtohttp://localhost:5000.Whenyoudeployyourapplicationtoaproductionenvironment,it'llstilluseKestrelbehindthescenes.However,it'srecommendedthatyouputareverseproxyinfrontofKestrel,becauseKestreldoesn'tyethaveloadbalancingandotherfeaturesthatmorematurewebservershave.

OnLinux(andinDockercontainers),youcanuseNginxortheApachewebservertoreceiveincomingrequestsfromtheinternetandroutethemtoyourapplicationhostedwithKestrel.Ifyou'reonWindows,IISdoesthesamething.

Ifyou'reusingAzuretohostyourapplication,thisisalldoneforyouautomatically.I'llcoversettingupNginxasareverseproxyintheDockersection.

Deploytheapplication

113

Page 114: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

DeploytoAzureDeployingyourASP.NETCoreapplicationtoAzureonlytakesafewsteps.YoucandoitthroughtheAzurewebportal,oronthecommandlineusingtheAzureCLI.I'llcoverthelatter.

Whatyou'llneed

Git(usegit--versiontomakesureit'sinstalled)TheAzureCLI(followtheinstallinstructionsathttps://github.com/Azure/azure-cli)AnAzuresubscription(thefreesubscriptionisfine)Adeploymentconfigurationfileinyourprojectroot

Createadeploymentconfigurationfile

Sincetherearemultipleprojectsinyourdirectorystructure(thewebapplication,andtwotestprojects),Azurewon'tknowwhichonetopublish.Tofixthis,createafilecalled.deploymentattheverytopofyourdirectorystructure:

.deployment

[config]

project=AspNetCoreTodo/AspNetCoreTodo.csproj

Makesureyousavethefileas.deploymentwithnootherpartstothename.(OnWindows,youmayneedtoputquotesaroundthefilename,like".deployment",topreventa.txtextensionfrombeingadded.)

Ifyoulsordirinyourtop-leveldirectory,youshouldseetheseitems:

DeploytoAzure

114

Page 115: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

.deployment

AspNetCoreTodo

AspNetCoreTodo.IntegrationTests

AspNetCoreTodo.UnitTests

SetuptheAzureresources

IfyoujustinstalledtheAzureCLIforthefirsttime,run

azlogin

andfollowthepromptstologinonyourmachine.Then,createanewResourceGroupforthisapplication:

azgroupcreate-lwestus-nAspNetCoreTodoGroup

ThiscreatesaResourceGroupintheWestUSregion.Ifyou'relocatedfarawayfromthewesternUS,useazaccountlist-locationstogetalistoflocationsandfindoneclosertoyou.

Next,createanAppServiceplaninthegroupyoujustcreated:

azappserviceplancreate-gAspNetCoreTodoGroup-nAspNetCoreTodo

Plan--skuF1

F1isthefreeappplan.Ifyouwanttouseacustomdomainnamewithyourapp,usetheD1($10/month)planorhigher.

NowcreateaWebAppintheAppServiceplan:

azwebappcreate-gAspNetCoreTodoGroup-pAspNetCoreTodoPlan-nM

yTodoApp

DeploytoAzure

115

Page 116: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Thenameoftheapp(MyTodoAppabove)mustbegloballyuniqueinAzure.Oncetheappiscreated,itwillhaveadefaultURLintheformat:http://mytodoapp.azurewebsites.net

DeployyourprojectfilestoAzure

YoucanuseGittopushyourapplicationfilesuptotheAzureWebApp.Ifyourlocaldirectoryisn'talreadytrackedasaGitrepo,runthesecommandstosetitup:

gitinit

gitadd.

gitcommit-m"Firstcommit!"

Next,createanAzureusernameandpasswordfordeployment:

azwebappdeploymentuserset--user-namenate

Followtheinstructionstocreateapassword.Thenuseconfig-local-gittospitoutaGitURL:

azwebappdeploymentsourceconfig-local-git-gAspNetCoreTodoGrou

p-nMyTodoApp--outtsv

https://[email protected]/MyTodoApp.git

CopytheURLtotheclipboard,anduseittoaddaGitremotetoyourlocalrepository:

gitremoteaddazure<paste>

Youonlyneedtodothesestepsonce.Now,wheneveryouwanttopushyourapplicationfilestoAzure,checktheminwithGitandrun

DeploytoAzure

116

Page 117: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

gitpushazuremaster

You'llseeastreamoflogmessagesastheapplicationisdeployedtoAzure.

Whenit'scomplete,browsetohttp://yourappname.azurewebsites.nettocheckouttheapp!

DeploytoAzure

117

Page 118: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

DeploywithDockerIfyouaren'tusingaplatformlikeAzure,containerizationtechnologieslikeDockercanmakeiteasytodeploywebapplicationstoyourownservers.Insteadofspendingtimeconfiguringaserverwiththedependenciesitneedstorunyourapp,copyingfiles,andrestartingprocesses,youcansimplycreateaDockerimagethatdescribeseverythingyourappneedstorun,andspinitupasacontaineronanyDockerhost.

Dockercanmakescalingyourappacrossmultipleserverseasier,too.Onceyouhaveanimage,usingittocreate1containeristhesameprocessascreating100containers.

Beforeyoustart,youneedtheDockerCLIinstalledonyourdevelopmentmachine.Searchfor"getdockerfor(mac/windows/linux)"andfollowtheinstructionsontheofficialDockerwebsite.Youcanverifythatit'sinstalledcorrectlywith

dockerversion

AddaDockerfile

Thefirstthingyou'llneedisaDockerfile,whichislikearecipethattellsDockerwhatyourapplicationneedstobuildandrun.

CreateafilecalledDockerfile(noextension)intheroot,top-levelAspNetCoreTodofolder.Openitinyourfavoriteeditor.Writethefollowingline:

FROMmicrosoft/dotnet:2.0-sdkASbuild

DeploywithDocker

118

Page 119: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

ThistellsDockertousethemicrosoft/dotnet:2.0-sdkimageasastartingpoint.ThisimageispublishedbyMicrosoftandcontainsthetoolsanddependenciesyouneedtoexecutedotnetbuildandcompileyourapplication.Byusingthispre-builtimageasastartingpoint,Dockercanoptimizetheimageproducedforyourappandkeepitsmall.

Next,addthisline:

COPYAspNetCoreTodo/*.csproj./app/AspNetCoreTodo/

TheCOPYcommandcopiesthe.csprojprojectfileintotheimageatthepath/app/AspNetCoreTodo/.Notethatnoneoftheactualcode(.csfiles)havebeencopiedintotheimageyet.You'llseewhyinaminute.

WORKDIR/app/AspNetCoreTodo

RUNdotnetrestore

WORKDIRistheDockerequivalentofcd.Thismeansanycommandsexecutednextwillrunfrominsidethe/app/AspNetCoreTododirectorythattheCOPYcommandcreatedinthelaststep.

RunningthedotnetrestorecommandrestorestheNuGetpackagesthattheapplicationneeds,definedinthe.csprojfile.Byrestoringpackagesinsidetheimagebeforeaddingtherestofthecode,Dockerisabletocachetherestoredpackages.Then,ifyoumakecodechanges(butdon'tchangethepackagesdefinedintheprojectfile),rebuildingtheDockerimagewillbesuperfast.

Nowit'stimetocopytherestofthecodeandcompiletheapplication:

COPYAspNetCoreTodo/../AspNetCoreTodo/

RUNdotnetpublish-oout/p:PublishWithAspNetCoreTargetManifest="

false"

DeploywithDocker

119

Page 120: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Thedotnetpublishcommandcompilestheproject,andthe-ooutflagputsthecompiledfilesinadirectorycalledout.

Thesecompiledfileswillbeusedtoruntheapplicationwiththefinalfewcommands:

FROMmicrosoft/dotnet:2.0-runtimeASruntime

ENVASPNETCORE_URLShttp://+:80

WORKDIR/app

COPY--from=build/app/AspNetCoreTodo/out./

ENTRYPOINT["dotnet","AspNetCoreTodo.dll"]

TheFROMcommandisusedagaintoselectasmallerimagethatonlyhasthedependenciesneededtoruntheapplication.TheENVcommandisusedtosetenvironmentvariablesinthecontainer,andtheASPNETCORE_URLSenvironmentvariabletellsASP.NETCorewhichnetworkinterfaceandportitshouldbindto(inthiscase,port80).

TheENTRYPOINTcommandletsDockerknowthatthecontainershouldbestartedasanexecutablebyrunningdotnetAspNetCoreTodo.dll.Thistellsdotnettostartupyourapplicationfromthecompiledfilecreatedbydotnetpublishearlier.(Whenyoudodotnetrunduringdevelopment,you'reaccomplishingthesamethinginonestep.)

ThefullDockerfilelookslikethis:

Dockerfile

FROMmicrosoft/dotnet:2.0-sdkASbuild

COPYAspNetCoreTodo/*.csproj./app/AspNetCoreTodo/

WORKDIR/app/AspNetCoreTodo

RUNdotnetrestore

COPYAspNetCoreTodo/../

RUNdotnetpublish-oout/p:PublishWithAspNetCoreTargetManifest="

false"

FROMmicrosoft/dotnet:2.0-runtimeASruntime

DeploywithDocker

120

Page 121: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

ENVASPNETCORE_URLShttp://+:80

WORKDIR/app

COPY--from=build/app/AspNetCoreTodo/out./

ENTRYPOINT["dotnet","AspNetCoreTodo.dll"]

Createanimage

MakesuretheDockerfileissaved,andthenusedockerbuildtocreateanimage:

dockerbuild-taspnetcoretodo.

Don'tmissthetrailingperiod!ThattellsDockertolookforaDockerfileinthecurrentdirectory.

Oncetheimageiscreated,youcanrundockerimagestotolistalltheimagesavailableonyourlocalmachine.Totestitoutinacontainer,run

dockerrun--nameaspnetcoretodo_sample--rm-it-p8080:80aspnet

coretodo

The-itflagtellsDockertorunthecontainerininteractivemode(outputtingtotheterminal,asopposedtorunninginthebackground).Whenyouwanttostopthecontainer,pressControl-C.

RemembertheASPNETCORE_URLSvariablethattoldASP.NETCoretolistenonport80?The-p8080:80optiontellsDockertomapport8080onyourmachinetothecontainer'sport80.Openupyourbrowserandnavigatetohttp://localhost:8080toseetheapplicationrunninginthecontainer!

SetupNginx

DeploywithDocker

121

Page 122: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Atthebeginningofthischapter,ImentionedthatyoushoulduseareverseproxylikeNginxtoproxyrequeststoKestrel.YoucanuseDockerforthis,too.

Theoverallarchitecturewillconsistoftwocontainers:anNginxcontainerlisteningonport80,forwardingrequeststothecontaineryoujustbuiltthathostsyourapplicationwithKestrel.

TheNginxcontainerneedsitsownDockerfile.TokeepitfromconflictingwiththeDockerfileyoujustcreated,makeanewdirectoryinthewebapplicationroot:

mkdirnginx

CreateanewDockerfileandaddtheselines:

nginx/Dockerfile

FROMnginx

COPYnginx.conf/etc/nginx/nginx.conf

Next,createannginx.conffile:

nginx/nginx.conf

events{worker_connections1024;}

http{

server{

listen80;

location/{

proxy_passhttp://kestrel:80;

proxy_http_version1.1;

proxy_set_headerUpgrade$http_upgrade;

proxy_set_headerConnection'keep-alive';

proxy_set_headerHost$host;

proxy_cache_bypass$http_upgrade;

}

DeploywithDocker

122

Page 123: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

}

}

ThisconfigurationfiletellsNginxtoproxyincomingrequeststohttp://kestrel:80.(You'llseewhykestrelworksasahostnameinamoment.)

Whenyoumakedeployyourapplicationtoaproductionenvironment,youshouldaddtheserver_namedirectiveandvalidateandrestrictthehostheadertoknowngoodvalues.Formoreinformation,see:

https://github.com/aspnet/Announcements/issues/295

SetupDockerCompose

There'sonemorefiletocreate.Upintherootdirectory,createdocker-compose.yml:

docker-compose.yml

nginx:

build:./nginx

links:

-kestrel:kestrel

ports:

-"80:80"

kestrel:

build:.

ports:

-"80"

DockerComposeisatoolthathelpsyoucreateandrunmulti-containerapplications.Thisconfigurationfiledefinestwocontainers:nginxfromthe./nginx/Dockerfilerecipe,andkestrelfromthe./Dockerfilerecipe.Thecontainersareexplicitlylinkedtogethersotheycancommunicate.

DeploywithDocker

123

Page 124: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Youcantryspinninguptheentiremulti-containerapplicationbyrunning:

docker-composeup

Tryopeningabrowserandnavigatingtohttp://localhost(port80,not8080!).Nginxislisteningonport80(thedefaultHTTPport)andproxyingrequeststoyourASP.NETCoreapplicationhostedbyKestrel.

SetupaDockerserver

Specificsetupinstructionsareoutsidethescopeofthisbook,butanymodernflavorofLinux(likeUbuntu)canbeusedtosetupaDockerhost.Forexample,youcouldcreateavirtualmachinewithAmazonEC2,andinstalltheDockerservice.Youcansearchfor"amazonec2setupdocker"(forexample)forinstructions.

IlikeusingDigitalOceanbecausethey'vemadeitreallyeasytogetstarted.DigitalOceanhasbothapre-builtDockervirtualmachine,andin-depthtutorialsforgettingDockerupandrunning(searchfor"digitaloceandocker").

DeploywithDocker

124

Page 125: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

ConclusionThanksformakingittotheendoftheLittleASP.NETCoreBook!Ifthisbookwashelpful(ornot),I'dlovetohearyourthoughts.SendmeyourcommentsviaTwitter:https://twitter.com/nbarbettini

HowtolearnmoreThere'salotmorethatASP.NETCorecandothatcouldn'tfitinthisshortbook,including

BuildingRESTfulAPIsandmicroservicesUsingASP.NETCorewithsingle-pageappslikeAngularandReactRazorPagesBundlingandminifyingstaticassetsWebSocketsandSignalR

Thereareanumberofwaysyoucanlearnmore:

TheASP.NETCoredocumentation.TheofficialASP.NETCoredocumentationathttp://docs.asp.netcontainsanumberofin-depthtutorialscoveringmanyofthesetopics.I'dhighlyrecommendit!

ASP.NETCoreinAction.ThisbookbyAndrewLockisacomprehensive,deepdiveintoASP.NETCore.YoucangetitfromAmazonoralocalbookstore.

CoursesonLinkedInLearningandPluralsight.Ifyoulearnbestfromvideos,therearefantasticcoursesavailableonPluralsightandLinkedInLearning(includingsomebyyourstruly).Ifyoudon'thaveanaccountandneedacoupon,sendmeanemail:[email protected].

Conclusion

125

Page 126: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Nate'sblog.IalsowriteaboutASP.NETCoreandmoreonmyblogathttps://www.recaffeinate.co.

Happycoding!

AbouttheauthorHey,I'mNate!IwrotetheLittleASP.NETCoreBookinalong,caffeine-fueledweekendbecauseIlovethe.NETcommunityandwantedtogivebackinmyownlittleway.Ihopeithelpedyoulearnsomethingnew!

YoucanstayintouchwithmeonTwitter(@nbarbettini)oronmyblog(https://www.recaffeinate.co)[email protected].

SpecialthanksToJennifer,whoalwayssupportsmycrazyideas.

TothefollowingcontributorswhoimprovedtheLittleASP.NETCoreBook:

0xNFMattWelke

TotheseamazingpolyglotprogrammerswhotranslatedtheLittleASP.NETCoreBook:

sahinyanlik(Turkish)windsting,yuyi(SimplifiedChinese)

Changelog

Conclusion

126

Page 127: Table of Contents - sumit jangidsumitjangid.com/Uploads/Books/.Net/The Little AspNet Core... · 2019. 9. 21. · Node/Express, Spring, Ruby on Rails, Django, Laravel, and many more.

Thefull,detailedchangelogisalwaysavailablehere:

https://github.com/nbarbettini/little-aspnetcore-book/releases

1.1.0(2018-05-03):SignificantlyreworkedtheAddmorefeatureschaptertouseMVCthoroughthewholestackandremovetheAJAXpattern.RemovedFacebooklogintosimplifythesecuritychapterandstreamlinetestinganddeployment.UpdatedtheDockerinstructionstoreflectthelatestbestpractices.Fixedtyposandaddedsuggestionsfromreaders.Thebookalsosportsanew,improvedcoverdesign!

1.0.4(2018-01-15):Addedexplanationofservicecontainerlifecycles,clarifiedserverportsandthe-oflag,andremovedsemicolonsafterRazordirectives.CorrectedChinesetranslationauthorcredit.Fixedothersmalltyposandissuesnoticedbyreaders.

1.0.3(2017-11-13):Typofixesandsmallimprovementssuggestedbyreaders.

1.0.2(2017-10-20):Morebugfixesandsmallimprovements.Addedlinktotranslations.

1.0.1(2017-09-23):Bugfixesandsmallimprovements.

1.0.0(2017-09-18):Initialrelease.

Conclusion

127


Recommended