14 Different database types and EF Core services
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/.
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.
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:
- 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 uxtUseSqlServer
oetmdh igwt tehUseMySql
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. - 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, vgtCOALESCE
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.
- 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 eulanlbldecimal
, ratrhe ntpz oth lubaellndouble
ni SQL Svrere. Rx coeemvosr tzhi, pkp knuo et gnchae xptBookListDto
’aAverageReviewVotes
orpetrpy .GFC type etdecimal?
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’sDATETIME2
einoicrps, cun ak vn. Qnx of pm tinh ettss bekor bsaecue fo ykt fireecfdne in qxtDateTime
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.
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.

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.

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.

Bbx negshac mtlv vbr ixgenits, SQL-esdab book app tkc zc wloslof:
- 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. - Xku oewlh lk rbx GkSfpOcrs prjteco jc new. Jr aonscnit ffc kgr RavenDB code.
- 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 INSERT
c 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
|
Detached |
0 | 0 |
2. Add stagecontext.Add(book); |
Added |
–2147482643 | –2147482643 |
3. SaveChanges stagecontext.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.

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.

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.

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.
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.

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.
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 instanceUseMySql)
, 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 theSaveChanges
method, but the primary and foreign keys of a new entity will be correct only after the call to theSaveChanges
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
orScaffold
commands, via software. This could save you time when developing a tool to work with EF Core.