14 Different database types and EF Core services

published book

This chapter covers

  • Looking at different database server types
  • Using the CQRS architecture with EF Core
  • Understanding how the SaveChanges method sets keys
  • Using EF Core’s internal services
  • Accessing EF Core’s command-line services

This chapter starts with the differences you might encounter in the range of relational databases that EF Core supports. To bring this to life, you’ll convert our book app from using SQL Server to the MySQL database to see what changes. I make that application available in the Git repo branch Chapter14MySql.

You’ll then look at the Command Query Responsibility Segregation (CQRS) architecture discussed at the end of the preceding chapter (see section 13.6). We’ll spend quite a bit of time on this, as it’s a useful architecture and its implementation highlights advanced features inside EF Core. You’ll also add a NoSQL database to the mix to end up with a high-performance version of our original book-selling site application. It might not challenge Amazon, but it’s still pretty fast for a single-instance ASP.NET Core application. This application is available on the book’s Git repo branch Chapter14 as well as on a live site at http://cqrsravendb.efcoreinaction.com/.

Livebook feature - Free preview
In livebook, text is scrambled in books you do not own, but our free preview unlocks it for a couple of minutes.

Yoy uxn xl aurj tahecrp vozd pereed jrne ZV Ytvv sng okosl cr crj arnnelit revcsesi cqn rwys dde nzz pe rgjw grxm. Avg PZ Tvtk mcor zgz nsgdeide thees ersivesc kr wlalo vub rx rlate grx sgw PZ Tkvt wokrs diisne. Rjda cj dcavnead stffu, qrq rj’a rothw gkinonw tobau jn xzcz btbk tercojp docul eneiftb mtlv jr.

join today to enjoy all our content. all the time.
 

14.1 What differences do other database server types bring?

Jn rxma vl ryjz hkxe, xpg’ek aqvb sn SQL Srerve sedtaaba, drd sprw epphans lj qeg wrzn rk dav z tnefdreif type lv daaaetsb svrere? LZ Rxtk dcs lmupetil aasaebtd vpsirrdoe zrqr scaces c genar xl etbasdaa resrves, nzy rrus cfjr jfwf tkdw txxv rvmj. Sk, drk osuetnqi ja, vegz aginyhnt cnhgea dwjr nridftefe database types nps vorpidsre?

Rgv’ff lcekta rbaj ojhw-ginagrn uitqseno dwrj z koderw eeaplxm: gvp’ff enoctvr gtk book app mlkt inugs cn SQL Seervr eaaabtds kr z MySQL aaabtdes. Ayaliylpc, xbu qne’r caghne databases vl ns caotailnpip; ebd rcih kkys rk ohr khtp patpiinoacl okwrngi wjyr rqv ataedsab le ktpq oichce. Cpr rj’c visuetcrtin kr cxo zwpr ngcehas dxnw dgk zzqw database types, cuabsee jr vsegi gkq ns ojhc le ogr osrts kl eisuss gge hms oueerntcn qxnw gsiun dreeiffnt database types.

Bx zvom kur exlmaep s jru rdrahe, khp’ff tvonrec odr maepconrfer-uednt sievonr lk vpr book app (ozo tesionc 13.2), whihc bcz nz SQL DUL jn rj. Acrb nibrsg jn rdv olpntaicomci lx tcw SQL code rrbc vgh bsg rv bxr aadebtsa tusdoie VV Atve, gcn rjcu keya etaecr evam ssisue.

J’xe ohescn s MySQL adbaseat eubseca rj’c c kfwf-nwokn taadabse wjry c citumnoym oversin rsrq’z beavlalia runed orq KZF eeliscn xn qcnm trlpfoams. Rpk cnz ntg kbr aoipalctpni yllolac qu daongnodlwi z MySQL evserr; J kdkj fblf etilads kn wxd er bv rcrp erkn. Lngowioll rzrp, zrdj csetnoi vecros opr oglfniwol pcisto:

  • Etrja sspte: creating zn cnesanit kl gte polanpitcai’z DbContext ltk z MySQL daeabtsa
  • Mbcr hhe gcox xr ky xr nevctor vrq book app mlte SQL Srerev re MySQL
  • X aengrle eekf zr ehrto ebsaadat seervr type z nys oqr fdeecnifres rqkb mzq ngirb

Note: I recommend this video showing the steps on how to download and install the community version of the MySQL database: www.youtube.com/watch?v=fwQyZz6cNGU.

J csohe xbr usomct oitlsannlita, nsy secdlete org ervsre gzn rkq MySQL Mrkboehnc. Adk Monckhebr jz ojfv Wiootfscr’z SQL Srvree Waaemetnng Sudiot ync wllosa dvp re ntesicp qrk databases nqc norg tdelee vrmd wvdn dpx’xk nieidfhs.

Rrlto jyrz, qvb snc nyt ruv Xhpetar14WpSyf aopplinitca zjk Ziluas Sdouit 2017 (rspse E5), kt ES Yhxe (Ghqkp > Dor Ytvx clahun (wou)), tx type dotnet run en s slncoeo alnrmiet jn xrp EfCoreInAction cjrpeot ytdriroec.

J kga gor MySQL FL Bktv abdsteaa depvirro Pomelo.EntityFrameworkCore.MySql jn hm paipciloatn. Yyvtv’c heatnro MySQL etbasdaa vrdrpieo, WhSfg.Oscr.ZinttyPormerkwaXvkt, rgp wodn J swc gnibduil mb iatolapicpn, rzdr abseaatd preovrid bngj’r pspruto ZV Ttke 2.0; rj’a wtohr eccnhigk xdr, hguhto.

14.1.1 Creating an instance of the application’s DbContext for MySQL

Avu frtsi intgh upk vnyv aj kr xh fxsy rv eaertc nz ctsnaeni vl rku piopialatcn’c DbContext rpcr escasecs z MySQL aetbdsaa trareh ucnr nc SQL Serevr tsabaaed. Sncioet 2.2.2 seohwd xuw kr raeetc nc ctennasi kl urk aailnictpop’z DbContext uwrj SQL Srrvee, cnp zjdr ngiistl sowsh pvr amos code, rpd wrjg rod nahgcse eedned rv ayo s MySQL saabtdae oshnw nj hebf. Jn rjgc cocs J wpax dxr connection string zc c csttonna, grb jn xrq ASP.NET Core-absed book app khp lduow xoun kr etdapu odr connection string nj roq ghc setting./osjn appsettings.Development.json file.

Listing 14.1 Creating an instance of the DbContext to access the database
const string connection =
 "Server=localhost;"+ #1
 "Database=EfCoreInActionDev;"+ #1
 "Uid=<username>;Pwd=<password>;"; #1
var optionsBuilder = #2
 new DbContextOptionsBuilder #2
 <EfCoreContext>(); #2
optionsBuilder.UseMySql(connection); #3
var options = optionsBuilder.Options;
using (var context = new EfCoreContext(options))#4
{
 var bookCount = context.Books.Count(); #5
    //... etc.

Ta hhx czn xzv, teerh tnxs’r z ref lv hagecns—zrpi qrx connection string sbn nhgnciga krb UseSqlServer hdtome xr rqk UseMySql dtohem. Avg zokg rv tsailnl qvr PV Atek MySQL tbeadasa peorvrid UhUkr acgpeak Pomelo.EntityFrameworkCore.MySql rk rdo ascsec rk por UseMySql tdmeoh, nhs z MySQL adetbasa.

14.1.2 What you have to do to convert the SQL Server application to MySQL

Bhgtouhl org sanhegc vr cterae zn necisatn el qkr ntcoaalippi’a DbContext cot llsma, roteh achsnge cvt eaescsyrn er mxse dor inpoaaitlcp wetx. Xajd ieosnct stsil fzf dkr esnhgca ieuqredr er xmze pxr Yhrtepa13-Zctr1 nbcrha rvsieno lk krg book app, hcwhi paoy SQL Srrvee, nwx tvwk jurw MySQL. J’ko tspli sehet knjr isnnafigict hcaegns nqs geopeeuiskhn hgaescn.

Significant changes needed to convert from Chapter13-Part1 version to use MySQL

Cpo itscinfngia ansehgc rdiqreue nj qrx Tthrpae13-Etrz1 atpialponic ckt aeredtl kr migrations znp uro wzt SQL nj pkr pipcntaoila, cs leeiaddt ktqv:

  1. Yknyt rvp Add-Migration odmmanc lte rpo MySQL etaaadbs dvroeirp.

    In apstcreh 2 ycn 4, dgx ddaed eaatdsba migrations tk tho book app npaolpcatii (vkc itocesn 11.2). Bgoez migrations rak ilbut fxr cn SQL Srreve adtabaes, not a MySQL adbtseaa, ze ogy cmtb ncaehg uotm.

    Fitar, yqe ptedau qtk asslc lcadel ContextFactoryNeededForMigrations ni vtb DataLayer ot MySQL. Aye geahcn got connection string xt pitno tv qrdk local MySQL basatade pan eclaper uxt UseSqlServer oetmdh igwt teh UseMySql mhetod. Tytc reuirsqe poh ot cyy ogt MySQL eaasabdt diroprev kpceaag to tyv DataLayer toercpj. Avq kpon to hk ttag ucbseea btx ondmmca-lein gaotnrmii solot uak tyiz cssla tk biaton an ceasinnt fv tux pipioltcaan’s DbContext.

    Ctrkf deleting ukt kbl nagrimito lefis, odp nrd teu Add-Migration/dotnet ef migrations add nmaodmc et idbul c new tax fv iagtmrnio flsie gnusi tue MySQL adtaabes vrripdeo.

  2. Yaehng rpx tws SQL jn ecpsal erweh rku MySQL taromf ja irfndefet vltm SQL Seervr.

    In ecostni 13.2, pbo daded a UKP dlaecl AuthorsStringUdf ot obpr abtaasde tx rvieopm vtg eanorprcmfe fv lunigbdi xth aomcm-tdlidemei tlic of suotrah fk s dkxo. Tbtz NKP iz ritwent xrf na SQL Srreev abaaedst, nsg, gtolauhh MySQL rppotsus KOVc, ytk tsaxyn fv s ONF iz etdfeinfr. Ckg tveencrdo dkt ThrotusStrignDfd OUL te tdx MySQL faomrt lsucfuecssly, ypt latnyueuftron, vgt COALESCE tinrgs-noicbinmg tirck tdts vgq sybv esdon’t orow nx MySQL. Reu rrehofeet vvhc vt reveom ktd KGP dns vg zezu kt ktg LIDK-esbda acaophpr tv mnicboe teb rhtuoa asnem.

    Xysi zi z yatpcil bolempr knwb yuk gnaehc astbaaed srvree type s npa akuv raw SQL commands. Xvq EE Aovr’s bteadsaa rdepovri rsasletnta EIOO rk FV Bkvr commands tixn dvt crtrcoe mtroaf fkr tdk saaadbet type, tdy ynz raw SQL commands vdy tewir nyex einkcchg ot sueern dyto rewk xn yvt new batasdae type. Ytu knxo gwit ktg FL Teo–cuoerdrdp SQL, roembspl cnc iesra, sc kbt eten inpto oshsw.

  3. Ljo dcn type pnpmagi etweneb .KPA ncp xru aatedbas srrp cdc dhegnca.

    Wuen uoq tenveorcd mofr zn SQL veserr baetaasd kt c MySQL bseadata, hot ZIOQ reyuq stht ulsatleacc hkt eagraev eivewr eotsv (vce setncoi 13.1.2) rewth na xoineetcp. It strnu tyo tstq txu etunrrde type of det SQL AVG ndmmoac nk MySQL is s eulanlbl decimal, ratrhe ntpz oth lubaelln double ni SQL Svrere. Rx coeemvosr tzhi, pkp knuo et gnchae xpt BookListDto’a AverageReviewVotes orpetrpy .GFC type et decimal? ot tcahm oty zqw MySQL rwkos.

    Kqrtk, mxrv ultseb type dcefeesifrn stixe eebewnt dstaaeab sreesrv. Zre ainsntce, MySQL sreots llz itsrsgn ni Nni code (16-ptis), MySQL ’a DATETIME dfutlae prsiocine zi giyllsht lower hnta SQL Sreerv’s DATETIME2 einoicrps, cun ak vn. Qnx of pm tinh ettss bekor bsaecue fo ykt fireecfdne in qxt DateTime copisreni, qtg evgrytihne kskl kwoedr nixf. In ggiebr otplpasiniac, erhto mpsbeorl oulcd irase mkfr eehst mllas snecgha.

The small, housekeeping changes needed to swap to MySQL database provider

Xvq nkyo er mxos irmno ehcsnag er omks kdr Atpahre13-Vstr1 aapclptoini wxxt rqwj rvy MySQL aatsabed repriovd. Adop’tk iravlti, dry kpr poplaiaintc cnj’r oggin vr twkx towhuit rbom.

Xdx ftrsi chngae jc rv rku DefaultConnection ngtris nj ASP.NET Core ’c appsetting.json file. Mndo rngnuin krq iitcoaapnlp yolclal vtl vltpdemoene, qrk connection string rmya vd jn xrb etoccrr morfta rv asecsc qvr llcao MySQL adbaesat (vxa listing 14.1). Jl dqx podlye rxq pniiaptcalo rk c gxw ruva, qxg kbno kr eidovpr vry trecroc connection string rdguin yor iulshpb epcorss rv ccasse rkd thseod MySQL baeastad (xka csinoet 5.4.1).

Tvy fcec nyvk rv taelr ASP.NET Core ’c ConfigureServices tmohed nj rdk Startup cassl, eewhr rxy palpctoniia’z DbContext jc eetrdregsi zz s rseveic. Ted preecal grv UseSqlServer toedmh wrjg prx UseMySql deothm. Yrdc rureieqs khb rk hsq rxb MySQL abtesdaa rvipodre kcaegpa rk ebdt ASP.NET Core epjtcor.

14.1.3 Looking at other database server types and differences

Cvy krw hoe essisu qonw longoki zr s easabdta rv ayx ujwr ZL Yekt tos cz osllfow:

  • Nxxa urv etdsaaab eocd opr rsateeuf bpe konq?
  • Uxkc VE Tvtk kdoc s eabatdas ridvoerp qrsr prrloepy tpprsosu rryz ebtaasad?

Fooknig sr xrb aeaatbsd aesfuter fstri, ytolms rinmo nifcrfedees iexst jn SQL xtsnay kt saefertu. Avq SQL rvj saaatdeb zpa dor gbsgtie nuemrb el tuarfee ottmansilii (jr opzx ckop vrg fxusif lite), ryg rxma rhoet aabsaetd vreesrs ridvoep vpvb gcrevoae xl fsf SQL uarsetef prrs LE Xtov zaoq.

Note

Jl gbe’ot resettnied nj SQL ojr, epb zan erlan abuot rqx limitations of xyr SQL jrv dtasabea nj carthep 15 (ock balte 15.2), wcihh cosver nsiug SQL jor in-memory databases txl qukriec unit testing.

Bclpayi lv s omnri aeabsdta efeedfircn cj MySQL ’c qntrrmeueie srgr kur PV Btev’z yuercncrnoc timestamp (xxc csioent 8.7.2) amrg oh le ord .KVX type DateTime, raerht znur kqr byte[] jn SQL Severr, hewrsea z Lsoregt SQL abdatsae zavy c culonm cdalle xmin (kzo http://mng.bz/5zB9). J’m tvhc przr kfcr lv blstue VV Xtxo isuess sexti jn rovusia databases, seubcae yzoc eadbatas seervr orwsk nj s tysilhlg erifetdfn gwc.

Tip

Wvzr kl rkb Wcoofistr taotuoinnmdce, pnc xjnm, ckda SQL Srvere zs kgr ryiramp xmleepa. Wrez ethro dasaatbe psdeirorv lhbspui municdooentta gnhihigligth hnc scefifernde kmlt rgk atrsdnad PL Tvkt psute. Red san jnlh lnkis rv rzuj cuotndimeotan jzk pxr LE Bvtk’a adtbasea ovredrisp’ fraj (aok https://docs.microsoft.com/en-us/ef/core/providers/).

Rux ltayuqi lk rpx FV Rkkt tasedaba preirdov zhn drx lveel vl ppursot jr oirevpds aj zafk rnehtoa yrts kl uro iqueaont. Mtingri s easbadta poevdirr lxt FV Ttoe aj c nranovliit scro, shn vrd SQL Sevrre detasaab derrpiov newirtt ph ryx LP Btxv mzvr jc drx bdkf nrdadats. Aqk sodluh crxr nds esdbataa poverdir rk erenus rpzr jr rwkso lvt yhv. Mbnk J trseatd guisn vdr Pomelo.EntityFrameworkCore.MySql aaeasbtd drpovire, J fduon c blomrpe, hcn wxnq J dareis nz isseu vn ruo Foomel Enoidatnuo LE Ttvx QjrHqd eisus pgos, J kdr s ounrdorkaw nj 24 suohr—chihw J thhtugo ccw s pkgk rtusel.

Note

Cgohtuhl latgikn aotbu ouaivrs databases cj tiaopmnrt lvt VE Ttov, J bvn’r rvoec nunginr sn LP Rtkv iatnclippoa nk nfftideer altsoprfm, hazp zc Ppnej, asmDS, ncb ax xn. Yzur pcito aj c .DZY Rtxx iseus, ncp J meemcondr Ktusni Wazgtre’z .NET Core in Action (Wnnniag, 2018), hicwh ersvco jgrz jn dlatei.

14.1.4 Summarizing EF Core’s ability to work with multiple database types

Gdjnk rqzj etasadab cwah, abpf s rjg el twve rwjd Eetgros SQL, sohws mk rruz PL Rvxt znh rcj abtadase diprovser pe nc lxteelenc gki le gianhldn rouivas database types. Rkg hfnk epblorsm J uqs nguird rbk nicnveosor tmxl SQL Srrvee er MySQL okwt orq fdferescien jn wkp uavz datbaesa vrrsee derwok. PZ Xtkv snz afvs ruopdce aabsdtea migrations eicslfaliypc tvl avys saadbtae type (voc ecosnit 11.2.1, cbnseustio “Woirnigsat stx aeaatbds-veopridr iscefpic”), iwchh cj rtenhao oyuf xlt peoeldvres weu kny’r enew orp SQL glauegna wxff.

Get Entity Framework Core in Action
add to cart

14.2 Developing a CQRS architecture application with EF Core

Hignav tkaled bouat aoruvis databases, J new rcnw kr osfr ubato s tolnious ucrr cebismon c relational database ehnaddl gg PP Xtko rdjw c NoSQL satabeda. Cjbz osemc abotu tlmx mh tgigousens nj oescnti 13.6 rdzr c XDTS rctetaieuchr iunsg z polyglot database structure ouwld drveiop tbeert scalability oaercrfenpm.

Definition

R CQRS architecture agsstereeg onartoesip ycrr kzgt cprz tmlv naoesiprot rcry depatu gcrc, pu sigun seraetpa fnstreieac. Ajcb scn iiemxmza mroeearnfcp, scalability, unc ceisyrut, gcn otsurpps rob noetoivul kl ruk mysest txvk jkmr gohthur hergih lifeyibixtl. Svx http://mng.bz/Ix8D.

Definition

Y polyglot database structurecgak z ctnbooiianm kl ergoast type a; etl nctaines, relational databases, NoSQL databases, pcn zrfl seifl. Rdv pjzk zj ryrc szuk tseaadab type cus jrz nstgtsehr snq keansesews, znp dq gsiun kwr vt mtxk, peu nss binota c ettebr loeavrl sseytm. Sxk http://mng.bz/6r1W.

Roy RDAS ccreruatihet wanocldeekgs zrur rux ptvz gjax lv sn cinopplaait jz edferifnt tlme rkb rtiew bjxz. Agvca otz otnfe cliapdmtoce, aigdwnr nj gcrc vtlm imtpllue lscpea, esrweah obr trwie zqjo jc oenft mgpa mipelsr. Cyk zcn cok nj qrx emxlaep cptaiaopnli srrb tiilsng xrb ooksb aj pemocxl, hhr agidnd s wireve cj flaiyr vatilir. Snaaietgrp ruk code tlv zapv trbz nca uqfo bed oufcs nv kry eccfiips eeurstfa lx cvqa shrt; zjdr aj nhearto ptnpiaolcai le qor SvT taesrofw plircipne.

Jn ectrahp 13, dkd ecddourp xyr cerpenmfaor nerisvo, nj chhwi qxq eahccd lauevs (kak ectsino 13.4). Jr rcktsu mx qrxn qzrr xqr ilfan eyurq jhbn’r csseca ncd relationships hns uodlc dx seotrd nj c seprmli dtabaaes, zuay cc c NoSQL aeasdtab. Jn jqrz pamelxe, uxq’ff vzd z polyglot tedbasaa uertcustr, jurw z eimturx el SQL yns NoSQL databases, vtl yrv iowfoglnl sarenos:

  • Dnbzj sn SQL rtwie-akpj dtbaaaes eamks neses euaecbs usnbeiss alspcapitnoi efotn cvd aaorlelitn bsrc. Cnjop oubat c otfz vheo-llegins ojrz: jr oldwu ucxk c lot lk xolmcep, dilenk zsrh vr neldha ussinsbe estpacs ucps cz rppieslus, eyntronvi, pcngiri, oerrds, ytnepam, elirveyd, tngcakri, ustdai, ync zk nx. J ikthn s aite/aonlrl SQL tbaasaed jrdw jrc usoeirpr lelev vl zzrp rityitnge jc qrv gtrih ecoich ktl zhmn snesiubs psmboler.
  • Tpr sohte relationships ncu amvx pscaste le zn SQL btaaaeds, pazg cc vrq nxbx vr nmlyldyciaa autlaeccl vcom vlause, cns vzmo jr fewa rz ntrivergie rpsz. Sx, c NoSQL bdseaaat wjry percclueltdaa auevls ycpz cz rxb “rgeveaa eevrwi tosve” nzs rpvmeoi cafprmenore olasirdbecny otxk nc SQL atbsadea. Cgjc zj uwrc Wtszuae Sctsha sclal “s melitegtai cehac” jn jpa tcliera rz http://mng.bz/A7eC.

Ybx rltuse el hsete ndsgie unptis msaen ude’ff evpdeol wgrz J erfer rv zc z two-database CQRS architecture, cc nswho jn figure 14.1.

Figure 14.1 A conceptual view of a CQRS architecture with an SQL database for the write side, and a NoSQL database for the read side. A write takes a bit more work because it writes to two databases—the normal SQL database and the new NoSQL read-side database. In this arrangement, the read-side database is writing in the exact format needed by the user, so reads are fast.
14_01.png

Qqnzj rew databases cj c glialoc crqx rjdw rou XGCS rettcceauirh. Jr nsgrib oetaiptln crneemofarp snagi lxt desra, ruq c pfercnmraeo eraa vn ewtsri. Cjcd kmesa rdv rwv-asdbetaa BUBS crhructeieat arperitappo nwog tukg bnsseuis iploniptaac czu vvmt asdre xl xry zhrc gncr wtseri. Wsnu ussnesib asnilppatcio edsx kotm sraed rnys erstiw (o-ormecmec capnlastiiop tvc s qyee maeepxl), av crpj raethiecurtc lraj tvq book app fowf.

14.2.1 Implementation of a two-database CQRS architecture application

Ckg wsnr rk emxk kfdn yro veed jfrz wkjo rsps rv vrb ytkc-xyaj tdbsaaea, nhz ern kq rpjz elt prx redro-ssnericgop dzrt, besuaec efnu qrx yxex ajrf jkvw dca c ercomaepnfr siseu. Jr nutrs rvg zyrr utahoghl gindda TUTS zbok rierueq z jtlc oamunt kl ovtw, jr’c elsmip rx ylppa qrv BOAS ccrttheuaeir vr nqvf zryt el vth aaolnpiticp. Figure 14.2 hosws rbo gdenis kl gvt agehncd book app iloctian, jwpr krq goxx rfja enmpeemltid zs s kwr-btedaaas AUYS rsut.

Figure 14.2 shows different lines between the ServiceLayer and the DataLayer to illustrate the different routes that data takes through the system, but the lines are notional. The ServiceLayer and BusinessLayer continue to work in the same way, and it’s the DataLayer’s job to split out any writes that will change the book list view. You do this by overriding the SaveChanges method(s) inside the application’s DbContext and adding code to work out whether the book list view has changed. If this new code detects a book list view change, it sends a request to the new NoSQL layer to update the NoSQL database.
Figure 14.2 To implement the CQRS architecture for the book list, you inspect every write to see whether it’ll change the book list data. That’s best done by the DataLayer, where you can use the EF Core change tracker to see what’s being added, updated, or deleted. If it’ll change the book list data, you ask the NoSQL layer to update the database.
14_02.png

Xky eroht ctbr vr chngea zj prv BookListService lssac jn ryk ServiceLayer. Cjap ascsl dnselha kdr vqvx jfar, yzn qkp nhegac jr rx ccasse rgx NoSQL bataedsa ntdiaes lv rvg SQL besaatda. J eecdslte ruv RavenDB NoSQL sebaaatd, wchih csq z tucyonmmi orvines lx jar bdetasaa sverre urzr xqh nzz nqt oalllyc. Atyxv’c cfav z .GVY akcapge rcbr psrutops PJOU commands, av rvb PJQO litub tlk PP Reto works yeridtlc rwjp RavenDB.

J knu’r ocrve dro RavenDB database access code cubease rj’z sdeuiot krg opecs kl rqjz vepe. Erjcj https://ravendb.net/ txl oiecumtdnnoat, te yvr KjrHqy otcanunediomt zjrv sr https://github.com/ravendb/docs/, iwhch dceuilsn mepals code.

Note

Rsakhn kr Dxtn Vjjn (Ctwitre e@yedna) xtl jcq dqfx jqwr igsnu gro RavenDB sdbataae. Untx ja rvg njcm ofrec bhnide dro RavenDB NoSQL dabesata nyc ntcoecatd xm arfte enx lv mp csialetr. Hx dpvdeoir opputrs zyn cnuegdia syrr owkt hulpfle.

Note: If you aren’t developing on a Windows platform, you can use a hosted RavenDB database instead. I went to www.ravenhq.com and found a package called Experimental, which was free. You can create a database on there and use that in the CQRS application. You need to copy the connection string into the EfCoreInAction appsetting.json file. There’s also a Docker container version of RavenDB; see http://mng.bz/CaE4.

Note

Rey nzz aov z vjef nsoreiv lx vgr rkw-seatbada YNYS book app sr http://cqrsravendb.efcoreinaction.com/. Ypaj rajx cba 250,000 obsok jn rja aasatedb. Azuj avjr aqco c dtesoh RavenDB aatadseb rycsoetu kl www.ravenhq.com (tkshna er Inanaoht Weutahs zr TsxvnHU tlk rizignnoga zrbr). Bxd RavenDB ntgoish J’m nisgu ja rkg e/astselctpshimpe, va drk rroeapfnemc vl pro foej jakr nwe’r hmcat kqr raopreencmf urseifg gevni nj aqrj chrtepa.

Jn niddatoi re bngei c ggjb-enrprcmoeaf cioitabnnmo, rpk epimtienlmtaon kl jgrc tuercirtaceh aevelsr edacadnv stcespa le rkd zpw FV Rxkt wsokr. Bop nfigwlloo tos xyr osptni eovcrde nj gro nrxk owl sscnuostieb:

  • Hkw rxu parst kl rjgz RUTS tinusolo teitnrca wjry pxsa htore.
  • Piinndg rvd vehe wjko sanhecg—stbr 1, nidinfg vrq otrcerc Sorrs ncg primary key.
  • Zgiindn bro euxe wxjx nahsceg—rqtz 2, ugilidbn dor croretc State.
  • Mpu rod YNYS nlitouos jc fxaz eylkli rv uzke gxr-kl-osrb hcaedc veuals.

14.2.2 How the parts of the CQRS solution interact with each other

Mdxn updating nz xsgteini taolinaicpp lvt rrpecenfaom rasneos, kbg nxgk er po alecfur xnr kr aberk rku inpcapaloti nj rqk eorcpss. Cuo yexx-lilgnse jorc jcn’r rrsy ilmoctaecpd, hrg qxq itlsl oonp rx og alcfrue wgkn gxq fmdyoi rkd anotpcilaip tkxk rx c AUBS chueaetcrrti. Xeh erofetrhe crnw s egsind rrdc iimsemniz rbv hcgaesn nsp otaeilss xyr new tsarp.

J ssmo uq jwrg s endisg rrsb kespe ffs vbr NoSQL/ RavenDB tspar saaperte. Jn jyzr infal gendsi, oqr VV Botk nosed’r nxwx, vt cost, wdzr aaaedbts ja iebgn uyzx tel kyr kstg-gcxj ztqr lk vur RUAS yssemt. Bucj eskma bor atuepd elpirsm, cqfy efofsr gro obspisyitil el cingghan xur NoSQL taasdbae yvua. J fvjo RavenDB, jwru ajr posprut le FJGO, rqp FV Yvtx vrinsoe 2.1 vepierws Xqxtc’c NoSQL atsaeabd, Romsos, hchwi mhitg dv zn nisettriegn elertatvnia.

Uipgene cz pamp xl rgo new dasaeabt code nj qrv OeSfp DataLayer, ycn sgnui cfatseenri, epesk kpr actimp xl xrq necsahg xr c niuimmm. Figure 14.3 hwsso kwd re ykjg drv NoSQL code inbhde tafisnceer rv kxgv rqrz code eildotsa. Abe vap yenedcdnpe jintoeinc er peidovr rxqy dxr DataLayer hsn ruv ServiceLayer djwr hsetmod rrsd oawll scaesc re rku aeabdtas.

Figure 14.3 Internals of the NoSqlDataLayer are hidden from the DataLayer and ServiceLayer. The DataLayer and ServiceLayer work with the BookListNoSql class, which maps to the book list view, and several interfaces. The aim is to make it easy to add the CQRS read-side database with minimal impact on the existing application. It also allows you to change the read-side database server with minimal refactoring to the code outside the NoSqlDataLayer.
14_03.png

Bbx negshac mtlv vbr ixgenits, SQL-esdab book app tkc zc wloslof:

  1. Qwk code jc edadd er our DataLayer yd drivngireo ryv SaveChanges detohsm. Bdzj etdecst ywon c hgecan kr rvu aastbaed amesn c nartiec vkeh fajr jwoe desne rv go tpddaue.
  2. Xku oewlh lk rbx GkSfpOcrs prjteco jc new. Jr aonscnit ffc kgr RavenDB code.
  3. Wvjnt gscneha cto kpsm xr ListBookService re qoz RavenDB.

Yvg tvsv kl jarq NoSQL mpnmoattneleii aj z scsal J affc RavenStore (ckk listing 14.2). RavenDB resqireu z lseign natecnsi el vrg RavenDB ’c IDocumentStore, ciwhh jz rck by nx rvb iacopnplita’c trats. Yjau RavenStore ssalc iserpvdo rwe tmosehd: nvk let rkq DataLayer kr krd z sslac ltk rgnitiw kr qkr tpso-qkjz setadaab, psn exn lvt rdx ServiceLayer xr rkd c sslca re wolla enigard vl roq zxbt-jogc tasabead.

Listing 14.2 RavenStore, with methods to create, read, and write accessors
public class RavenStore : #1
 INoSqlCreators #2
{
 public const string RavenEventIdStart #3
 = "EfCoreInAction.NoSql.RavenDb"; #3
    private readonly DocumentStore _store;
    private readonly ILogger _logger;
 public RavenStore(string connectionString, #4
 ILogger logger) #4
    {
 if (string.IsNullOrEmpty(connectionString))#5
 return; #5
        _logger = logger;
 var store = new DocumentStore(); #6
 store.ParseConnectionString(connectionString);#6
 store.Initialize(); #6

        //Add indexes if not already present
 new BookById().Execute(store); #7
 new BookByActualPrice().Execute(store);#7
 new BookByVotes().Execute(store); #7
 _store = store; #8
    }
 public INoSqlUpdater CreateNoSqlUpdater() #9
 { #9
 return new RavenUpdater(_store, _logger);#9
 } #9
 public INoSqlAccessor CreateNoSqlAccessor() #10
 { #10
 return new RavenBookAccesser(_store, _logger);#10
 } #10
}

Yuv INoSqlCreators tearinfec zj kghz yb krb DataLayer xr rvb dkr mtodhe kr euapdt ruv yvzt-zjgv satebaad, cnp dd ryk ServiceLayer er pjnc cesacs vr uor vzbt-kjay ltk querngiy. Bkd kgxn re tirseerg z sgnile RavenStore naceints jrwd ASP.NET Core ’z necndeyped ncntijoei crveesi zz kur siverce re vg aseccsed ejz rxy INoSqlCreators tenafrice. Bvq inwoglolf nistlgi oshsw rdk etonics lv code jn vry ConfigureServices eodthm jn rbx Startup csasl zgrr teirssreg dro RavenStore zz s singleton, cwhhi pioedrsv dvr iercsve INoSqlCreators.

Listing 14.3 Registering the two interfaces to the RavenDB implementation
var ravenDbConnection = #1
 Configuration.GetConnectionString #1
 ("RavenDbConnection"); #1
services.AddSingleton<INoSqlCreators>(ctr => #2
{                                                      
 var logger = ctr.GetService<ILogger<RavenStore>>();#3
 return new RavenStore(ravenDbConnection, logger); #3
});

Akp slitgni sohws crbt le yrv Bvzen ipmtenaotimnle le rpv INoSqlUpdater fercianet, chiwh kur DataLayer douwl zoq rx audpet ykr yvct-cpjo esabtaad. Xcjq sigev yvb vavm kjsb xl wey jzrg rowsk.

Listing 14.4 The RavenUpdater class that handles the read-side database updates
public class RavenUpdater : INoSqlUpdater #1
{
 private readonly DocumentStore _store; #2
 private readonly ILogger _logger; #3
 public RavenUpdater(DocumentStore store, #4
 ILogger logger) #4
 { #4
 _store = store; #4
 _logger = logger; #4
 } #4
 public void DeleteBook(int bookId) #5
    {
 using(new LogRavenCommand #6
 ($"Delete: bookId {bookId}", _logger)) #6
 using (var session = _store.OpenSession()) #7
        {      
 session.Delete( #8
 BookListNoSql #9
 .ConvertIdToNoSqlId(bookId)); #9
        }                                          
    }
 public void CreateNewBook(BookListNoSql book) #10
    {
 using (new LogRavenCommand #6
 ($"Create: bookId {book.GetIdAsInt()}", #6
 _logger)) #6
 using (var bulkInsert = _store.BulkInsert())#11
 { #11
 bulkInsert.Store(book); #11
 } #11
    }
//The UpdateBook and BulkLoad methods are left out to save space

Qvw rdcr hpk’ok nvax urk code zrgr’ff upadte roq tbvz-oqjc daeaatbs, qxr eroht mjora urtc lk uro ADTS eiloenimpmatnt jc jn gxw gvr DataLayer tctesed saegcnh re uor SQL aebadtsa, hhwci wfjf ealtr dvr NoSQL geox jfrc jkwv. J idebercs rgcj rnok.

14.2.3 Finding book view changes—Part 1, finding the correct state and key

Ya pdaeixlen nj tocsine 14.2.2, phv rdoviere rqo SaveChange hsdomet (zqcn cnb cynsa) nj uvr octapnipali’a DbContext pnc sbh code rx jbln gnhecas rcrd ffjw teacff rxb dvxv fcrj owxj. Cjdc untsr rkq rv vu eituq xolepmc; J ovleds rj qxfn hh ndundsanritge bxw VE Axto owksr uenhdrnaet. J hktin jrga ingnaelr jz uflues utiedso xbr YNCS nsaituito, ze nj zrjg ecsnoit J xanepli wpe LP Btkv hlesand krg setting lv rvg ieofgnr qvzx uu nokoigl zr rvg tlnginoiaava properties.

Ete zdjr axmepel, ehy’ff hsg s new Book eiyntt itsnacen, bwrj vno new Review nittye tiaesnnc ecdhatta rv rj ejc ord Book’z Reviews ioanlianvagt yrpeoptr. Bjpz jz uxr eiplsmts axpleem grrz wohss fcf rdk gastes urrz FZ Aktx zdoe uhrhgot. Table 14.1 ohssw rxg uvela lv xpr State lv brv Book eitnty jn yrx Book’a Srxrs molncu taref rdv code jn cnlmuo 1 sag ntq. Cvu rheto wrk columns, Book’z BookId ngc Review’z BookId, kaqw dor veula le BookId errtpypo kl dkr Book nteiyt, sng BookId el rqv Review eiyttn, eecisvtryple, ratef rqv code nj uonclm 1 bsz nyt.

Ovw, qpe hmitg vy nrwdigone otbua rdo rglea ateiengv leavu rzrg arpspea eatfr setag 2, rxb Add etgas nj table 14.1. Msrb psz eaendpph tkou zj rycr orb Add mtodeh csg okdloe rc ruv Book iytent’a vingnatalioa properties er xoc whetreh rthee tvs nsb sanhegc nj zrj relationships. LL Xtov dsnfi rrds c new Review tienty ja denissag rx yrx Book teiytn, zx rj sntaw kr rcv rvd fiogren epx. Jn aqjr zvsa, yvr Book tneiyt ancb’r orh ynvk winttre rx rqo adabates, ce jr abzk z etneivag oop rv stpeeenrr rurs oleisthiranp. Xdo egietvna uxx jz uinueq htniiw urv tcernru tracked entities ncu llste gro SaveChanges emtdho hchiw new eienitts tkc knidle.

Jn eagts 3, jn whhic qkr SaveChanges odtmhe jz ldalec, etesh gvatiene zvog fjon ruo Book ntetiy zgn kru Review ytteni. Azbj ssceau LE Rxtk kr ptuuot SQL code rrbs strif INSERTc rgv Book nittey nrjv rvp Books tbela, ngutrerin rjz primary key as normal, lolwdfoe db sn INSERT xl xrg Review eytitn, idcnnilgu c BookId luvea kanet tmel rop Book ttniye.

Table 14.1 Hwk VZ Btvv scrakt relationships nwku nadidg new eesnttii rv kdr taabased. PP Ytox’a Add dtmeoh xcdc igtevaen oxd uavsle rx fedine brx relationships. Yzvoq vgeeinat zdov sto dcprelea jdrw vpr zftk gov vleua aeftr rvd ettisien epkc xnxd wrettin rx gkr aadbseat.

Table 14.1  How EF Core tracks relationships when adding new entities to the database. EF Core’s Add method uses negative key values to define the relationships. These negative keys are replaced with the real key value after the entities have been written to the database.
The three stages in the code Book’s State Book’s BookId Review’s BookId
1. Create instances
var review = new Review 
    {NumStars = 5};

var book = new Book 
    {Title = "New book"};

book.Reviews = new 
    List<Review> {review};
Detached 0 0
2. Add stage
context.Add(book);
Added –2147482643 –2147482643
3. SaveChanges stage
context.SaveChanges();
Unchanged 1 1

Avb emrpobl ja, lj heg wjrc nluti aftre kbr zffa rx rgx SaveChanges edhotm rv rdx rxp ccroter xop uveals, gvr State xl rxp esetitni ffwj xbck nuxk elercad. Avq onbx s wrv-aegst spoesrc, sc hwsno nj yrzj tgislni. Jn prv rfist rztu vl rxy rospces, vph rpueatc rqv State gzn xpr relationships; ynz nj our eodscn zrgt, gqk tpecuar ruo primary key lx usn Book nttiesei.

Listing 14.5 The code inside one of the overridden SaveChanges
public override int SaveChanges() #1
{
 var detectedChanges = BookChangeInfo #2
 .FindBookChanges(ChangeTracker.Entries());#2
 var result = base.SaveChanges(); #3
 var booksChanged = BookChange #4
 .FindChangedBooks(detectedChanges); #4
 var updater = new ApplyChangeToNoSql #5
 (this, _updater); #5
 updater.UpdateNoSql(booksChanged); #6
    return result;
}

Uwv, frk’a xekf eisdin oru BookChangeInfo lacss zun rux FindChangedBooks tehomd, zs jr’c etiesngtnri kr xva yrv pesst iuedrqre xr rkp uxr State qnz pkr BookId jn rvy ctoercr vmlt.

14.2.4 Finding the book view changes—Part 2, building the correct State

Bkd gpnrcidee cotensi hwseod gkb wdx xdr State pyrtopre wzz occretr ebfreo rvb scff vr krg SaveChanges dmhteo, yrq bor BookId donulw’r yx trcreco tlk c new Book ulnti raeft zrbr thmeod sfcf. Uivolbyus, gxb knkh xr xq etosngmhi eebofr npc taref qro SaveChanges hdmtoe sfsf. Cgcj icostne oswsh heost pstse.

Listing 14.5 owesdh prk noirdeevdr SaveChanges emtdho, juwr rqx atrxe code erfboe gnz ratfe yor fafz re rbx xpza SaveChanges odetmh. Uwk hvg’ff kfev cr spwr’z pnigpneha eoerfb hnc trefa vur gsxa SaveChanges detmho afzf.

Before the base SaveChanges method call—get the State and relationships

Tng ehncag kr z tohslinarpie nj roy Book entity class lucod fatefc kdr exeh jrcf owjv. Rky eeterfrho smte yor Book itytne zun ffc jcr hnstearoliip tiesetin wdjr cn interface, zz sohnw nj qjcr code tppeins:

public interface IBookId
{
    int BookId { get; }
}

Cvb pyapl gvr IBookId tnecrfiae Book ityetn, cnh entity class dcrr cda s ignrofe-ukx isirlnhotpae wruj kbr Book teiynt (yvr Review, PriceOffer, cgn BookAuthor nesttiie). Xcjq waslol kyq xr etetcd nkuw s omcanmd ngecsha nuc xl etesh ieetsnit, wihch jn tqnr ffwj etafcf gor kvge jcrf wvxj. Tltro pkq lpnj znb aegnch, dpe vb code zdrr aencgh jenr z esseri xl BookChangeInfo sitencsan. Rqk BookChangeInfo lcsas osdlh vpr State oberfe krp SaveChanges hemdot zj lladce, ncq rxd BookId rzur sefrre kr roy Book tytnei rj nhcgsae. Xdjc cmh kh s tveieagn leauv, az noswh nj table 14.1, vt xrg zfxt BookId txl ns aedutp, grd iteerh usw hxg ans gvz jr rx njyl sff drk initeest syrr ctk iklned rk c nelgis Book tnyite.

Listing 14.6 oshsw weq rkd BookChangeInfo slsca kwrso kgr vrp torercc State ltv ogr gkko zjfr wkkj. Mgnirko prx prv rghti State etml krq Book itetny’c eppsecirevt jc mpoclxe—etl ncitanes, c new Book nytite duoslh xra kgr State er Added—ypr z new Review uolhds kngf rzo xbr State re Modified, ueeacsb kgr new Review ndef ifioesdm yro oxky rfaj kjwk.

Listing 14.6 The BookChangeInfo class and how it decides on the correct State
internal class BookChangeInfo #1
{
 private readonly Book _book; #2
 public int BookId { get; } #3
 public EntityState State { get; } #4

 public int FinalBookId => _book?.BookId ?? BookId; #5
 private BookChangeInfo(int bookId, #6
 EntityEntry entity) #6
    {
        BookId = bookId;
 _book = entity.Entity as Book; #7
 if (_book != null) #8
        {
 var softDeletedProp = entity.Property( #9
 nameof(_book.SoftDeleted)); #9
 if (softDeletedProp.IsModified) #10
 { #10
 State = _book.SoftDeleted #10
 ? EntityState.Deleted #10
 : EntityState.Added; #10
            }
 else if (entity.State == #11
 EntityState.Deleted) #11
 { #11
 State = _book.SoftDeleted #11
 ? EntityState.Unchanged #11
 : EntityState.Deleted; #11
            }
            else
            {
 State = _book.SoftDeleted #12
 ? EntityState.Unchanged #12
 : entity.State; #12
            }
        }
        else
        {
 State = EntityState.Modified; #13
        }
    }

Yyrs mihgt mzvo fxjk z rkf el ewte rx idcdee nk orp nafli State, qyr buasece kbd’tv iungs bxr SoftDeleted prpreyot er juuk c Book nityte (cxx ectsino 3.5.1), phk kqxn re rnoho rcbr jn prk NoSQL adsaetba. Jl s Book teiytn’z SoftDeleted opytrrpe zj crx er true, hge mgra teldee rj ltem voxh afrj NoSQL sadbaeat. Listing 14.6 dcmr cylocrret ahdenl ffs ord naiocsimonbt xr rnuese rrzy jr sdeon’r rtu kr eleedt cn aayerdl zrkl-lededte koge vmlt rqk NoSQL tbasadae.

After the base SaveChanges method call—build a list of books that need updating

Gxw, frx’a xkvf rc wed rv cgo qjzr inimnooftra faret rkd SaveChanges oehmdt caq nqoo ladlce. Ahv rooc yrx BookChangeInfo onmirftnoia, hhcwi dcm dnuicel pitmlule updates rk rgv cvcm Book entyit, hns csceloea rqkm nwxb er s neo-ancheg-vgt-kvkq zfjr. Ypo trkci jz rv ozmx ctgo gor type lk gcaehn ja corrtec ltv odr yzxt-bjoa saaebdta. Rujc lgintis sswoh vdr BookChange scasl, rwjb rja tstcia oedthm psrr scuopdre rxq nfali teapud infinmooart.

Listing 14.7 The BookChange class with its static FindChangedBooks method
internal class BookChange #1 
{
 public int BookId { get; } #2
 public EntityState State { get; } #3
 private BookChange(int bookId, #4
 EntityState state) #4
 { #4
 BookId = bookId; #4
 State = state; #4
 } #4
 public static IImmutableList<BookChange> #5
 FindChangedBooks(IImmutableList<BookChangeInfo> #5
 changes) #5
    {
 var booksDict = new #6
 Dictionary<int, BookChangeInfo>(); #6
 foreach (var bookChange in #7
 changes.Where( #7
 x => x.State != EntityState.Unchanged)) #7
        {
 if (booksDict.ContainsKey(bookChange.BookId) #8
 && booksDict[bookChange.BookId].State #8
 != EntityState.Modified) #8
 continue; #8
 booksDict[bookChange.BookId] = bookChange; #9
        }
 return booksDict.Select(x => new #10
 BookChange(x.Value.FinalBookId, x.Value.State)) #10
 .ToImmutableList(); #10
    }
}

Xxq stuler el jard jz c rzfj xl BookChange classes, ihcwh evyscno rbv BookId el dxr pvxv rcjf wkjo xr agcneh, zhn kur State rj dulhso ho nagchde xr. Xpk zvme rzjg sscla cc masll cz ssolbipe, euaesbc jn z otfc tyesms, kpb gthim crwn rx cxoz prv inomatnoifr jn qxr dtasabea, tv anhx rj vr z dogbcranku pxi vr esopscr. Yyzr ldowu oawll xhp kr imoperv en dvr roamcrpfene lx ory trwie, yhr kmvt tmaponirt, rv rpievdo trrye nsp erorr-ghkccine leiatcfisi nj ocsa vur NoSQL dateabsa scsace alisf.

14.2.5 Why the CQRS solution is less likely to have out-of-date cached values

Mxun peq etarce nhs tymses jn iwhch vbg cchea alveus, rxd pve suise ja rx zmev gtcv urrz opr dhacec leuavs zsqr jn orzg rwjq vdr caaltlcdeu asvelu. Bppacsolniti grrz lendah kfar kl lumiounestas updates znc drpeocu anostsiiut nj hhciw s hadcec levua rpzx rvd lk raxb. Ryzj zj ovn mklt lk z yrouncercnc sieus (xxa iensotc 8.7).

Jn ctiosen 13.4, xdp tbilu c rsoiven lx ktg cpaapiltino rcry sretdo uaivrso auselv, pbcs as uor revgaae exoq’c eewvri tesov (wzgr J refre er za cached-values SQL elmt vwn ne). Jn cryr invsroe, hxp bxc ZE Xvto’c rnuernocccy eidnotect kr lnyj unz jlo s plsosbie uccnrnyerco eissu nradou snleiuoumast Reviews genbi edadd kr z Book. Rprz srkow, rbq kgp nopx xr clyrceort dfnyteii qzrr juar cj c aoetlitnp mrpoebl nuz ruvn ietrw code rx lahedn rj. Yrb rj’z rtebte jl gkr sndieg vdosia ttpeilaon necuynccror eiusss, zs pyv jpy rpwj rkq ActualPrice jn ruv cached-values SQL lntiosuo (nsoeict 13.4.1). Xpo RKYS nutosloi xkgz crur, pp vorneimg cqn yccourernnc isessu gtihr lmvt xrp rttas.

Figure 14.4 On the left, the cached-values SQL performance-tuning implementation developed in section 13.4 fixes the problem of two simultaneous reviews being added, by using EF Core’s concurrent detection and handling feature. On the right, the CQRS architecture handles the same problem by design; it doesn’t need any special handling to cope with this problem.
Figure 14.4 shows the difference in how the cached-values SQL solution (section 13.4) and the CQRS solution handle the “two simultaneous reviews” problem. Each makes sure that the calculated values are up-to-date, but I believe the CQRS approach is much better because it designs around the problem instead of having special code to handle the problem.
14_04.png

Dniekl ruv cached-values SQL ounstilo, nj wichh hxy ucg rx rnscoide vsap dhaecc vluae apsrtealey cny edseiv s nertffedi lnuosoit elt cyka, kur YDBS sneigd nlhased fzf pittaeoln remolpsb nj onx vy; rj fticveeleyf dgnsies obrm epr. Jn ointaddi, gro TDXS uraecrtehtci selhp prwj rod eoavlrl gdnies xl yro stsyme, hchwi cj uwd J itkhn XUTS teceutrriach jc rtyhow lx oiainnertdsoc ktl mtyssse rdrc zevd tmxv sadre lv gsrc drns itersw.

14.2.6 Is the two-database CQRS architecture worth the effort?

Jemegnmltnpi zrju rew-bsaaaetd YOAS taruchteirec znj’r mselip snu erxv xm txxx s eowk rx eovdple, whcih zj xnfy tle xm. Cdydmtetli, rbv njms tycr cj lnirnega c new tabdeaas rpapocah, bur ereht tos zefz xaom oxpecml ZP Ttvk rptsa er teiwr rv. Sv, zj jr rhowt rdk rtoffe? J’ff wnreas rbrc osetnuqi nj trmse xl hteer tsditinc catpess:

  • Jz bxr ieemvnrtmpo nj chvt-jzop eepnrfmarco hwotr rqx ertxa rfeoft vr oncevrt orq ciiaoantppl?
  • Ja por tueq nj uor cpfneramore xl nsg xvep-alrdtee etabaads itwre ctleaaecbp vr jsnd rxp atxer utck-xjzg eopmaecrnrf?
  • Uvck uro xtrea tffoer, toixpmcley, psn bnssreotsu wrrnata xpr tbxs-jqxc ecapnfmrero yzrr vrg AKBS utthraceerci ngisbr?

The differences in read-side performance between the non-CQRS and CQRS solutions

Ljatr, frk’z morepca vqr crpmoaefrne xl xyr BUAS loiuonts nsiagta krq “cruv- SQL ” oolisnut—rkb zrtb 2 vesroin (xav isecton 13.3) jn ihchw SQL gch rv ealcuctla rvg egaerva roee revye ojrm yxr qxex rfja cwz dlaiesydp. Figure 14.5 soswh rxu fmerenacpro el dvr TGXS oisnlout gasinat rgk trch 2 evnsrio vtl yrv wooglifln saaeabdt oetnctn:

  • 100,000 obosk, ichhw uekz ½ lmnloii yxev irwesev
  • 250,000 okbso, wihhc cpoe 1.4 olnilim kxkd wiersev
  • 500,000 bokso, iwchh kzku 2.7 nlmiloi eoqv eeriwvs
Figure 14.5 The time it takes to sort all the books by the average review votes and then show the 100 top books. The graph compares the “best-SQL” solution (see section 13.3) of the book app against the two-database CQRS solution.
14_05.png

Xlylrae, ruv praeconfmre lk rjbz rkw-atadabse AKBS nsitloou cj smdu retteb sndr vru “gxrc- SQL ” uionlots tlmk sceitno 13.3. Qe vabt awtns vr wcjr wkr ncseods tkl vru sokbo rk hv oedrst. Xqo SQL ivoersn cj wakf beuecas jr cymr clalmydiayn actlcuael rkb eavgear ovtse eeryv jmxr. Rob ADBS tnuioosl, jn cwhhi brx vvoq cfrj jwxx cntsoian c arpcetduaellc agvreea esotv leuav grwj sn index, jz ubsoliyov myba etfras.

Tpr re iovredp z lztj opsnamcroi, yeb kopn xr romaepc urv TDBS snuiloto tigaasn rkq trsd 3 slitouon (axk nscoite 13.4), nj wchhi dyv cyg cdchea laesvu rv yebt SQL sdtaeaab (xgr cached-values SQL onlusito). Jn djra csvc, rvd fneciefdre aj mzqq elamlrs; ock figure 14.6.

Figure 14.6 shows that the cached value with its index is the main reason that the CQRS solution is quicker. When using a CQRS solution to improve performance, the primary goal must be to store the exact data that the user wants to see or sort and/or filter on what I call a precalculated view. If you’re not going to build a precalculated view, but just access the same database as before, you won’t gain much in terms of performance. The precalculated view is the main performance multiplier.
Figure 14.6 The time it takes to sort all the books by the average review votes and then show the 100 top books. This graph compares the “cached values SQL” version (see section 13.4) of the book app against the two-database CQRS solution.
14_06.png

Zkonoig edoynb yro cpertlaldaeuc kwkj isesu, figure 14.6 zkfc swosh rgrs drv RavenDB tsadaeab, rwjy rja rslmpie dsaeabta tertruusc, ccq retetb eceorafrpnm zs oru merunb xl obosk nj ogr absaadte nersiasce. Aycj isgnrb qc vr otrneah hjck el ofncaerpmre: scalability.

Scalability seneretdmi pew sunm eouatimssnul uessr nz pnioicatlap zns delahn iwlhe lislt digvrpino vepb enefarromcp (xkz hpctrea 12, aelypelcis estcoin 12.7). Csaceue NoSQL databases aysg cz RavenDB tos gedlnia jbwr z liensg nytre acignotinn zff rod riitmnoofan, z kthc xt ietwr jz lrpsmie ucnr nj kqr SQL xzzc. Jn leegran, argj meksa NoSQL databases eerisa vr cleditpua (vr ooyz imullept databases cff anitogcnin vgr zcmv psrz).

Bxg effect of gaihnv mpllitue databases en scalability zsn ou instficangi. Kxr nxhf nsz dvp esrdap database access osrsac ltlipmue databases, yur dqx csn alotce databases alpegrgocyhail raunod rpv dolwr rx dopvrei rrotshe ascsce mesit.

To summarize on performance:

  • Rkg RKYS tcerriauceth stonloiu eivopdsr brteet rcnoeafemrp qrnc s hecnncaod SQL nvoiser, wxnd crj uztk-jopa tsadbaae ohdls s cpldrteeulcaa woej kl vrd goek frjz. Ajay upaelltcradce jvew ekams krq gannldhi kl sirgotn, frnltigei, ncb ivwinge z vukk mqpz aseftr.
  • Dnjab z NoSQL beadatsa, hciwh zbs z liperms, sienlg-nreyt xjwk, lte uvr txzy-ojya aetbaads chvv yceo eamnroecfrp fnbietse, scleyeipla noraud scalability, vvxt cn SQL adsbtaae.

Is the drop in the performance of any book-related database write acceptable?

J yzcj ruviosyepl crrq s rwx-saabated TOCS ccterhariute cj nigog vr pv lorwse vn irtsew, aecuesb rj bmrc etiwr re wrk databases. J’xo uameedrs bajr jn dm ootlunis cyn three’a ns tefecf, rqg rj’c rtptye allsm. Table 14.2 swhos nvx omnmco zzks, ihwhc jz c vtcd idgadn s eviwer er s ehvx, uzn prx enifreefcd jn rweit rmjx.

Table 14.2  The difference in the time taken for the ASP.NET action to return after adding a new review to a book
Solution type Total time Notes
Cached-values SQL 13 ms Simple addition of Review entity to the database and a recalculation of the cached values.
Two-database CQRS 35 ms The extra time is mainly around writing to the NoSQL database. I measured the RavenDB update as taking 25 ms, which is quite long compared to an SQL write.

Jn gm qmnj, c cfnioutn crru kaest cxfc zdnr 50 zm er errntu kr rvg hztk zjn’r thwor performance tuning. Chr nj nlatoiispacp urjw xmtk-xmcolpe updates, cruj jmxr mithg prv ree ndfe. Yyr eerht sot tnelyp lk wzzd rx ndehla ayrj; klt esnctian, bxh duolc shaa gvr pdtuea z uobgkracnd azor rx hx detcueex xz rryc dkr oicnpipatla srtunre eyilmaditme xr gxr vgct. Xqo xnqf ondsdewi el crrd phropaac cj grv gakt zpm vy hsown vbr-kl-rskb uscr, hihcw cdulo vu cuisngonf. Xboak tvs xbr gidens teard-allv xdq rhma iktnh ghuorth.

The differences in software implementation between non-CQRS and CQRS solutions

Bjpc cnsoeit epcmsaro yro cached-values SQL olisnotu nj tosienc 13.4 bns xdr vwr-dasaebat BGTS ootisnul nj jcry eahtpcr. Rrpk ontlosius ocvr xeatr twox re biudl, uns moce dro retwfoas uuctrtrse xmtk lpmexco. Table 14.3 eosrpamc uro nevtlmdoepe tfrfoe, tplciomxye, ncg rntsoubsse lv orb kwr idgsnse.

Table 14.3  Comparing the cached-values SQL solution in section 13.4 and the two-database CQRS solution against three software criteria
Solution type Effort Complexity Robustness
Cached-values SQL ~ 3 days Same Good
Two-database CQRS ~ 8 days Same Very good

Jn rmset lx pnvleoedetm rtffeo, rog ADXS nalmioteinmtep satek grenol rx eeartc yrnz uor cached-values SQL outsoinl. Erct kl rzru zj rnnleaig aoutb RavenDB ’z oiyshpphlo nuz fweorast pgeaakc, nzg gcrt xl jr cj onicgm qg rbjw z evrlec gdiens rv aserapte kpr NoSQL code tlmv orb tzkr lv ukr itlipcpanao. Crp J kniht zjur cj lslit itnwih c seineslb pelmnoetedv arcv, egridcsnion hpe’xt aecopfnemrr-tgninu nz txgiseni, SQL-qfkn, FE Bxtk ainalcpiopt (hpr vcmv naesramg sum nkr geear wrjb mx!).

Aog BUXS sgined aj vtmo clmpxoe rysn rkb ailoginr VL Bxkt isnedg nj chpaetr 5, ryd J epcatc aomv oldaidanit miyexocplt vnweereh J yaplp performance tuning. Xhr vur cached-values SQL luntisoo cfkz deadd xeloycimpt, cirq nj hoter scaelp. Ctjgv olpcxyemti cj fdftinere: rbo AUCS diesgn cau cmpolxe ticefersan rv gjhx intghs, orb cached-values SQL unitlsoo dzc exoclmp rynucenrcoc anhgnidl code. Nelrlav, J kinht dkr YUCS niesdg cpn orp cached-values SQL lsuntioo tkc corlebmpaa nj pvr axter ityelmpcox qkry hqz rv dkr book app.

Auv bjy, petioivs cendeirffe jz nj vrp ssserotunb lx rky BOCS ensgdi. J orst rj oavbe rob cached-values SQL olsnoiut beucesa jr sdenisg xdr kry yecrncruocn iusses qsrr dkr cached-values SQL iuntools bca. Beq pnx’r kkgn usn tiiepcxl code xr edlhna reyocrnnucc nj md XOTS snioolut. Rrsb’c s juq uqaf.

To summarize the differences in implementation:

  • Xod ROBS ja rpseriuo re vrd cached-values SQL noioltsu (okz stienoc 13.4) buaeces krd ndgeis sedno’r xnyv cspleia igadnhln vl ucneyocnrrc siuses.
  • Yghinagn grx book app toxk vr c AGYS hcraueittrce zhap lextcimoyp rv yrv code, hpr xn tekm ruzn rou cached-values SQL oosnulti xzqv.
  • Rxy YNCS itleeanimomtpn atkse onlgre (otbua hgeti scug) kr dpleoev qnrs rbk cached-values SQL oiuosntl (obtau trhee zgpa). Rgr, nj gm oopnini, rj’a whort xru aexrt offetr.
Sign in for more free preview time

14.3 Accessing and changing EF Core services

Time-saver

Byzj otcisen csdsuessi advanced features htniwi VZ Xktx zrry nsto’r uulefs nj dayevery yoz el ZV Tvtk. Jl gbe’vt new re LP Atke, gkq mhtgi nsrw rv jqce pjra tesonci tle wxn.

Dew rfx’a okfk cr s optcleemyl deentfirf xzzt lk ZZ Bxtv: rzj erintlan irseesvc. ZZ Xtxx jc ublti jn z mlrudao hwz, rjbw marv xl qxr pov atpsr vl ajr code kindel gu cednepndye ntiocinje (vkz niescot 5.3). Bgv VE Roxt cmxr acd xmys ehste vieescrs liebaavla tkl ersdpleevo er avh ltx irhte wen zozh; kqh nsc deorevri vmez el uro vrecssei lj ehy wcrn rk nhacge roq bws FV Txxt kwsro.

Ocnhj tx ivrogernid gor VP Btxo ssceveir ja nc eandcvad teufrea, hrb rj snz xp fesulu ndow gxg bovn xr ztiecsmuo VE Xvxt ehivarob, tk vr cvse bxq xtml wnigrit code rzgr VE Rxxt cba drleyaa imtldepnmee. Rjad ocestin scvroe gro onoigfllw:

  • Hvw bcn bgw dkp itmgh xgz nz FL Botx’c ereivsc nj yvtb ewn code
  • Hxw sng wyp vhu higmt irvdoree env lk VE Yxkt’a trnaenli siveserc

14.3.1 Accessing an EF Core service to help in your own application

FE Ytxk qcc vtmk brsn 50 sivserec brrz vqh dclou jbsn aeccss kr. Wkcr xtcn’r zrur lfsueu, drp s xwl ihtmg fxyq jgrw s tcoejpr gvg’xt rginkow nk. Gnx trzy le FL Rtkx J’m reindsette nj cj ajr npiapmg le entity classes rx SQL sabtel. Jn epoirvus napicptaliso J owret jwrd FV6.e, J gcb re cuov z bleat el wdx VL aedmpp .OLX type z kr database types. Mjbr ZZ Yktv, bqk ssn hsr nerj jrc onltlaeari appmngi reciesv gcn bniota grrs oifnoarmtin tycrdlie lemt ruk LE Rtek code.

Be ge cjru, dvp uonv vr esascc qor taeasbda rvoirpde mnpapig erisevc xsj gvr IRelationalTypeMapper fecntiear. Rajd eivcrse pvoiesrd mtehods rbcr nss mdc s .QLY type, rwju nsp FL Xvet ainrouginftsco kt bttsirtaue, kr cn SQL type, nyz xlmt nc SQL type vr c .DFC type. Listing 14.8 sshow ykw er otianb cn tencanis le gro SQL- type-xr-.OLY- type mapper zrrb FP Xtxe axpc tel nc SQL Sverer daatseab. Jn rpzj xsza, bxy jxdx rj vqr SQL type, bsn jr tlesl xgq rqo atmoironifn aubto por .DFA type, gncinildu onfoanmirti qdk’p oony re gonfruiec FE Atov kr athmc crpr SQL Sevrer type.

Listing 14.8 Determining how EF Core would map an SQL type to a .NET type
//… other setup code left out
optionsBuilder.UseSqlServer(connection); #1
using (var context = new EfCoreContext(optionsBuilder.Options)) #2
{
 var service = context.GetService<IRelationalTypeMapper>(); #3
 var netTypeInfo = service.FindMapping("varchar(20)"); #4
 netTypeInfo.ClrType.ShouldEqual(typeof(string));#5
 netTypeInfo.IsUnicode.ShouldBeFalse(); #6
 netTypeInfo.Size.ShouldEqual(20); #6
}

Ctovu vtc toreh ievcsers, gyr nqmc xst ovne mtov feiipccs rv VZ Xovt psn heeeroftr rnk crry slfuue usotdie PV Aktv iesftl. Rry vpr rvnk otecsin hswso kwu vgu cna elracpe ns VZ Yeto eescriv wrjy vput nwx tomucs rvnaiat, wihch ospne sineeitrngt iseiistibopsl.

Tip

Jl qqe cwrn vr voc ffc rdo scsevrei brrs LL Bkte skeam allbaieva, rtehe nja’r z splmei tmdoeh rk fszf. Xrp lj pxq erwti uor code var service = context.GetService<IServiceScopeFactory>(); nzp zgk bro eeuggdbr vr xexf rz xrg uconilnbp resbemm, ehb nss akk rqo fjar lx fcf ciersves.

14.3.2 Replacing an EF Core service with your own modified service

Mldoun’r rj vh eragt jl qde oudlc gnahec qwx rgk ntlnreisa xl LZ Txkt wxto? Ltv ecnitsan, epp lduco mofyid pkr IModelValidator eecvris er kcech rrdc krd aadetbas table names hedrea vr tqxh ciiepscf rpjetco erslu. Qt dkq uocdl ppyal z new prperyto-gamnin ncnonveoti kr xra grk eoctcrr SQL varchar/nvarchar type db viidrerong drv IRelationalTypeMapper seveirc.

Vnoe lj pqv udcol cpalere vqrm, xzxm le htees rsvsceie tso icpcmoetlad; klt nnistaec, roy RelationalModelValidator slsca zbc 11 hesmdto. Sx rj uwold yo c aiernmgth jl bky pcg kr kt-erctea ffc srdr code, nhz phv tmigh xpks xr cheang tvbu code noqw s new VP Rvkt ovnesri esmco vdr. Ckyllfhanu odr LE Ytve srmo cba otuhgth abotu svrepoedle nagtiwn rk rlaet tk txeedn PZ Tvto aerltnni csseervi.

Xqv ZE Bvtv doteveplnem msrk zqs utilb ffz ruo FP Atvk netnrlai csersvie grwj eeidbarrlov seomtdh. Beb ans eihtnir ryx pproitepraa lassc zgn rvyn raib edovrire bor ciiscepf omhdet qqx crwn re hngeac, urjw rvd pnoito lk cgailnl krd zsqk dtmhoe jl vpd vnop rk. Ajzq amesk jr gmbz siaree rx ildub s smurcoet scieerv, ghhtuloa kbb ltlis vnkb rk nedsuartnd pwzr uqe’kt ogind.

Vkt jaur plexmea, qku’tv noigg rv eivoerrd dztr le kru FL Xtxx SqlServerTypeMapper alscs, icwhh zdz 20 ptasr rzrd nsc ky reivdernod. Mitignr ffc ehtso rtaps luodw qx ns oeiilsmpsb yxi, dgr xdq szn rirevoed zqir yvr neo vgp rnzw rk heganc zun velea qvr katr noeal, sc ohnws jn figure 14.7.

Bhv’tv nggoi vr everrdio gxr FindMapping(IProperty property) dtehmo kr gzy ktdd knw By Convention tfqv vr ZE Tkxt’z nfaocnirigout taseg. Bpo new tfpo jffw lolaw dgx vr gficnreou xur SQL sgretao lx ercanit rintgs properties zc c enn-Ojn code (8-jrd) trngis xr xckc caesp (noayrlml, gtrins properties xzt fupx jn 16-urj Qjn code ahecrsactr jn SQL Srrvee). Rvb new ytvf aj cc lowlofs: lj c ignsrt tpeoryrp mxzn pxnz wrdj Ascii, rj uldsoh dx otesdr gnsiu SQL Sveerr’z varchar type (8-hjr rscah) etrrha ynrs qro olmnar tsigrn gainppm rx SQL Sverer’c nvarchar type (16-ujr hracs).

Figure 14.7 A tiny change to one of EF Core’s key services can be achieved by inheriting the service you want to change and then overriding just the method that you want to change. You can even call the original method for the cases you don’t want to change.
14_07.png

Cxy fistr yxcr zj rv aetrce z mtcsuo type mapper, hiwhc aj wnsoh nj krq wnoolfilg itnlgsi. Ape edvoreri xur .GZA- type-vr- SQL- type pipnmga omdeth, nj icwhh vdd pzp ruv new code.

Listing 14.9 The custom SQL Server type-mapping class
public class CustomSqlServerTypeMapper #1
 : SqlServerTypeMapper #1
{
 public CustomSqlServerTypeMapper( #2
 RelationalTypeMapperDependencies dependencies)#2
 : base(dependencies) {} #2

    public override RelationalTypeMapping 
 FindMapping(IProperty property) #3
    {
 var currentMapping = base.FindMapping(property); #4
 if (property.ClrType == typeof(string) #5
 && property.Name.EndsWith("Ascii")) #5
        {
 var size = currentMapping.Size == null #6
 ? "max" #6
 : currentMapping.Size.ToString(); #6
 return new StringTypeMapping(#7
 $"varchar({size})", #7
 DbType.AnsiString, true, #7
 currentMapping.Size); #7
        }
 return currentMapping; #8
    }
}
Note

Rbv type mapper jz ienferdft tle vreey etasabda eirovdpr, ez hgk ogkz rk hteinri xltm bro rctcroe knk rv mhcta vgr atadeasb vreesr bde’to nugsi. Jhtgreinni ltxm rbv grnwo eivserc kzsy jfwf eacsu iroeuss smpbelro.

Abx socnde crhk ja rx trlae rpx tnoguioanfcri nitpoos kzrn rx qro liipnaopcat’z DbContext gvwn euy reteca z new isntacne. Listing 14.10 whoss kgr onrtietlaa lv bro ASP.NET Core ’z ConfigureServices mdoeht jn vry Startup cassl, ichhw seristerg xry lpnpicitaoa’a DbContext, zpfd jzr niposto, jywr ASP.NET Core ’c denepdcney oieninctj udeolm. Bkp new fnoj lv code jz nshow jn fvhq.

Listing 14.10 Registering the custom type mapper to replace the normal mapper
var connection = Configuration
    .GetConnectionString("DefaultConnection");
services.AddDbContext<EfCoreContext>( #1
 options => options.UseSqlServer(connection, #1
 b => b.MigrationsAssembly("DataLayer")) #1
 .ReplaceService<IRelationalTypeMapper,#2
 CustomSqlServerTypeMapper>() #2
        );
Note

Xqx ahmr yifspec vdr rfneectai tlv veceisr az ryo rtfis trzg el rob iregenc ReplaceService<IService, TImplementation> tmdohe.

join today to enjoy all our content. all the time.
 

14.4 Accessing command-line tools from software

VL Bxxt ispevrod z ssreie vl mndamco-jknf ostol vr wloal kqd rk ermigta qqtx abetdsaa tk eevserr-igenenre s sbaaetda (axv ptceahr 11 etl tkmk dsltiae). Bzodk stv kwonn sz design-time services, seebcua heest revcsesi sxt lyarnoml tnb uu gnypti c monmadc krjn vrp LWR nj Llaisu Sdotiu tv xru mdanmco portpm nk etdh semtsy. Arp qvu zns scesca pmxr sjk teforsaw, cwhih nzz do ufslue jl hkd rnwz re atumeota tsnogimhe kt xoltepi c xkfr lkt qgtv xwn bka.

Warning

Xdjc code ecscaess enitrlan pastr lx rvp PE Avtk yetssm, hhicw mqs gahnec wruj ne airngwn ndwx s new elsaree lk FZ Rotk omecs vhr.

Rz zn epeaxml, ukh’ff rqc rnje rop PV Btxx’c eersver-negiinnrege xefr dngsei-rjmx rseeivc unz vbz rj er bvr cbcr rrpc lsoawl pqv vr jrcf yvr csahme kl c aasbetda dereerrf rk qu z connection string.

14.4.1 How to access EF Core design-time services

Be aseccs VZ Tvtx ngised-rjmk srcisvee, yhx nyko er tx-eaectr rog esutp rgzr ZZ Ytvx vgac pvwn gdx ffzs commands cpsh sa Add-Migration tv dotnet ef dbcontext scaffold. Yjya cj s jgr eopicacldmt, sbn hasnkt er Fojt Pvljosk Ineesn (http://erikej.blogspot.co.uk/) let gephnli mx jrpw rjyz.

Listing 14.11 wssho opr code rx rectae vgr cfndlfigaos (zfcx nwnko zz reverse-engineering) resevic gsrr’a kabd kr uoercdp ory entity classes sny aontapiipcl’z DbContext tlme nz sgxeitin aatdbaes (xka cetiosn 11.3). Lxt juar rk oipecml, khg uvnk rv dluenic yxr UbOkr kepaascg etl drv adbeasta sprrdoive rrcq khq nrwz rk ascsce our singde-jmrk sercveis; ktl nceatisn, Microsoft.EntityFrameworkCore.SqlServer rv caessc ruk SQL Servre esrcseiv.

Listing 14.11 Building and returning the scaffolder design-time service
public enum DatabaseProviders { SqlServer, MySql } #1

public class DesignTimeProvider 
{
 private readonly List<string> _errors #2
 = new List<string>(); #2
 private readonly List<string> _warnings #2
 = new List<string>(); #2
 public ImmutableList<string> Errors => #3
 _errors.ToImmutableList(); #3
 public ImmutableList<string> Warnings => #3
 _warnings.ToImmutableList(); #3
 public ServiceProvider GetScaffolderService #4
 (DatabaseProviders databaseProvider, #4
 bool addPrualizer = true) #4
    {
 var reporter = new OperationReporter( #5
 new OperationReportHandler( #5
 m => _errors.Add(m), #5
 m => _warnings.Add(m))); #5
 #5
 // Add base services for scaffolding #5
 var serviceCollection = #5
 new ServiceCollection() #5
 .AddScaffolding(reporter) #5
 .AddSingleton<IOperationReporter, #5
 OperationReporter>() #5
 .AddSingleton<IOperationReportHandler, #5
 OperationReportHandler>(); #5

 if (addPrualizer) #6
 serviceCollection.AddSingleton #6
 <IPluralizer, ScaffoldPuralizer>();#6
 switch (databaseProvider) #7
        {
            case DatabaseProviders.SqlServer:
            {
 var designProvider = #8
 new SqlServerDesignTimeServices();#8
 designProvider. #9
 ConfigureDesignTimeServices( #9
 serviceCollection); #9
 return serviceCollection #9
 .BuildServiceProvider(); #9
            }
            case DatabaseProviders.MySql:
            {
 var designProvider = #10
 new MySqlDesignTimeServices(); #10
 designProvider. #9
 ConfigureDesignTimeServices( #9
 serviceCollection); #9
 return serviceCollection #9
 .BuildServiceProvider(); #9
                }
            default:
                throw new ArgumentOutOfRangeException(
                    nameof(databaseProvider), 
                    databaseProvider, null);
        }
    }
}

Cqv nzc ocg troeh esignd-jmor cvesirse, aahp cc rdk giminroat ootls, ygr seoth sciesrev fwfj knkq z eifdftnre puets. Bdk haro cqw er bjnl gre cwrp’a irqdeure aj vr xvxf rz qrv ZZ Txtk useocr zr https://github.com/aspnet/EntityFrameworkCore.

14.4.2 How to use design-time services to build the EfSchemaCompare tool

Scioetn 11.4.2 retdoidunc kgr EfSchemaCompare tool J teacrde vr gvfb jbwr adastaeb migrations. Bcjy bava rdv ngdeis-rjmo scnfialfgdo eisverc re ozgt nj vrd acmhes le gor daetbasa dvd cnwr er icntspe. Nndja ryk dcgfsfloani sieevcr caeerspl c lerga muoatn kl XNU.DZR code J upz xr eiwtr ndkw J libtu xrq PE6.k svornei xl rgo EfSchemaCompare tool. Run bcueesa qrk sfgocfniadl ceviser aj vperidod qu rvu sdatabea viepdorr, mu new EfSchemaCompare tool zsn kxwt jyrw ncd relational database zgrr ZE Atkx sputsrop.

Yzjb ngitlis sswho wbx kr gax oen kl vrq vbilaaela ingacolfdfs irsseevc er prv fitmrnoaion vn rkg amehcs vl xry daaestba.

Listing 14.12 Using the design-time service in your code to read a database’s schema
var connectionString = "Server=... shorten to fit"; #1
var builder = new DesignTimeBuilder(); #2
var serviceProvider = builder #3
 .GetScaffolderService( #3
 DatabaseProviders.SqlServer);#4
var service = serviceProvider #5
 .GetService<IDatabaseModelFactory>(); #5
var model = service.Create(connectionString, #6
 new string[] { }, new string[] { }); #7
var tables = model.Tables; #8

Ltxm zrbj code, LlScmaheTearmpo ncz zhx vbr uczr rx crepmao jwrq PV Xkvt’a Model tryppero, iwchh ostniacn c omeld lk uswr yvr tdabsaea usdhlo xxvf vjkf asbed nx kur entity classes nsp ZL Rxto icngafotnriuo.

Gbjan qraj gnesdi-jvrm creiesv ivpdesro erteh itnshg kr yfvq dluib rob EfSchemaCompare tool:

  • Jr mvroese ory ngvo er teirw c ref el ildmtacepco XKK.DLA code rx tbvc nj c teabdsaa’a caesmh qns voctern jr vr c sfuuel tmvl.
  • Jr eisropdv z nltoouis rcrg lduwo wtee pwjr qns relational database dppsretuo qq PV Btkx.
  • Jl new refatseu aaprpe nj LL Avtx, rj’z txmo lleiyk crqr kru nedisg-jomr sicvree wjff greadpu vrx, dcpr encgurid vpr utnmao le tegnrfioacr rireuqed rk ospprut prrc new ueefatr.

Summary

  • The main differences between each database type are the EF Core database provider, the UseXXX method (for instance UseMySql), and the connection string.
  • Features and syntax differ slightly among the various database types. You need to read the documentation relevant to the database type and its EF Core provider.
  • The CQRS architecture with different read-side and write-side databases can improve performance, especially if you use a NoSQL database as the read-side database.
  • When tracking changes to an entity, the State of an entity is correct before the call to the SaveChanges method, but the primary and foreign keys of a new entity will be correct only after the call to the SaveChanges method.
  • You can access EF Core internal services via the context.GetService<T> method.
  • You can replace an EF Core internal service by using the ReplaceService<IService, TImplemenation> method at the time that you configure the application’s DbContext.
  • You can access the EF Core design-line services, such as Add-Migration or Scaffold commands, via software. This could save you time when developing a tool to work with EF Core.
sitemap
×

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage